11

I have a few powershell scripts that I'm trying to get to trigger as a failed state in the windows task scheduler when they have failures inside them. So I'm doing something like this inside the powershell script. I tried an exit code of 1 or 99, and it doesn't look like windows task scheduler is seeing that as a failure state. So my failure code email doesn't get sent out to notify me.

How do I get task scheduler to see that my powershell script had a failure? It always has event codes of 129 (created task process), 100 (task started), 200 (action started), 110 (task triggered), 201 (action completed), 102 (task completed).

$global:ErrorStrings = New-Object System.Collections.Generic.List[System.Object] #I add strings onto the list as I find errors

$errorCodeAsString = ""
foreach ($item in $global:ErrorStrings.Members){
   $errorCodeAsString += (" " + $item + "..")
}
if($errorCodeAsString -ne "")
{
   write-output  "Error: $errorCodeAsString"
   Exit 99 #Exit 1 didn't cause task scheduler to see error at exit either
}
Exit 0

I know my list is populated with errors because I created them to test it. I checked that the errorCode as string was a length and hit the exit 99 or 1. The task scheduler is still showing the normal event codes.

I have an email alert on failure scheduled and since the event codes aren't showing failures, it will never trigger to send my email. This is windows 10, in case it matters.

I've been looking at powershell errors sql, task scheduler success error, tips tricks scheduled tasks, powershell exit code, but it's not helping.

The powershell scripts are set up in task scheduler like this:

action: start a program

program/script: PowerShell

Add arguments: -ExecutionPolicy Bypass -File C:\Users\me\Documents\powershell\disasterBackup.ps1

Michele
  • 3,617
  • 12
  • 47
  • 81

5 Answers5

13

Part 1 is to have PowerShell return the correct Last Exit Code to Task Scheduler.

This is one of the peculiarities of Task Scheduler. It simply is reporting that, yes, PowerShell.exe ran successfully. The problem is that PowerShell.exe doesn't report back the exit code, because, yes, PowerShell.exe ran correctly, even if the script did not.

The way I have been able to get around this is to switch from running the script with the -File parameter, which doesn't return the exit value, to a -Command parameter. That way I can exit PowerShell.exe with the correct exit code by explicitly exiting with the$LASTEXITCODE value:

#Run Scheduled task with the following command

powershell.exe -Command ". C:\Scripts\RunScript.ps1; exit $LASTEXITCODE"

So in your case it would be:

powershell.exe -ExecutionPolicy Bypass -Command ". C:\Users\me\Documents\powershell\disasterBackup.ps1; exit $LASTEXITCODE"

--- Edit----

Part 2 is to have a Scheduled Task Triggering on an Event when it fails to send an email or something.

The trouble with Task Scheduler is the same thing we had with PowerShell exiting. No matter what exit code is returned, the task always logs an Event ID 201 - Action Completed... which is correct... no matter what, the task completed even if the job that was run failed internally.

Looking further into the Details of the logged Event, we can see the ResultCode in the EventData does get set correctly. So it's a simple job to filter that through the GUI right?.... well no... There is no filter beyond EventID. Now we have to write a custom Event filter to trigger on based on the ResultCode. The XML XPath query that we need is this:

<QueryList>
  <Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
    <Select Path="Microsoft-Windows-TaskScheduler/Operational">
      *[System[(Level=4 or Level=0) and (EventID=201)]]
        and
      *[EventData[Data[@Name='ResultCode'] and (Data='2147942401')]]</Select>
  </Query>
</QueryList>

So to break it down, we want:

Event log: Microsoft-Windows-TaskScheduler/Operational
Event Level: 4 or 0 = Information
Event ID: 201
And
Event Data: ResultCode = 2147942401

If we set the bad exit code to 1, why is ResultCode = 2147942401? because it actually returns 0x1 which is hexadecimal 0x80070001 which equals decimal 2147942401. So you will have to look at the Details of the Event to find the "Correct" ResultCode.

