Handling SQL Errors / Exceptions in PowerShell Script - sql

I am trying to catch the exceptions generated in the SQL query execution result in my log file using powershell script.
But however the code that I use below catches the exceptions in the syntax of my powershell script and not the SQL returned exceptions.
Also I would like to log the result set of my SQL script upon successful execution. How can I acheive this.
The following is my PowerShell script,
try
{
if($Windows_authentication.tostring() -eq "1"){
invoke-SqlCmd -inputfile $Script_path\script.SQL -serverinstance $Ser_name -database $Db_name -querytimeout 65535 }
else
{
invoke-SqlCmd -inputfile $Script_path\script.SQL -serverinstance $Ser_name -database $Db_name -username $Userid -password $Passwd -querytimeout 65535 }
Write-Host "script.SQL Completed execution"
}
Catch
{
$ErrorMsg = $_.Exception.Message
Out-File -InputObject "The error message is - $ErrorMsg" -Append -NoClobber -FilePath "$Script_path\log.txt"
while( $ErrorMsg.InnerException ) {
$ErrorMsg = $ErrorMsg.InnerException
write-output $ErrorMsg
Out-File -InputObject "The error message is - $ErrorMsg" -Append -NoClobber -FilePath "$Script_path\log.txt"
}
break
}

Unless the exception being generated is caused on the Powershell / .Net side, the excepition handler doesn't process it. An example would be being unable to connect to the Sql Server. For any error in the TSQL code, error handling is done within Sql Server.
You can use try...catch in TSQL. There are more articles like Using TRY...CATCH in Transact-SQL and SO questions too.

Related

Running sqlcmd against the WID in PowerShell remotely

I am fighting with WSUS and trying to set up a script to manage the maintenance of SUSDB on multiple servers in our remote sites, we have servers on multiple versions and multiple OS, so it gets a bit complicated, the newer ones (post 2008R2) work with no issues, however, the Server 2008s have an issue:
I run this locally or in an interactive remote session and it works as expected, but if I nest the invoke-command to run it in a remote session as per below, it errors out with the following:
HResult 0x2, Level 16, State 1
Named Pipes Provider: Could not open a connection to SQL Server [2].
Sqlcmd: Error: Microsoft SQL Server Native Client 10.0 : A network-related or instance-specific error has occurred while establishing a connection to SQL Server. Server is not found or not accessible. Check if instance name is correct and if SQL Server is configured to allow remote connections. For more information see SQL Server Books Online..
Sqlcmd: Error: Microsoft SQL Server Native Client 10.0 : Login timeout expired
I have hunted around for days and tried all the permutations I can think of, but here's what I have:
$SQLPath = "C:\Users\<username>\Documents\SQL Server Management Studio\"
$SQLfile = "SUSDB-ReIndex.sql"
$WsusSvrs = ("WSUS-Site1","WSUS-Site7","WSUS-Site13","WSUS-Site14","WSUS-Site15")
$ErrorActionPreference = "Stop"
$results = #()
Foreach ($WSUSSvr in $WSUSSvrs) {
Try {
$sess = New-PSSession -ComputerName $WSUSSvr -EnableNetworkAccess -ErrorAction Stop
}
Catch {
continue;
}
$output = Invoke-Command -Session $sess -ScriptBlock {
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
If((Get-Command SQLcmd.exe -ErrorAction SilentlyContinue)-eq "") {
$NoSQLCmd = new-object System.IO.FileNotFoundException("SQLcmd is not Installed on this machine, please install the appropriate version and try again")
Throw $NoSQLCmd
}
$Output = New-Object psobject
$OSVerStr = (Gwmi win32_operatingsystem).version.split(".")
[single]$OS = [convert]::ToSingle(($OSVerStr[0],$OSVerStr[1] -join "."))
$Output | Add-Member -MemberType NoteProperty -Name OSVer -Value $OS
if ($OS -gt 6.1) {
$conStr = "\\.\pipe\Microsoft##WID\tsql\query"
}
Else {
$conStr = 'np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query'
}
if(Test-Path "$using:SQLPath\$Using:SQLfile") {
$cmd = "sqlcmd.exe -S $ConStr -i '$Using:SQLPath\$Using:SQLfile' -d 'SUSDB' -E -o 'C:\temp \$Using:WSUSSvr-reindexout.txt'"
Invoke-Expression $cmd
$output | Add-Member -MemberType NoteProperty -Name Message -Value "done"
}
else {
$output | Add-Member -MemberType NoteProperty -Name Message -Value "Unable to find $Using:sqlfile"
}
$Output
} -ArgumentList $SQLPath,$SQLfile,$WSUSSvr
$results += $output
if(test-path "\\$wsussvr\C$\temp\$WSUSSvr-reindexout.txt") {
cp "\\$wsussvr\C$\temp\$WSUSSvr-reindexout.txt" "D:\wsus-reports\" -Force
}
If( $sess.State -eq "Opened" ) { Remove-PSSession $sess }
}
$results | ft
I know we should be shot of the 2008 boxes by now, but there's a niche product vendor and some budget issues in replacing the boxes.
On line 35 I see the following
Else {
$conStr = 'np:\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query'
}
Could you try running it without np: in front of it?
Else {
$conStr = '\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query'
}

