How do I monitor Windows Scheduled Tasks?

This one was quite the journey. Obtaining this information wasn’t as easy as one would think. Once I did my research, it turned out to be relatively easy to create a monitor in WhatsUp Gold. I will keep the remarks on that journey below the problem/solution if you are interested.

Problem

  • I have a critical Windows Scheduled Task that I need to ensure runs successfully every time it runs
  • I want to be notified when a task starts as well as when it stops
  • WhatsUp Gold can not monitor Windows Scheduled Tasks directly out of the box

You have a critical Windows Scheduled Task that you need to ensure runs successfully each time it runs. WhatsUp Gold can not monitor this directly out of the box.

Solution

Using a PowerShell active monitor with the research I had done seemed the most simple.

#Get device information
$ip = $Context.GetProperty("Address");
$DnsEntry = [System.Net.DNS]::GetHostByAddress($ip)
$DnsName = [string]$DnsEntry.HostName;
# Get the Windows credentials
$WinUser = $Context.GetProperty("CredWindows:DomainAndUserid");
$WinPass = $Context.GetProperty("CredWindows:Password");
$pwd = ConvertTo-SecureString $WinPass -asplaintext -force
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $WinUser,$pwd

#Task Info list
[array]$Error_List=@("0:The operation completed successfully."
"1: Incorrect function called or unknown function called."
"2: File not found."
"10: The environment is incorrect."
"267008:Task is ready to run at its next scheduled time."
"267009:Task is currently running."
"267010:Task is disabled."
"267011:Task has not yet run."
"267012:There are no more runs scheduled for this task."
"267014:Task is terminated."
"-2147216609: An instance of this task is already running."
"-2147023651: The service is not available (is 'Run only when a user is logged on' checked?)."
"-1073741510:The application terminated as a result of a CTRL+C."
"-1066598274:Unknown software exception."
"267008: The task is ready to run at its next * Scheduled time."
"267009: The task is currently running."
"267010: The task will not run at the * Scheduled times because it has been disabled."
"267011: The task has not yet run."
"267012: There are no more runs * Scheduled for this task."
"267013: One or more of the properties that are needed to run this task on a * Schedule have not been set."
"267014: The last run of the task was terminated by the user."
"267015: Either the task has no triggers or the existing triggers are disabled or not set."
"267016: Event triggers do not have set run times."
"-2147216631: A tasks trigger is not found."
"-2147216630: One or more of the properties required to run this task have not been set."
"-2147216629: There is no running instance of the task."
"-2147216628: The Task * Scheduler service is not installed on this computer."
"-2147216627: The task object could not be opened."
"-2147216626: The object is either an invalid task object or is not a task object."
"-2147216625: No account information could be found in the Task * Scheduler security database for the task indicated."
"-2147216624:Unable to establish existence of the account specified."
"-2147216623: Corruption was detected in the Task * Scheduler security database."
"-2147216622: Task * Scheduler security services are available only on Windows NT."
"-2147216621: The task object version is either unsupported or invalid."
"-2147216620: The task has been configured with an unsupported combination of account settings and run time options."
"-2147216619: The Task * Scheduler Service is not running."
"-2147216618: The task XML contains an unexpected node."
"-2147216617: The task XML contains an element or attribute from an unexpected namespace."
"-2147216616: The task XML contains a value which is incorrectly formatted or out of range."
"-2147216615: The task XML is missing a required element or attribute."
"-2147216614: The task XML is malformed."
"267035: The task is registered, but not all specified triggers will start the task."
"267036: The task is registered, but may fail to start.' Batch logon privilege needs to be enabled for the task principal."
"-2147216611: The task XML contains too many nodes of the same type."
"-2147216610: The task cannot be started after the trigger end boundary."
"-2147216609: An instance of this task is already running."
"-2147216608: The task will not run because the user is not logged on."
"-2147216607: The task image is corrupt or has been tampered with."
"-2147216606: The Task * Scheduler service is not available."
"-2147216605: The Task * Scheduler service is too busy to handle your request.' Please try again later."
"-2147216604: The Task * Scheduler service attempted to run the task, but the task did not run due to one of the constraints in the task definition."
"267045: The Task * Scheduler service has asked the task to run."
"-2147216602: The task is disabled."
"-2147216601: The task has properties that are not compatible with earlier versions of Windows."
"-2147216600: The task settings do not allow the task to start on demand."
)
       
        function Get-TaskStatus ([string]$Status)
        {
            $Return="Return Code is $Status"
            Foreach($E in $Error_List){
             if($E.ToString().Contains($Status))
               { 
                $Return=($E.split(":")[1])
                return $Return;break
               } 
              }
         return $Return
        }
