check RAM,page file, /PAE, /3GB, SQL server memory using powershell - sql-server

I am a powershell novice.
After days of searching....
I have put together a small powershell script (as below) to check page file, /PAE switch, /3GB switch, SQL server max RAM, min RAM.
I am running this on 1 server.
If I want to run it on many servers (from a .txt) file, How can I change it ?
How can I change it to search boot.ini file's contents for a given server?
clear
$strComputer="."
$PageFile=Get-WmiObject Win32_PageFile -ComputerName $strComputer
Write-Host "Page File Size in MB: " ($PageFile.Filesize/(1024*1024))
$colItems=Get-WmiObject Win32_PhysicalMemory -Namespace root\CIMv2 -ComputerName $strComputer
$total=0
foreach ($objItem in $colItems)
{
$total=$total+ $objItem.Capacity
}
$isPAEEnabled =Get-WmiObject Win32_OperatingSystem -ComputerName $strComputer
Write-Host "Is PAE Enabled: " $isPAEEnabled.PAEEnabled
Write-Host "Is /3GB Enabled: " | Get-Content C:\boot.ini | Select-String "/3GB" -Quiet
# how can I change to search boot.ini file's contents on $strComputer
$smo = new-object('Microsoft.SqlServer.Management.Smo.Server') $strSQLServer
$memSrv = $smo.information.physicalMemory
$memMin = $smo.configuration.minServerMemory.runValue
$memMax = $smo.configuration.maxServerMemory.runValue
## DBMS
Write-Host "Server RAM available: " -noNewLine
Write-Host "$memSrv MB" -fore "blue"
Write-Host "SQL memory Min: " -noNewLine
Write-Host "$memMin MB "
Write-Host "SQL memory Max: " -noNewLine
Write-Host "$memMax MB"
Any comments how this can be improved?
Thanks in advance

In case you would like just to check the boot.ini file you could use Get-Content (in case you will not have problems with credentials)
# create a test file with server names
#"
server1
server2
"# | set-content c:\temp\serversso.txt
# read the file and get the content
get-content c:\temp\serversso.txt |
% { get-content "\\$_\c`$\boot.ini" } |
Select-String "/3GB" -Quiet
Later if you add some stuff that will be needed to run on remote computer then you will need to use remoting and basically Invoke-Command. Recently two resources appeared that touch remoting:
Administrator's Guid to Windows PowerShell Remoting
Series on PowerShell remoting by Ravikanth Chaganti that starts here

Related

Problem when uninstall direct from msi location path & in for loop

I try to uninstall a msi file, but when I try this via array I get an error (cant find installation package)
When I do the same but not in array - it works
for ($i=0; $i -lt $msiArrayClean.length; $i++){
Write-Host $msiArrayClean[$i]
& msiexec.exe /x $msiArrayClean[$i]
}
here the output of Write Host
How i come to $msiArrayClean
$msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*" | Format-Table LocalPackage -AutoSize -HideTableHeaders
$msiString = $msiCache | Out-String
$msiArrayWithEmptyLines = $msiString -split "`n"
$msiArray = $msiArrayWithEmptyLines.Split('', [System.StringSplitOptions]::RemoveEmptyEntries)
$msiArrayCleanString = $msiArray | Out-String
$msiArrayClean = $msiArrayCleanString -split "`n"
A few caveats up front:
Format-* cmdlets output objects whose sole purpose is to provide formatting instructions to PowerShell's output-formatting system - see this answer. In short: only ever use Format-* cmdlets to format data for display, never for subsequent programmatic processing.
The CIM cmdlets (e.g., Get-CimInstance) superseded the WMI cmdlets (e.g., Get-WmiObject) in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell (Core) (version 6 and above), where all future effort will go, doesn't even have them anymore. For more information, see this answer.
Use of the Win32_Product WMI class is discouraged, both for reasons of performance and due to potentially unwanted side effects - see this Microsoft article.
An alternative - available in Windows PowerShell only (not in PowerShell (Core) 7+) - is to use the following to get uninstall command lines and execute them via cmd /c:
Get-Package -ProviderName Programs -IncludeWindowsInstaller |
ForEach-Object { $_.meta.attributes['UninstallString'] }
If you need to stick with Win32_Product:
# Get the MSI package paths of all installed products, where defined.
$msiCache = (Get-CimInstance Win32_Product).LocalPackage -ne $null
foreach ($msiPackagePath in $msiCache) {
if (Test-Path -LiteralPath $msiPackagePath) {
# Note that msiexec.exe runs *asynchronously*.
# Use Start-Process -Wait to wait for each call to complete.
& msiexec.exe /x $msiPackagePath
} else {
Write-Warning "Package not found: $msiPackagePath"
}
}
I don't like reaching to WMI, since its perfomance is the issue. I prefer to do it via registry and it worked for me many times. Code explanation in comments.
$name = "7-zip"
#Get all items from registry
foreach ($obj in Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") {
#Get DisplayName property of registry
$dname = $obj.GetValue("DisplayName")
#Search for given name
if ($dname -like "*$name*") {
#Get uninstall string (it gets you msiexec /I{APPID})
$uninstString = $obj.GetValue("UninstallString")
foreach ($line in $uninstString) {
#Getting GUID from "{" to "}""
$found = $line -match '(\{.+\}).*'
if ($found) {
#If found - get GUID
$appid = $matches[1]
Write-Output "About to uninstall app $appid"
#Start uninstallation
Start-Process "msiexec.exe" -arg "/X $appid /qb" -Wait
}
}
}
}
Edit: Added solution with msi path after Nehat's comment as this works for me (I tried to minimize the code :))
$msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*" | Format-Table LocalPackage -AutoSize -HideTableHeaders
foreach ($msi in $msiCache | Out-String) {
if ([string]::IsNullOrEmpty($msi)) {
continue
}
Write-Host $msi
Start-Process "msiexec.exe" -arg "/x $msi" -Wait
}

