We are almost finished on our journey to manage services with PowerShell. In this article I thought it might be helpful to determine when something changes in a service such as if it starts or stops. We can accomplish this in PowerShell with a concept called eventing. Windows is based on this idea of events. When something happens, an “event” occurs. We can tell PowerShell to watch for certain kinds of events and do something when they occur.

Now, before we get too far into this I want to point out that I’m not a big fan of using PowerShell scripts to monitor events on a long-term basis. If you really need to know when a critical service has stopped, you need to invest in appropriate management software. PowerShell, and what I’m going to demonstrate, is great for short-term, ad-hoc monitoring such as when troubleshooting a problem.

Using WMI Events

The first technique is to use WMI. In much the same way you subscribe to an RSS feed to be notified when there is something new, we do the same thing. We set up a subscription in WMI to a particular type of event. The event is formulated by a query. In this case the class is one of several system classes related to events.

  • __InstanceOperationEvent
  • __InstanceCreationEvent
  • __InstanceModificationEvent
  • __InstanceDeletionEvent

For a service, we’re primarily interested in modification events. That is, anything that modifies the service. We start with a query like this:

Select * from __InstanceModificationEvent

Without getting too deep into the mechanics, suffice it to say there is a performance price for this type of query. When the query is running PowerShell will check WMI constantly to see if something has changed. That is very resource intensive, especially when querying a remote computer. Instead, we can tell PowerShell to only check, or poll, within a certain time frame, say every 10 seconds.

Select * from __InstanceModificationEvent within 10

Because this is a system class, the query right now would fire events for everything in WMI that is changing. We need to limit the query further. Here we can use a special property called TargetInstance and ISA operator, to tell WMI only watch for instances that are of a specific class.

Select * from __InstanceModificationEvent within 10 where TargetInstance ISA 'Win32_Service'

But, we want to narrow this down to a single service. When the event fires, the TargetInstance object will be written to the pipeline, which will be a service object. This means we can filter on service object properties.

Select * from __InstanceModificationEvent within 10 where TargetInstance ISA 'Win32_Service' AND TargetInstance.Name='bits' and TargetInstance.State='Stopped'

I will save this to a variable, $query. Next this query needs to be registered with WMI to create the event subscription. As luck would have it there is a cmdlet called Register-WMIEvent.

When the query is registered, whenever the event is detected, in this case when the BITS service stops, PowerShell will record the event. For right now let’s register this for the local computer and not do anything but display a message.

Register-WMIEvent -Query $query -sourceIdentifier "StoppedService" -MessageData "The BITS Service has stopped"

The cmdlet won’t write anything to the pipeline, so use Get-EventSubscriber to see the subscription.

PS Scripts:\> Get-EventSubscriber
SubscriptionId   : 2
SourceObject     : System.Management.ManagementEventWatcher
EventName        : EventArrived
SourceIdentifier : StoppedService
Action           : 
HandlerDelegate  : 
SupportEvent     : False
ForwardEvent     : False

This subscription will only last for as long as this PowerShell session is running. How can you tell when an event has fired? Use the Get-Event cmdlet.

PS Scripts:\> get-event
PS Scripts:\>

So nothing yet. I’ll stop the BITS service and wait about 10 seconds to see what happens. Well, nothing interactively. There’s no popup or anything like that. But checking the event queue again shows an event.