$bDown = 0
$Tasks = gwmi -Query "SELECT * FROM MSFT_ScheduledTask" -Namespace Root/Microsoft/Windows/TaskScheduler -ComputerName $DnsName -Credential $cred
ForEach($Task in $Tasks){
If($Task.TaskPath.Length -eq 1){
  $TaskName = $Task.TaskName
  $TaskOutput = Invoke-WmiMethod -Class PS_ScheduledTask -Namespace ROOT\Microsoft\Windows\TaskScheduler -Name GetInfoByName -ArgumentList $TaskName -ComputerName $DnsName -Credential $cred | Select -expand cmdletOutput | Where LastTaskResult -ne 0 | Select-Object TaskName,LastTaskResult
  If($TaskOutput){
  $bDown = 1
  $TaskName = $TaskOutput.TaskName
  $TaskResult = Get-TaskStatus($TaskOutput.LastTaskResult)
  $Results += "$TaskName - $TaskResult`r`n"}
}
}
If($Results){
$bDown = 1
$Context.SetResult(1, $Results);
}
else {
$Context.SetResult(0, "All scheduled tasks last run completed successfully")
}

Journey

Figuring this one out actually proved to be quite challenging and expanded my knowledge in the “new Microsoft Windows” world where you can’t simply look up data in WMI. In a lot of scenarios, you must run a PowerShell cmdlet in order to populate the information you are looking for — or so I thought.I always start with: where would I normally look for this data? The ‘Task Scheduler’ dialog in Windows has the ‘Last Run Result’ field. But, wait how do I populate it as readable text to WhatsUp Gold? Perhaps using Microsoft’s PowerShell cmdlet, Get-ScheduledTask. That only shows the current state, not the last run result like I want. But if I pipe it too Get-ScheduledTaskInfo, then I get my last run time and last task result as desired. Problem is this is all through PowerShell. Those commands don’t support -ComputerName argument, thus there are additional hoops to jump through as well as potential PowerShell security problems.

Questions

  • Is there a way to trace what is happening in WMI, similar to a SQL trace?
  • Why can’t I run a “single query” like the Windows GUI seems to do when I open my Task Scheduled dialog?

Turns out you can trace WMI activity, but it seemed a bit complicated. I next wondered if someone had already automated the process using PowerShell. Of course, someone already had done this and shared it because it’s the internet and there are almost 8 billion people on the planet and quite a few of them have an internet connection. Using the script I found above, I was able to see that the namespace being called was \\.\Root\Microsoft\Windows\TaskScheduler. Interesting…I wonder what is in that namespace? That question then led me to this nice WMI explorer.

Using a combination of the WMI tracing PowerShell, WMI explorer, and the built-in PowerShell cmdlets I was able to find exactly what I was looking for. I do use SELECT * FROM MSFT_ScheduledTask to get a list of all scheduled tasks, and then invoke the same WMI method the PowerShell commands normally do in order to get my desired output.

The really important thing to note here is in order to gather our data this code uses *ONLY* WMI and *NO* PowerShell commands. This sounds strange considering it’s a PowerShell script, but let me explain a bit. When using plain WMI, you simply need to authenticate using your username and password. When using PowerShell and the -Credential parameter on some commands, you also have other layers of security to take into account as well as remote PowerShell sessions, session timers, among other things.

Because of this, I really wanted to find a “WMI only” way of obtaining the data. This simplifies it’s use universally and eliminates potential backward compatibility problems. Noting that, different Windows Server versions come with different versions of PowerShell ranging from version 2.0 to 6.0. I tested my script all the way back to Windows 2008 and did not note any issues.

So, the idea was to first run the WMI query to get the task name, and then invoke the same WMI method the PowerShell commands normally do. This was easy now that I had all the information. First, let’s collect all of the tasks.

$Tasks = gwmi -Query "SELECT * FROM MSFT_ScheduledTask" -Namespace Root/Microsoft/Windows/TaskScheduler -ComputerName $DnsName -Credential $cred

Now, using a loop let’s cycle through each of the tasks and invoke the WMI method to get it’s last task result.

$TaskName = $Task.TaskName
  $TaskOutput = Invoke-WmiMethod -Class PS_ScheduledTask -Namespace ROOT\Microsoft\Windows\TaskScheduler -Name GetInfoByName -ArgumentList $TaskName -ComputerName $DnsName -Credential $cred | Select -expand cmdletOutput | Where LastTaskResult -ne 0 | Select-Object TaskName,LastTaskResult

This will give us the ability to have a single monitor looking at *ALL* Windows Scheduled Tasks. With some slight modification, you can look for a specific task as opposed to all of them.

Leave a comment

Your email address will not be published. Required fields are marked *


CAPTCHA Image
Reload Image

This site uses Akismet to reduce spam. Learn how your comment data is processed.