HAL9256
  • 12,384
  • 1
  • 34
  • 46
  • 1
    If I use -Command do I have to still run with start program/script: Powershell? – Michele Dec 21 '18 at 17:31
  • I tried using start program/script of both Cmd and Powershell. When I use Cmd with what you're showing for my case/added arguments, and run it, the task is hanging and doesn't finish in a normal time so I had to terminate it. When I use PowerShell, like I had before, it finishes with normal events in taskScheduler in a normal time but there's no failure exit code. – Michele Dec 21 '18 at 17:50
  • 1
    Yes. Your Program/Script is still `PowerShell.exe` you simply are changing the Arguments to use `-Command` . – HAL9256 Dec 21 '18 at 17:50
  • Then that isn't catching the exit code and seeing it's not exiting normally. All looks good. – Michele Dec 21 '18 at 17:51
  • Any other ideas on how to get taskScheduler to register a different event? It's not registering a different event. – Michele Dec 21 '18 at 17:53
  • Does it have to be an Event? the event will always be Action Completed (201), even when it is bad. Using the above, you will have to use the non 0 return code and that the Last run result will not be "The operation completed successfully. (0x0)" in order to detect a failure. i.e. the Event will only be different if something like the server crashes. – HAL9256 Dec 21 '18 at 17:59
  • I'm not sure how I would watch the last run result. Right now I scheduled an email alert script to send an email out and it's triggered "on an event", with a custom setting/filter looking for event codes. How would I get taskScheduler to run a script on a return code? – Michele Dec 21 '18 at 18:54
  • What you need is to trigger on an event, but, you need to create a custom event filter. See my edits above. – HAL9256 Dec 21 '18 at 23:54
  • 2
    Some great info here. Note that you _can_ make `-File` report an exit code, but only if the script invoked explicitly uses `exit ` to exit the script. Also note that a script may fail due to PowerShell-native commands failing, which won't be reflected in `$LASTEXITCODE`. _0x1 which is hexadecimal 0x80070001_ is a bit confusing: more accurately, the task-execution engine reports non-zero exit codes by bit-oring them with `0x80070000`; to get the embedded exit code, use `2147942401 -band -bnot 0x80070000` or, conversely, `1 -bor 0x80070000L` – mklement0 Dec 23 '18 at 20:37
  • @mklement0 - I exit my script with "Exit 99" and also tried "Exit 1", but don't see anything in the Event list. Is there something to add with the -File in task scheduler to take the exit code? Maybe I'm not understanding the -band -bnot discussion. – Michele Dec 26 '18 at 18:07
  • @HAL9256 - I'm not sure what to do with the xml you're showing in my powershell script or elsewhere. – Michele Dec 26 '18 at 18:39
  • @Michele: If you use `exit 99`, then you should find `2147942499` (`99 -bor 0x80070000L `) in the `ResultCode` field of the `201` event entry's custom event _data_, as described in this answer. It'll also show in the entry's message (the one that starts with "Task Scheduler successfully completed task ... "). – mklement0 Dec 26 '18 at 18:42
  • @Michele: I think the ideas is to create _another_ scheduled task that is triggered by the original task logging action-completed events with a specific (unexpected) exit code. The XPath query can be used as the event filter that defines the trigger; that is, events getting logged that match the filter cause the new task to fire. – mklement0 Dec 26 '18 at 18:47
  • @mklement0 - Last Run Result column in task scheduler library is showing (0x1), not 2147942499. Operational Code column in History shows (1). I didn't see any columns to add in history that were called ResultCode. Maybe Windows 10 is different? – Michele Dec 26 '18 at 19:17
  • @Michele: It is the Task Scheduler UI that shows the true exit code, but the information is only _indirectly_ reflected in the _event log_, as described. – mklement0 Dec 26 '18 at 19:59
  • Oh well, task scheduler UI isn't showing the exit code, as I described. Right now I have my script directly calling the email script instead of having the task scheduler trigger it. Thanks anyway! – Michele Dec 26 '18 at 20:14
  • 1
    I'm glad you found a workaround, @Michele, but just to clarify: The `Last Run Result` column should show `(0x63)` if the exit code was `99`. If it is showing `(0x1)`, perhaps you ran your script with `-Command` instead of `-File` or your script currently uses `exit 1`. You can only select _standard fields_ as display columns, and `ResultCode` is not a standard field; it is embedded in event-instance-specific _custom event data_ associated with event `201`. – mklement0 Dec 27 '18 at 15:19
  • Both `powershell -file script.ps1` and `powershell -command .\script.ps1` work for me. – js2010 Oct 23 '22 at 19:01
1

HAL9256's answer did not work for me when looking for all failed tasks

When I converted his XML to search for all non-zero return codes, it ended up matching all return codes. Both zero and non-zero alike

*[EventData[Data[@Name='ResultCode'] and (Data!='0')]]

Instead, I created a task with an "on an event" trigger and the following custom xml:

<QueryList>
  <Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
    <Select Path="Microsoft-Windows-TaskScheduler/Operational">
      *[System[(Level=4 or Level=0) and (EventID=201)]]
        and
      *[EventData[(Data[@Name="ResultCode"]!=0)]] </Select>
  </Query>
</QueryList>

and runs the following powershell script

Send-MailMessage -To -Subject "My subject" -Body "One of the tasks failed. Please log into the server and view the event log details to find out which task failed" -SmtpServer mail.company.com -From server@company.com

Cary Sweet
  • 11
  • 2
1

I know this is way after the fact, but what I wound up doing for this is that I called an email script directly from my powershell script when it has a failure case.

Michele
  • 3,617
  • 12
  • 47
  • 81
0

If you don't want to do the bit math ($tasknum -band -bnot 0x80070000), get-scheduledtaskinfo returns a lasttaskresult property.

Get-ScheduledTaskInfo script.ps1

LastRunTime        : 10/23/2022 10:51:51 AM
LastTaskResult     : 1
NextRunTime        :
NumberOfMissedRuns : 0
TaskName           : script.ps1
TaskPath           :
PSComputerName     :
js2010
  • 23,033
  • 6
  • 64
  • 66
-2

What I'd do is to attach a scheduled task to the failed event so when this takes place raise the scheduled task.