Change instance level collation of SQL Server using powershell

I want to change the collation of SQL Server instance programmatically using powershell script. Followings are the manual steps:
Stop the SQL Server instance
Go to directory location: "C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\Binn"
Execute following command: sqlservr -c -m -T4022 -T3659 -s"SQL2017" -q"SQL_Latin1_General_CP1_CI_AS"
After the execution of the above command, following message displayed: "The default collation was successfully changed."
Then I need to press ctrl+c to stop further execution. How can I do
this programmatically?
When we execute the command to change the SQL Server Collation, it logs the execution details in event viewer application logs. Using loop we can check the event viewer application logs for SqlServr.exe continuously, and when it generates the following log message: "The default collation was successfully changed", we can kill the process.
#Take the time stamp before execution of Collation Change Command
$StartDateTime=(Get-Date).AddMinutes(-1)
# Execute the Collation Change Process
Write-Host "Executing SQL Server Collation Change Command"
$CollationChangeProcess=Start-Process -FilePath $SQLRootDirectory -ArgumentList
"-c -m -T 4022 -T 3659 -s $JustServerInstanceName -q $NewCollationName" -
NoNewWindow -passthru
Do
{
$log=Get-WinEvent -FilterHashtable #{logname='application';
providername=$SQLServiceName; starttime = $StartDateTime} | Where-Object -
Property Message -Match 'The default collation was successfully changed.'
IF($log.count -gt 0 -and $log.TimeCreated -gt $StartDateTime )
{
Stop-Process -ID $CollationChangeProcess.ID
write-host 'Collation Change Process Completed Successfully.'
break
}
$DateTimeNow=(Get-Date)
$Duration=$DateTimeNow-$StartDateTime
write-host $Duration.totalminutes
Start-Sleep -Seconds 2
IF ($Duration.totalminutes -gt 2)
{
write-host 'Collation Change Process Failed.'
break
}
}while (1 -eq 1)
Thanks #Murali Dhar Darshan. I've made some changes to your solution to make it easier to use. (I don't have high enough reputation to add this as a comment to your answer).
# Params
$NewCollationName="Danish_Norwegian_CI_AS"
$SQLRootDirectory="C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Binn\sqlservr.exe"
$SQLServiceName="MSSQLSERVER"
# Stop running SQL instance
net stop $SQLServiceName
#Take the time stamp before execution of Collation Change Command
$StartDateTime=(Get-Date).AddMinutes(-1)
# Execute the Collation Change Process
Write-Host "Executing SQL Server Collation Change Command"
$CollationChangeProcess=Start-Process -FilePath $SQLRootDirectory -ArgumentList "-c -m -T 4022 -T 3659 -q $NewCollationName" -NoNewWindow -passthru
Do
{
$log=Get-WinEvent -FilterHashtable #{logname='application';
providername=$SQLServiceName; starttime = $StartDateTime} | Where-Object -Property Message -Match 'The default collation was successfully changed.'
IF($log.count -gt 0 -and $log.TimeCreated -gt $StartDateTime )
{
Stop-Process -ID $CollationChangeProcess.ID
write-host 'Collation Change Process Completed Successfully.'
# Start SQL instance again
net start $SQLServiceName
break
}
$DateTimeNow=(Get-Date)
$Duration=$DateTimeNow-$StartDateTime
write-host $Duration.totalminutes
Start-Sleep -Seconds 2
IF ($Duration.totalminutes -gt 2)
{
write-host 'Collation Change Process Failed.'
Stop-Process -ID $CollationChangeProcess.ID
break
}
}while (1 -eq 1)

