Show failure for SQL 2005 Job that executes Powershell script through CMD prompt, IF PS script fails

坚强是说给别人听的谎言 提交于 2019-12-09 20:38:12

问题


I have a SQL 2005 instance that runs a job that uses a Powershell script to rename the current SQL TX Log backup file by appending "-PrevDay" to it, (subsequently deleting the backup already named "XXX-PrevDay.bak" if it exists), and then run a full backup of the DB and a TX Log backup, if the DB is not in Simple mode.

SQL Server Agent Job kicks off the Powershell script through CMD in each job step and the powershell script kicks off the sql backup using "Invoke-SQLCmd" cmdlet. This works great, unless the backup fails, because the SQL job still shows as "Successful". This is because the SQL job that kicks off the Powershell script through the CMD prompt, only cares if the Powershell script runs...not if the commands IN the script actually succeed or fail.

Is it possible, using error trapping in powershell (or any method really), to have the powershell script "fail" the cmd prompt action of running the script...so that the SQL Job reports a failure?

Does this even make sense? LOL

I would assume that if I was able to use SQL 2008, which allows for a SQL job step type of "Powershell Script" (instead of the step type having to be Operating System...that kicks off the PS script) this wouldn't be an issue...however...that's not an option.

Right now, the job step runs the powershell script, through CMD using Parameters for DBName, Path, and Servername and looks like this:

powershell.exe "C:\SQLBackupScriptsTest\SQLServerBackup.ps1" -DBName 'Angel_Food' -Path 'E:\SQLBackup1' -Server 'DEVSQLSRV'

The actual Powershell script looks like this:

Param($DBName,$Path,$Server)

## Add sql snapins...must have for Invoke-Sqlcmd with powershell 2.0 ##

add-pssnapin sqlserverprovidersnapin100
add-pssnapin sqlservercmdletsnapin100
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-null

## Set parameter for finding DB recovery model ##
$Recovery = (Invoke-Sqlcmd -Query "SELECT recovery_model_desc FROM sys.databases WHERE name = '$DBName'" -Server $Server)

## Do full backup of DB ##
(Invoke-Sqlcmd -Query "BACKUP DATABASE $DBName TO  DISK = N'$Path\$DBName\$DBName.bak' WITH NOFORMAT, INIT,  NAME = N'$DBNameTEST', SKIP, NOREWIND, NOUNLOAD,  STATS = 10, CHECKSUM" -Server $Server -ConnectionTimeout 0 -QueryTimeout 65535)

############################################################################################################
## Check recovery mode, if FULL, check for Log-PrevDay.bak. If exists then delete.  If not exist, move on ##
## Then check for Current TX log backup.  If exists, rename to Log-PreDay.bak. If not exist, move on      ##
## Then perform TX Log backup                                                                             ##
## If recovery mode NOT FULL, do nothing                                                                  ##
############################################################################################################
    IF
    ($Recovery.recovery_model_desc -eq 'FULL')
    #THEN#
    {
            ## Look to see if PrevDay TX log exists.  If so, delete, if not, move on ##
            IF 
            (Test-Path $Path\$DBName\$DBName-Log-PrevDay.bak) 
            #THEN#
            {remove-item $Path\$DBName\$DBName-Log-PrevDay.bak -force}
            ELSE
            {}
                ## Look to see if current TX log exists, if so, rename to Prev Day TX Log, if not, move on ##
                IF
                (Test-Path $Path\$DBName\$DBName-Log.bak)
                #THEN#
                {rename-item $Path\$DBName\$DBName-Log.bak -newname $DBName-Log-PrevDay.bak -force}
                ELSE
                {}

    Invoke-Sqlcmd -Query "BACKUP LOG $DBName TO  DISK = N'$Path\$DBName\$DBName-Log.bak' WITH NOFORMAT, INIT,  NAME = N'$DBName LogTEST (Init)', SKIP, NOREWIND, NOUNLOAD,  STATS = 10, CHECKSUM" -Server $Server -ConnectionTimeout 0 -QueryTimeout 65535}
    ELSE
    {}

回答1:


Ok, after looking at a few blogs and a little trial/error/luck...I got it to do what I want.