In Powershell, how can a non-admin user run a process as different user and then read the process exit code?

I need to run an executable as a particular user during a build task in Azure Pipelines. To determine whether to fail the build, I need to read that process's exit code.
I can't run the script from an explicitly administrative session.
When I view the process handle using the code below, the exit code is always empty. I'm positive that the executable is returning exit codes (it was written in-house).
In addition to the code below, I also tried using $LASTEXITCODE, but it won't be set unless I run the executable directly (as opposed to using Start-Process).
Is there a way to view the exit code of that process?
$process = Start-Process -FilePath $pathToExe -ArgumentList $argsString -Credential $credential -PassThru
# I tried waiting like this as well
#while ($process.HasExited -ne $true) {
# Start-Sleep -Seconds 5
#}
$process.WaitForExit()
Write-Host "Process exit code: $($process.ExitCode)"
An approach similar to the one below can be used to access the exit code of a process started by one user in the context of another, but it requires an Admin session.
$scriptBlock = {
param($exePath, $exeArgs)
$process = Start-Process -FilePath $exePath -ArgumentList $exeArgs -PassThru
$process.WaitForExit()
return $process.ExitCode
}
$runAsUserSession = New-PSSession -Credential $credential
$exitCode = Invoke-Command -ScriptBlock $scriptBlock -ArgumentList #($pathToExe, $argsString) -Session $runAsUserSession
Write-Host "Process exit code: $exitCode"
Try invoke method:
$process = Start-Process -FilePath $pathToExe -ArgumentList $argsString -Credential $credential -PassThru
$process.WaitForExit()
#while ($process.HasExited -ne $true) {
# Start-Sleep -Seconds 5
#}
Write-Host "Process exit code:" $process.ExitCode

Invoke-DbaQuery Failure - The wait operation timed out

I try to run the following command:
$serverName = 'firjt6'
Invoke-DbaQuery -SqlInstance 'some site' -Database 'CM_IDC' -Query $query -SqlParameters #{SrvName = $serverName}
And I keep on getting the following error:
[Invoke-DbaQuery] Failure | The wait operation timed out
Does anyone know where should I add the command TimeOut property?
$serverName = 'firjt6'
Invoke-DbaQuery -SqlInstance 'some site' -Database 'CM_IDC' -Query $query -SqlParameters #{SrvName = $serverName}

How to run a SQL batch within an Azure runbook?

I need to execute a batch to perform some maintenance tasks in my database but all the examples on Azure Automation I see are dealing with a single SQL command.
How do I do it if creating an SP is not an option? I think I need to either somehow embed my script.sql file into a runbook script or reference it (like here, for example)?
You could store the .sql file in Azure Blob Storage, and within the runbook download the .sql file, read its contents, and pass that to the SqlCommand object.
Something like:
try {
# Connect to Azure using service principal auth
$ServicePrincipalConnection = Get-AutomationConnection -Name $AzureConnectionAssetName
Write-Output "Logging in to Azure..."
$Null = Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $ServicePrincipalConnection.TenantId `
-ApplicationId $ServicePrincipalConnection.ApplicationId `
-CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint
}
catch {
if(!$ServicePrincipalConnection) {
throw "Connection $AzureConnectionAssetName not found."
}
else {
throw $_.Exception
}
}
$Path = "C:\abc.sql"
Set-AzureRmCurrentStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName
Get-AzureStorageBlobContent -Container $Container -Blob $Blob -Destination $Path
$Content = Get-Content $Path
$Cmd = New-Object System.Data.SqlClient.SqlCommand($Content, $Conn)

kvm.ps1 cannot be loaded because running scripts is disable on this system

I am trying to run the Asp.net vNext sample application.
But when i try to execute the command
kvm list
It gives me the error message
kvm.ps1 cannot be loaded because running scripts is disable on this system
I tried to change to execution policy also. But still i am getting the same error.
Try this
execute this command on console
**"powershell Set-ExecutionPolicy RemoteSigned"**,
after that run the commands bellow
$tempPath = Join-Path $env:TEMP "kvminstall"
$kvmPs1Path = Join-Path $tempPath "kvm.ps1"
$kvmCmdPath = Join-Path $tempPath "kvm.cmd"
Write-Host "Using temporary directory: $tempPath"
if (!(Test-Path $tempPath)) { md $tempPath | Out-Null }
$webClient = New-Object System.Net.WebClient
Write-Host "Downloading KVM.ps1 to $kvmPs1Path"
$webClient.DownloadFile('https://raw.githubusercontent.com/aspnet/Home/master/kvm.ps1', $kvmPs1Path)
Write-Host "Downloading KVM.cmd to $kvmCmdPath"
$webClient.DownloadFile('https://raw.githubusercontent.com/aspnet/Home/master/kvm.cmd', $kvmCmdPath)
Write-Host "Installing KVM"
& $kvmCmdPath setup
You could try the powershell command Unblock-File?