Run multiple *.sql query files log to file

My goal is to have a PowerShell script run several Sqlquery.sql files against a specific SQL server and then log the output to a log file.
I can't get the logging to work and I don't know what I'm missing. My log file is always empty and I'm at a loss for that I am missing.
Contents of C:\Temp:
Build1.SQL
Build2.SQL
Build3.sql
Build4.sql
Build5.SQL
Build6.SQL
$PatchPostConvSQLScripts = Get-ChildItem -Path C::\Temp -Filter *.sql -Name
$Queries = $PatchPostConvSQLScripts
foreach ($query in $Queries){
Write-Host "Starting: $query"
Invoke-Sqlcmd -ServerInstance $DBServer -InputFile $query |
Out-File "C:\TEMP\scriptResults.log"
Write-Host "Completed: $query"
}
Once I get it logging to a file, I'll need to get a newline each time with a `n`r, but baby steps right now.
Is there a better way to do this that I just don't know?
The main reason you got nothing in log file is that Output-File rewrite whole data in it on each run. Try to use -Verbose as mentioned in answer by TechSpud to collect print/server statements, or write output to temp file and Add-Content to main log file:
$DBServer = "MYPC\SQLEXPRESS"
$sqlPath = "C:\TEMP\"
$log = "scriptResults.log"
$tempOut = "temp.log"
$files = Get-ChildItem -Path $sqlPath -Filter *.sql -Name
foreach ($file in $files){
Write-Host "Starting: $file"
Invoke-SQLcmd -ServerInstance $DBServer -InputFile $sqlPath$file | Out-File $sqlPath$tempOut
Get-Content $sqlPath$tempOut | Add-Content $sqlPath$log
Write-Host "Completed: $file"
}
Firstly, as #Ben Thul has mentioned in his comment, check that your SQL files actually output something (a resultset, or messages), by running them in Management Studio.
Then, you'll need to use the -Verbose flag, as this command will tell you.
Get-Help Invoke-Sqlcmd -Full
Invoke-Sqlcmd does not return SQL Server message output, such as the
output of PRINT statements, unless you use the PowerShell -Verbose parameter.
$Queries = Get-ChildItem -Path C::\Temp -Filter *.sql -Name
Clear-Content -Path "C:\TEMP\scriptResults.log" -Force
foreach ($query in $Queries){
Write-Host "Starting: $query"
Invoke-Sqlcmd -ServerInstance $DBServer -InputFile $query -Verbose |
Add-Content -Path "C:\TEMP\scriptResults.log"
Write-Host "Completed: $query"
}

Powershell: WMI Ping piped to variable