I decided I needed to send the Powershell exit code back to the CMDEXEC. However, from what I found, Powershell always defaults an exit code of 0 (success)...unless you jump through a few hoops invloving using 2 PS scripts...which I really didn't want to do. So I decided to just trap any error and if any error was trapped, have it exit the PS script with a code of 1, no matter what. Honestly...all I really wanted was a reliable exit code of 0 (success) or 1 (fail). So ...long story short...here's how I changed my code.

I changed the CMDEXEC of each step to this:

powershell.exe -noprofile C:\SQLBackupScriptsTest\SQLServerBackup2.ps1 -DBName 'Angel_Food' -Path 'E:\SQLBackup' -Server 'DEVSQLSRV'
@Echo %errorlevel%

Then I changed my PS script to:

Param($DBName,$Path,$Server)

## Add sql snapins...must have for Invoke-Sqlcmd with powershell 2.0 ##

add-pssnapin sqlserverprovidersnapin100
add-pssnapin sqlservercmdletsnapin100
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | out-null

## Set parameter for finding DB recovery model ##
$Recovery = (Invoke-Sqlcmd -Query "SELECT recovery_model_desc FROM sys.databases WHERE name = '$DBName'" -Server $Server)

## Do full backup of DB ##
trap {$_.Exception.Message; exit 1; continue}Invoke-Sqlcmd -Query "BACKUP DATABASE $DBName TO  DISK = N'$Path\$DBName\$DBName.bak' WITH NOFORMAT, INIT,  NAME = N'$DBNameTEST', SKIP, NOREWIND, NOUNLOAD,  STATS = 10, CHECKSUM" -Server $Server -ConnectionTimeout 0 -QueryTimeout 65535 -ea stop

############################################################################################################
## Check recovery mode, if FULL, check for Log-PrevDay.bak. If exists then delete.  If not exist, move on ##
## Then check for Current TX log backup.  If exists, rename to Log-PreDay.bak. If not exist, move on      ##
## Then perform TX Log backup                                                                             ##
## If recovery mode NOT FULL, do nothing                                                                  ##
############################################################################################################
    IF
    ($Recovery.recovery_model_desc -eq 'FULL')
    #THEN#
    {
            ## Look to see if PrevDay TX log exists.  If so, delete, if not, move on ##
            IF 
            (Test-Path $Path\$DBName\$DBName-Log-PrevDay.bak) 
            #THEN#
            {remove-item $Path\$DBName\$DBName-Log-PrevDay.bak -force}
            ELSE
            {}
                ## Look to see if current TX log exists, if so, rename to Prev Day TX Log, if not, move on ##
                IF
                (Test-Path $Path\$DBName\$DBName-Log.bak)
                #THEN#
                {rename-item $Path\$DBName\$DBName-Log.bak -newname $DBName-Log-PrevDay.bak -force}
                ELSE
                {}

            trap {$_.Exception.Message; exit 1; continue}Invoke-Sqlcmd -Query "BACKUP LOG $DBName TO  DISK = N'$Path\$DBName\$DBName-Log.bak' WITH NOFORMAT, INIT,  NAME = N'$DBName LogTEST (Init)', SKIP, NOREWIND, NOUNLOAD,  STATS = 10, CHECKSUM" -Server $Server -ConnectionTimeout 0 -QueryTimeout 65535 -ea stop}
    ELSE
    {}

Basically I added trap {$_.Exception.Message; exit 1; continue} right in front of each Invoke-Sqlcmd statement and ended each Invoke-Sqlcmd statement with -ea stop.

The trap $_.Exception.Message traps any error... collects the error message, then exit 1 immediate exits the PS script with an exit code of 1.

The SQL job reads each step with either a 0 or 1 and automatically interprets 0 as success and 1 as failure and marks the SQL Job as a success or failure correctly. Plus, since I captured the actual error message...it shows up in the SQL job history.

This wound up being exactly what I need. :)

If you're curious...here's the blogs that helped me the most:

  • Returning an exit code from a PowerShell script (weblogs.asp.net)
  • Trapping errors (Powershell.com)


来源:https://stackoverflow.com/questions/3823661/show-failure-for-sql-2005-job-that-executes-powershell-script-through-cmd-prompt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!