PS Scripts:\> get-event
ComputerName     : 
RunspaceId       : 52cef69a-b09d-46fc-be81-6156bf2ce556
EventIdentifier  : 2
Sender           : System.Management.ManagementEventWatcher
SourceEventArgs  : System.Management.EventArrivedEventArgs
SourceArgs       : {System.Management.ManagementEventWatcher, 
SourceIdentifier : StoppedService
TimeGenerated    : 2/15/2013 10:20:52 AM
MessageData      : The BITS Service has stopped

This is a pretty rich object. If I restart the service and it stops again, I’ll get another event. Or if I have multiple subscriptions running, all the events will share the same queue. This is where the SourceIdentifier comes in handy.

PS Scripts:\> $evt = get-event –sourceidentifier StoppedService

I have to drill down a bit, but I can get to not only the target instance object, but the previous instance.

PS Scripts:\> $evt.SourceEventArgs.NewEvent
__GENUS             : 2
__CLASS             : __InstanceModificationEvent
__SUPERCLASS        : __InstanceOperationEvent
__DYNASTY           : __SystemClass
__RELPATH           : 
__DERIVATION        : {__InstanceOperationEvent, __Event, __IndicationRelated, __Sys…
__SERVER            : SERENITY
__NAMESPACE         : //./root/CIMV2
__PATH              : 
PreviousInstance    : System.Management.ManagementBaseObject
TargetInstance      : System.Management.ManagementBaseObject
TIME_CREATED        : 130054152528314476
PSComputerName      : SERENITY

PS Scripts:\> $evt.SourceEventArgs.NewEvent.PreviousInstance.State
PS Scripts:\> $evt.SourceEventArgs.NewEvent.TargetInstance.State

With this type of subscription you have to monitor the event queue. But you can also create a more active subscription.

Responding to Events

I’m going to revise my query to check for any service that changes.

$query = "Select * from __InstanceModificationEvent within 10 where TargetInstance ISA 'Win32_Service'"

When the service changes its state, say from running to stopped, I want to log the change to a text file. I’ll create a scriptblock with the PowerShell commands I want to execute when a matching event fires.

 $previous = $Event.SourceEventArgs.NewEvent.PreviousInstance
 $target = $Event.SourceEventArgs.NewEvent.TargetInstance
 if ($previous.state -ne $target.state) {
  #log the change
  $msg="{0} Service: {1} has changed state from {2} to {3}" -f (Get-
  Write $msg | Out-File -filepath "c:work\ServiceLog.txt" -append

$Event is an automatic variable for the event object that we looked at earlier. If the state of the previous and target instance is different, then the change is logged. I register this action with Register-WmiEvent.

Register-WMIEvent -Query $query -sourceIdentifier "ServiceChange" -Action $action -computername $ENV:COMPUTERNAME

Now when a service starts or stops, the text file is update. However, the event queue won’t show anything when you use Get-Event. If you use an action, Get-Event won’t show anything.

If you would like something more interactive, here’s a sample using the PopUp method from the Wscript.Shell COM Object.

$action = {
 $previous = $Event.SourceEventArgs.NewEvent.PreviousInstance
 $target = $Event.SourceEventArgs.NewEvent.TargetInstance
 if ($previous.state -ne $target.state) {
    $wshell = new-object -COM "Wscript.Shell"
    $msg="{0} Service: {1} has changed state from {2} to {3}" -f (Get-
    #display a message box for 15 seconds. Use -1 to force user to click OK.
    $wshell.Popup($msg,15,"Service Alert",64) | Out-Null
Register-WMIEvent -Query $query -sourceIdentifier "ServiceAlert" -Action $action 
-computername $ENV:COMPUTERNAME

But now look what I get:

Service Events

Service Events

The message box will automatically close in 15 seconds.

Cleaning Up

At some point you will want to clean up. Everything goes away if you close your PowerShell session. Otherwise you can remove the event subscription.

PS Scripts:\> Get-EventSubscriber | unregister-event

This will remove all subscriptions. Or you can specify an identifier. To clear the event queue, run this:

PS Scripts:\> get-event | Remove-Event


These techniques should work for both PowerShell 2.0 and 3.0. And let me re-iterate that I think they are best used for ad-hoc or temporary monitoring. Be sure to read full cmdlet help and examples for everything I’ve demonstrated and try this out in a non-production environment.

The next post in this series will describe how to query events using CIM.


Leave a reply

Please enclose code in pre tags

Your email address will not be published.


© 4sysops 2006 - 2023


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account