By: Diana Moldovan | Updated: 2016-02-29 | Comments | Related: > PowerShell
Problem
Unfortunately the SQL Server Agent error logging can be deceiving for job steps when you run PowerShell scripts. I had a situation where the job looked like it finished properly, but after investigating I noticed that the job did not run as planned. In this tip I explain how I setup my SQL Server Agent Jobs to get correct error messaging from my PowerShell scripts.
Solution
Recently I had set up an index maintenance procedure for one of my clients to run off hours. While the index maintenance job was running, the Windows error log on the web server became full of warning messages about application timeouts, because there were still clients accessing the website after hours.
As a "quick and dirty" workaround, I chose to redirect the client's website to a maintenance page while running the index maintenance procedure and then revert this change after the maintenance was completed. According to the product documentation, this involves a sequence that I found suitable for automation through a SQL Server Agent Job having these steps:
- PowerShell step - changing the website's application pool mode to "classic" (to avoid a redirect loop)
- PowerShell step - changing the web.config file to allow the redirection to the maintenance page
- T-SQL step - run the index maintenance procedure
- PowerShell step - revert the site's application pool mode back to "integrated" (which is the default)
- PowerShell step - revert the changes done to the web.config file
Because I needed to work with objects not inside SQL Server (files and application pool settings), I expected permission issues. For example, let's try to change the application pool mode using the following PowerShell script:
#you need this module to access and change the app pool settings Import-Module WebAdministration Write-Host -ForegroundColor Green "WebAdministration module imported..." #getting the app pool(s) you need to change $pool = Get-ChildItem IIS:\AppPools | where {$_.Name -eq "your_app_pool"} Write-Host -ForegroundColor Green "application pool found..." #changing the app pool mode value $pool.managedPipelineMode = "Classic" $pool | Set-Item Write-Host -ForegroundColor Green "Done..."
As you can see from the results above, you need to run the script under an administrative account. If you go to the "computer management" console you'll see that the app pool's mode was unchanged. There is no error handling in this example, so I inserted a few Write-Host lines just to show the errors are non-terminating.
Now try to run the script under an administrative account - run PowerShell or your ISE "as an administrator". This time there are no errors and the setting is changed as expected. I addressed the permission issue in SQL Server Agent by running the PowerShell steps under a proxy account based on an admin account, but what if I forget to change the default proxy, let's see how to catch PowerShell errors in SQL Server Agent.
Create Sample SQL Server Agent Job
Let's create a SQL Server Agent Job with one step that runs the PowerShell script above. I saved the file and named it changing_settings_no_error_handling.ps1. To test the issue of not catching the error, make sure the step executes with a non-privileged account, such as the SQL Server Agent service account.
USE [msdb] GO /****** Object: Job [ChangingAppPoolSettings_no_err_handling] ******/ BEGIN TRANSACTION DECLARE @ReturnCode INT SELECT @ReturnCode = 0 /****** Object: JobCategory [[Uncategorized (Local)]] ******/ IF NOT EXISTS ( SELECT name FROM msdb.dbo.syscategories WHERE name = N'[Uncategorized (Local)]' AND category_class = 1 ) BEGIN EXEC @ReturnCode = msdb.dbo.sp_add_category @class = N'JOB', @type = N'LOCAL', @name = N'[Uncategorized (Local)]' IF ( @@ERROR <> 0 OR @ReturnCode <> 0 ) GOTO QuitWithRollback END DECLARE @jobId BINARY(16) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name = N'ChangingAppPoolSettings_no_err_handling', @enabled = 1, @notify_level_eventlog = 0, @notify_level_email = 0, @notify_level_netsend = 0, @notify_level_page = 0, @delete_level = 0, @description = N'No description available.', @category_name = N'[Uncategorized (Local)]', @owner_login_name = N'your_login', @job_id = @jobId OUTPUT IF ( @@ERROR <> 0 OR @ReturnCode <> 0 ) GOTO QuitWithRollback /****** Object: Step [PowershellScript] *****/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @jobId, @step_name = N'PowershellScript', @step_id = 1, @cmdexec_success_code = 0, @on_success_action = 1, @on_success_step_id = 0, @on_fail_action = 2, @on_fail_step_id = 0, @retry_attempts = 0, @retry_interval = 0, @os_run_priority = 0, @subsystem = N'PowerShell', @command = N'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe Your_script_location\changing_settings_no_error_handling.ps1', @database_name = N'master', @flags = 0 IF ( @@ERROR <> 0 OR @ReturnCode <> 0 ) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 IF ( @@ERROR <> 0 OR @ReturnCode <> 0 ) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' IF ( @@ERROR <> 0 OR @ReturnCode <> 0 ) GOTO QuitWithRollback COMMIT TRANSACTION GOTO EndSave QuitWithRollback: IF ( @@TRANCOUNT > 0 ) ROLLBACK TRANSACTION EndSave: GO
In SSMS, right click the job and choose "Start job...". You'll see that it looks likes everything ran without issue. However, if you check the application pool's "managed pipeline mode" setting, you'll find it is unchanged. Check the job history - there is no error reported at the job level, the message says that "The job succeeded...".
But if we look at the job step we can see a message telling us that something is wrong: "The job script encountered the following errors. These errors did not stop the script...". However, the message ends with "Process Exit Code 0. The step succeeded.". Sure this is confusing and if the job has more steps that follow "on success" or "on failure", the workflow will lead to unexpected results, since the previous step is "successful", but in fact it did not change the app pool setting as expected.
From this message you can see that the root of the problem is that the errors encountered don't stop the PowerShell script from running - they are non-terminating errors and the script doesn't have any error handling.
Add Error Handling to the PowerShell Script
Let's add error handling to the PowerShell script and run the SQL Server Agent Job again.
Here is an updated version of the initial PowerShell script above. I removed the Write-Host commands and the comments. I also put in the actual name of the app pool that I am working with. I also added Try Catch code to the script.
$ErrorActionPreference = "Stop"try{ Import-Module WebAdministration $pool = Get-ChildItem IIS:\AppPools | where {$_.Name -eq "culinary"} $pool.managedPipelineMode = "Classic" $pool | Set-Item } catch{ #use Throw or Write-Error here Throw }
After running the SQL Server Agent Job again, the "error reporting" is the same - apparently all went OK and you have to "dig deeper" to realize that this is not true.
Add Error Handling to SQL Server Agent Job Step
What if I also add error handling to the Command textbox of the step properties screen, as shown below:
$ErrorActionPreference = "Stop" try{ C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe C:\Notes\MSSQLTips\Powershell\ChangingSettings\changing_settings_no_error_handling.ps1 } catch{ Throw }
This time the error is correctly reported at the job level ("The job failed.") and at the job step level. The Process Exit Code reported at the step level is -1.
Notice the Throw statement from the catch block. If it is used alone in a catch block, the Throw statement will re-throw the exception caught and the "outer script" stops because of the $ErrorActionPreference value. In fact, it is enough to stop the PowerShell step if an error occurs. You can simply set the $ErrorActionPreference to "Stop". You don't need the try-catch block in the SQL Server Agent Job step command. The SQL Server Agent Job step command can be as simple as below and the error will be reported just as you see in the picture above.
$ErrorActionPreference = "Stop" C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe C:\Notes\MSSQLTips\Powershell\ChangingSettings\changing_settings_no_error_handling.ps1
Next Steps
Originally I ran the code snippets on SQL Server 2012 SP2 and PowerShell v4, but they will also work on SQL 2014 SP1 and PowerShell v5.
- Check and update your SQL Server Agent Jobs with PowerShell steps to avoid such situations.
- Check out these other PowerShell tips
- Check out these other SQL Server Agent tips
About the author
This author pledges the content of this article is based on professional experience and not AI generated.
View all my tips
Article Last Updated: 2016-02-29