I try to automate as much as I can with Windows 7 Task Scheduler but I found out Task Scheduler does not check at all the return code of the completed program. It always shows tasks status as "Action completed" no matter what errors happened during the execution. Because it always shows as completed, it's not possible to automatically send an email if task fails. Because it never fails.
There was not any decent answer even in Microsoft's Technet. So I had to dig into XPath event filters to get it working.
Creating the XPath filter
First we create the appropriate XPath filter. We can test the filter in Event viewer. Open Event viewer and open context menu for "Custom Views". Select "Create custom view". A window opens and select it's tab "XML". Check "Edit query manually", pass the warning and copy-paste this filter there:<QueryList>
<Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
<Select Path="Microsoft-Windows-TaskScheduler/Operational">
*[System[
(EventID=201)
]]
and
*[EventData[
Data[@Name="ResultCode"]!=0
]]
</Select>
</Query>
</QueryList>
Press "Ok" and "Save filter to Custom View" windows opens. Give the custom view a name and press "Ok". Now you have a view that should show all tasks that reported completed but actually have return code other than 0.
XPath syntax seems to be simple so you can try other search conditions also. You may have to blacklist or whitelist what programs you want alerts from. There are tasks that will return codes other than 0 even when the execution was ok. For instance task action "Display a Message" returns 1 as a result code so if you create a task that triggers on failed executions and as an action shows a message, you will end up with infinite loop...
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
<Select Path="Microsoft-Windows-TaskScheduler/Operational">
*[System[
(EventID=201)
]]
and
*[EventData[
Data[@Name="ResultCode"]!=0 and
( Data[@Name="TaskName"]!="\DONT_SHOW_THIS_TASK" and
Data[@Name="TaskName"]!="\DONT_SHOW_THIS_TASK_TOO")
]]
</Select>
</Query>
</QueryList>
Using XPath filter as an event trigger
When you have a working filter we can create scheduled task for it. Create a new task and select "On an event" as a trigger. Select "Custom" instead of "Basic" and press "Edit Event filter". Select tab "XML" and copy-paste your filter there.Sending mail as an event action
I don't have any easy answers for you about setting the mail. The "Send an e-mail" action in Task Scheduler does not work with mail systems that require authentication.I have Cygwin environment and configured SSMTP for these type of mails so I simply created a batch file to call SSMTP to send me an email.
One problem is you don't get any information in mail about what actually went wrong. You can only trigger static emails "Something is wrong". With cron it's easier because it sends you stderr as message body.
Using Powershell scripts as Tasks
One thing to remind you is if you are using Powershell scripts, you should always use -command instead of -file.If you have task:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noninteractive -nologo -file BadJob.ps1
It does not return Result code! This is an obvious bug in Powershell.
You have to use:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noninteractive -nologo -command .\BadJob.ps1
The XPath filter is brilliant and exactly what I was looking for.
ReplyDeleteI use PowerShell to send the email alerts. Just have the action call PowerShell and pass -command EmailAlert.ps1 -TaskID 101. Or whatever task ID the alert is for. This will grab that last task ID that matches and include in the email.
# Parameters are variables too
param ([parameter(Mandatory=$true)][ValidateNotNullOrEmpty()][int[]]$taskid)
# Computer specific variables
$computer = (Get-Childitem env:computername).Value
$logname = "Microsoft-Windows-TaskScheduler/Operational"
$event = get-winevent -FilterHashtable @{Logname=$logname;ID=$taskid} -MaxEvents 1
# Email specific variables
$from = "$computer<$computer@fictional.com>"
$to = "thing1@fictional.com, thing2@fictional.com"
$subject = $event.TaskDisplayName + " - " + $computer
$body = $event.Message
$smtpserver = "smtp.fictional.com"
# Send email
Send-MailMessage -From $from -To $to -Subject $subject -Body $body -SmtpServer $smtpserver