I'm having some difficulties in getting my PowerShell script to work as I'd like it to and after much jiggery-pokery here I am.
My overall aim is fairly simple, unfortunately I'm somewhat of a PowerShell noob!
I'm trying to determine the name, manufacturer and model of all of the systems in our estate without having to walk around staring at lots of tin.
I've constructed the following based solely on my bad knowledge of scripting and I've hit a snag.
My idea was to pass DNS/IP information from a CSV into a variable which I can then use in turn to perform the WMI query based on the Ping results.
False Ping response = do not query
True Ping response = perform WMI query
Here is what I've got so far...
Test-connection -computername
foreach ($Ping in $Hosts)
{
test-connection -computername $Ping.IP -count 1 -quiet
if ($Ping.StatusCode -eq 0)
{Get-WmiObject win32_computersystem -computername $ip.Name | select Name,Manufacturer,Model } out-file c:\CSV\Test1.csv -ea SilentlyContinue}
else
{write-host $Hosts.Status Offline}
}
Assuming you have file C:\CSV\Hosts.csv with contents as described:
computer1.mydomain.com
computer2.mydomain.com
With the following script you'll get file C:\CSV\Results.csv:
$Hosts = Get-Content "C:\CSV\Hosts.csv"
foreach ($PingIP in $Hosts)
{
$alive = Test-Connection -ComputerName "$PingIP" -count 1 -quiet
if ($alive)
{
Get-WmiObject Win32_ComputerSystem -ComputerName "$PingIP" | Select Name, Manufacturer, Model | Out-File "C:\CSV\Results.csv" -ea SilentlyContinue
}
else
{
Write-Output "$PingIP offline"
}
}

Powershell Script using Invoke-SQL command,needed for SQL job, the SQL Server version of Powershell is somewhat crippled, is there a workaround?

Full Question: Have Powershell Script using Invoke SQL command, using snappins, I need them to be included in a SQL job, the SQL Server version of Powershell is somewhat crippled, does anyone know a workaround?
From what I have gathered, SQL Management Studio's version of powershell is underpowered, not allowing for the use of snappins, as such it does not recognize the cmdlets that I used in the script. I have tried running it in the job as a command line prompt rather than a Powershell script, which causes the code to work somewhat, however I check the history on the job and it says that invoke-sql is still not a recognized cmdlet. I speculate that because I am running the code on a remote server, with different credentials than my standard my profile with the snappins preloaded isn't being loaded, though this is somewhat doubtful.
Also, as I am a powershell rookie, any advice on better coding practices/streamlining my code would be much appreciated!
Code is as follows:
# define parameters
param
(
$file = "\\server\folder\file.ps1"
)
"invoke-sqlcmd -query """ | out-file "\\server\folder\file.ps1"
# retrieve set of table objects
$path = invoke-sqlcmd -query "select TableName from table WITH (NoLock)" -database db -server server
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
$so = New-Object Microsoft.SqlServer.Management.Smo.ScriptingOptions
$so.DriPrimaryKey = $false
$so.Nocollation = $true
$so.IncludeIfNotExists = $true
$so.NoIdentities = $true
$so.AnsiPadding = $false
# script each table
foreach ($table in $path)
{
#$holder = $table
$table = get-item sqlserver:\sql\server\default\databases\database\tables\dbo.$($table.TableName)
$table.script($so) | out-file -append $file
}
(get-content "\\server\folder\file.ps1") -notmatch "ANSI_NULLS" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -notmatch " AS "| out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -notmatch "Quoted_" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace "\) ON \[PRIMARY\].*", ")" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace "\[text\]", "[nvarchar](max)" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace " SPARSE ", "" | out-file "\\server\folder\file.ps1"
(get-content "\\server\folder\file.ps1") -replace "COLUMN_SET FOR ALL_SPARSE_COLUMNS", "" | out-file "\\server\folder\file.ps1"
""" -database database -server server" | out-file "\\server\folder\file.ps1" -append
So I figured out the answer to my own question. Using this site: http://www.mssqltips.com/tip.asp?tip=1684 and
http://www.mssqltips.com/tip.asp?tip=1199
I figured out that he was able to do so using a SQL Server Agent Proxy, so I followed the yellow brick road, and basically I set up a proxy to my account and was able to use the external powershell through a feature. A note, you need to create a credential under the securities tab in object explorer prior to being able to select one when creating the proxy. Basically I ended up creating a proxy named powershell, using the powershell subsystem, and use my login info to create a credential. VOILA!
You have to add the snapins each time. In your editor you likely already have them loaded from another script/tab/session. In SQL Server you will need to add something like this to the beginning of the script:
IF ( (Get-PSSnapin -Name sqlserverprovidersnapin100 -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin sqlserverprovidersnapin100
}
IF ( (Get-PSSnapin -Name sqlservercmdletsnapin100 -ErrorAction SilentlyContinue) -eq $null )
{
Add-PsSnapin sqlservercmdletsnapin100
}
I'm not sure the error you are trying to workaround - can you post that?
Have you tried this from a PowerShell prompt?
Add-PSSnapin SqlServerCmdletSnapin100

Resources