Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Watching the Watcher with PowerShell

Posted on November 14, 2019November 14, 2019

ostrichIf you followed along with my recent articles about my PowerShell based backup system, you may recall that I used a PowerShell scheduled job an an event subscriber to monitor for file changes in key folders that I want to back up. I created the scheduled task to run at Windows startup and so far it appears to be working just fine. However, I did catch one instance where the scheduled task stopped. I didn't find any reason, although I didn't dig too deeply either. I simply restarted the scheduled task. But it got me thinking that since I'm relying on this task to log new and changed files, I need to make sure it is watching. In other words, I need to watch the watcher. This is the approach I took.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

Checking the Task

It is simple enough to use Get-ScheduledTask to verify the status.

checking scheduled task

If it has stopped, I can use Start-ScheduledTask. Naturally, I don't want to remember to run this several times a day so I took a slightly different approach using my PowerShell profile script. Because I always have a PowerShell session open this seemed like a smart idea. The first thing I do when logging on is to open a PowerShell session. In my PowerShell profile script I added this code.

$task = Get-ScheduledTask -taskname DailyWatcher
if ($Task.state -eq 'Ready') {
    Write-Host "Restarting scheduled task $($task.Taskname)" -ForegroundColor yellow
    $task | Start-ScheduledTask
}

I realize this only runs once every few days but it's something. However, because my PowerShell session is pretty much constantly running I can create an event subscriber watching for the scheduled task to change.

Monitoring the Watcher

If you recall from earlier articles, an event subscriber only lasts for as long as your PowerShell session is open. That's why I created DailyWatcher subscription in a scheduled task that is constantly running. Since my PowerShell session is open almost as constantly, I can create an interactive event subscriber. If I restart PowerShell, I'll simply get a new version of the event subscriber. In my profile script I added these lines:

#create the event subscription to monitor the scheduled task
. C:\scripts\MonitorDailyWatcher.ps1 | Out-Null

Let's take a peek at this script.

#requires -version 5.1 
#requires -module CimCmdlets

#verify the scheduled task exists and bail out if it doesn't.
$name = "DailyWatcher"
Try {
    $task = Get-ScheduledTask -TaskName $Name -ErrorAction Stop
}
catch {
    Throw $_
    #make sure we bail out
    return
}

#if by chance the task is not running, go ahead and start it.
if ($task.State -ne 'running') {
    $task | Start-ScheduledTask
}

<#
the scheduled task object is of this CIM type
Microsoft.Management.Infrastructure.CimInstance#Root/Microsoft/Windows/TaskScheduler/MSFT_ScheduledTask
#>


$query = "Select * from __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'MSFT_ScheduledTask' AND TargetInstance.TaskName='$Name'"
$NS = 'Root\Microsoft\Windows\TaskScheduler'

#define a scriptblock to execute if the event fires
$Action = {
 $previous =  $Event.SourceEventArgs.NewEvent.PreviousInstance
 $current =  $Event.SourceEventArgs.NewEvent.TargetInstance
 if ($previous.state -eq 'Running' -AND $current.state -ne 'Running') {
    Write-Host "[$(Get-Date)] Restarting the DailyWatcher task" -ForegroundColor green
    Get-ScheduledTask -TaskName DailyWatcher | Start-ScheduledTask
 }
}
Register-CimIndicationEvent -SourceIdentifier "TaskChange" -Namespace $NS -query $query -MessageData "The task $Name has changed" -MaxTriggerCount 7 -Action $action

The script is written to be independent of my PowerShell profile. It first verifies that scheduled task exists and bails out with an error if it isn't found. The script will also start the task if it is not running. Now for the good stuff.

One way to create an event subscriber is through WMI. We've always been able to set up something to watch for changes to an instance of something from WMI. But because I think of the WMI-specific cmdlets as deprecated, I am going to use the CIM equivalent commands. The command to create the event subscription is Register-CimIndicationEvent. The key element is the query.

$query = "Select * from __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'MSFT_ScheduledTask' AND TargetInstance.TaskName='$Name'"

The __InstanceModificationEvent reference is to a system class that should exist in all namespaces. This class should fire an event whenever a changes is made to a specified type of object. In my query this is specified by using the TargetInstance property and the ISA operator. If the modified object is a ScheduledTask, which I discovered by piping Get-ScheduledTask to Get-Member, and if the specific scheduled task name matches my task, an event will be triggered and handled by the event subscriber. The other very important part of this query is the polling. In the query I'm asking to be notified within 10 seconds of a change. In other words, check every 10 seconds. You want this number to be small enough to meet your needs but not so small that you end up constantly polling.

The other element in my script is the Action scriptblock. When an event fires, part of the event arguments are references to the previous instance of the object and the new, or target instance. This comes in handy when when want to do something based on a property difference which is more or less what I am doing. If the previous instance was running, and the new instance is not running, then I'll start the scheduled task. It is really that simple.

When  my profile runs, I end up with this event subscriber.

a PowerShell event subscriber

If the task stops, the event subscriber runs the action script block, which as written, displays a message using Write-Host in my PowerShell session.

An alert from the PowerShell event subscriber

I'm not expecting this to happen often if at all. This code is more like insurance for me.

Creating a CIM-based event subscription can be a useful management tool. I recommend starting out simple and be sure to read full help and examples.

I'll be back next time with some other fun stuff derived from my PowerShell backup work.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d