Trevor Sullivan's Tech Room

Minding the gap between administration and development

PowerShell: Getting started with WMI Events

Posted by Trevor Sullivan on 2009/11/16


Introduction

PowerShell version 1 provided good integration with WMI using the Get-WmiObject cmdlet, allowing you to easily retrieve and modify WMI instances, and call WMI methods, but PowerShell v2 has taken it, and many other things, a lot farther. One of those areas is eventing, and not just WMI eventing, but responding to WMI events is what I’d like to discuss in this article. Because WMI contains a large repository of information regarding a system’s hardware and software state, it is useful to understand WMI events, so that you can determine where they can fit into your environment. This topic is mostly geared towards systems administrators or engineers that are looking to do some advanced monitoring of their systems.

To get started with WMI events in PowerShell v2, I’m going to show you an example of how to use WMI events to detect DHCP lease changes. DHCP renewals occur infrequently, relative to the frequency of many other event types, however forcing a DHCP renewal is easy, and therefore makes for a good development example. Also, it seems like every other article out there uses Win32_Process instance creations or deletions for their WMI eventing examples, so I figured I would try something a little bit different. Keep in mind that DHCP lease updates are only one of thousands of potential uses for WMI events; I am simply attempting to convey the concept to you, so that you can figure out other methods of using it for your own needs.

About WMI Event Queries

For those of  you who may be unfamiliar with WMI event queries (aka. notification queries), they use a syntax similar to this:

SELECT <Properties> FROM <EventClass> WITHIN <Seconds> WHERE TargetInstance ISA '<WmiClass>' AND <OtherCriteria>

Now, writing an event query is a little bit different from a standard select query. Why, you ask? Well, the system event classes you’ll work with respond to WMI instance events for all of the built-in WMI classes. In other words, they are responsible for a very large number of events. Because of this, you will need to write a query that provides extensive filters in order to avoid some inherent limitations of WMI. The main event classes of which I speak are as follows:

  • __InstanceCreationEvent – occurs when a WMI instance is created (eg. a Win32_Process being instantiated)
  • __InstanceDeletionEvent – occurs when a WMI instance is deleted (eg. a Win32_Process terminating)
  • __InstanceModificationEvent – occurs when a WMI instance is modified (eg. a Win32_Process uses additional memory, or deallocates some)
  • __InstanceOperationEvent – occurs when a WMI instance is created, deleted, or modified (any of the above)

These are the main WMI event classes you’ll work with, unless you have another specific need, for example:  monitoring a 3rd party piece of software, or are looking to monitor installation or deletion of WMI classes (as opposed to instances of classes).

If you would like to test out writing an event query, you can use the Wbemtest utility, that is included with Windows 2000 and up (XP, 2003, Vista, 2008, 7, 2008 R2). Simply type “wbemtest” at the run prompt, or from a command prompt, then use the “Connect” button to connect to the “root\cimv2” namespace, and you should be presented with a window that looks similar to the below screenshot. Make sure that you select the “Asynchronous” method invocation option, as that will enhance wbemtest’s GUI performance during event queries; If you leave it at the default of “Semisynchronous“, you will probably find the GUI frustrating to work with, as it hangs during the polling interval.

Wbemtest

WbemTest Utility (connected to root\cimv2)

To test out an event query select the “Notification Query” button, and you’ll be presented with a box to type your query into. For now, just to get you started, an easy query to type here would be:

SELECT * FROM __InstanceModificationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_Process'
Notification Query Window

Notification Query Window (wbemtest)

Once you’ve written your query, select “Apply,” wait the interval you specified (in the WITHIN clause), and you will start to see WMI event instances start to populate in the “Query Result” window. This window will remain open (and subscribed to events) until you close it. You can double-click on these event instances to open them, and then close the “Query Result” window if you’d like to stop polling for events.

Wbemtest Notification Query Results

Event Query Results (wbemtest)

Here is what you’ll see when you double-click on one of these events:

WMI Event Instance

WMI Event Instance (wbemtest)

As you can see, the event instance contains a few useful properties:

  • TIME_CREATED – When the event occurred (we’ll talk about how to interpret this later)
  • SECURITY_DESCRIPTOR – Not sure what this is used for, but it appears to be NULL typically
  • PreviousInstance – The WMI instance in its state, prior to the event
  • TargetInstance – The WMI instance in its state, after the event occurred

By double-clicking on the PreviousInstance and TargetInstance properties (and then clicking the “View Embedded” button), we can view the WMI instance, and its properties, both before the event occurred, and after the event occurred. Because we have access to both of these states in an __InstanceModificationEvent, we can do a comparison to see which properties actually changed, and which ones didn’t. The __InstanceCreationEvent, __InstanceDeletionEvent, and  __InstanceOperationEvent classes do not have the PreviousInstance property, because they are only dealing with a WMI instance in a single state, not a before/after state.

Property Editor for TargetInstance (wbemtest)

Property Editor for TargetInstance (wbemtest)

Object Editor for TargetInstance (wbemtest)

Object Editor for TargetInstance (wbemtest)

The above screenshot is a representation of the WMI instance after it was modified. Had we gone through and opened the PreviousInstance property, we would have seen a similar screen for it as well. On top of that, you can click the “Show MOF” button, which generates the Managed Object Format (MOF) syntax that represents that object. This is great for development and troubleshooting, because you can copy/paste both the MOF syntax for both PreviousInstance and TargetInstance into a text editor, and compare all of the different property values that changed. In fact, to save even having to do that, you can go all the way back to the __InstanceModificationEvent screen, click “Show MOF” there, and it will include the sub-instances (embedded objects).

__InstanceModificationEvent MOF Syntax (wbemtest)

__InstanceModificationEvent MOF Syntax (wbemtest)

For now, this concludes the section about WMI event queries, and how to test them using the wbemtest utility. If you have other questions regarding WMI Events, please review the resources available on MSDN.

Building Our DHCP Lease WMI Event Query

In this section, let’s talk a little bit more deeply about the WMI event query we need to create in order to detect DHCP lease changes. Remember, the goal of this article is to show you how to detect and respond to WMI events that indicate a change in the DHCP lease time. At any point, we can invoke a DHCP lease renewal by issuing the command:

ipconfig /renew

or (from PowerShell):

([wmiclass]"Win32_NetworkAdapterConfiguration").RenewDHCPLeaseAll()

Granted, the PowerShell method is a little longer, but it’s also more understandable, as we’re directly calling the WMI API to initiate the DHCP renewal, rather than going through a software utility. I just wanted to make sure you understood that, without “ipconfig”, there is still an easy way to do this through PowerShell. That’s the beauty of PowerShell … simple access to .NET and WMI objects! Anyway, we’re getting off-topic here.

Now that we know how to initiate a DHCP renewal, we can talk about how to write our event query in such a way that we can pick up on this event. It so happens that there is a WMI class called Win32_NetworkAdapterConfiguration in the root\cimv2 WMI namespace, which contains information about the network adapters in a computer (not restricted to only physical adapters). This class has some useful properties:

Win32_NetworkAdapterConfiguration WMI Class (wbemtest)

Win32_NetworkAdapterConfiguration WMI Class (wbemtest)

The “DHCPLeaseObtained” property is pretty self-explanatory, and contains the date & time that the DHCP lease was obtained on a particular network adapter. Now, keep in mind that you will typically see a number of instances of the Win32_NetworkAdapterConfiguration, but normally only the “real” network interfaces (eg. wired, wireless, or VPN) will actually have a non-null value for “DHCPLeaseObtained“. This will be helpful information for writing our WMI event query.

For our first attempt at writing an event query, we might come up with something as simple as this:

SELECT * FROM __InstanceModificationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_NetworkAdapterConfiguration'

After all, that query will return any changed instances of network adapter configurations, but what is the problem with that? Well, the problem is that we’ll get back events for a whole lot more than just DHCP lease changes. So then, how do we zero in the information we need? If you check out my (albeit high-level) original definition of a WMI event query, you’ll notice the “AND <OtherCriteria>” part at the end. Thankfully, we can dig deeper into objects’ properties, as part of our query, to determine a restricted set of returned instances. Because we already know that: 1) there is a DHCPLeaseObtained property, and 2) we have access to both a PreviousInstance and TargetInstance, we can construct a query that looks like this:

SELECT * FROM __InstanceModificationEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_NetworkAdapterConfiguration' AND TargetInstance.DHCPLeaseObtained <> PreviousInstance.DHCPLeaseObtained

If you examine the above query, you’ll see that we’re further restricting the query, by only returning instances where the target instance’s DHCPLeaseObtained property does not match the previous instance’s DHCPLeaseObtained property. What this effectively gives us, is only events where the DHCP renewal time has changed, and nothing else! So, for the remainder of this article, the query directly above this paragraph is what we’ll use to detect DHCP lease time changes.

Before we go on, and now that we have identified the query to use, let’s test it out using wbemtest:

Wbemtest

1. Open wbemtest, connect to rootcimv2, select Asynchronous, and click Notification Query

Wbemtest - Notification Query

2. Paste your notification query and click Apply

PowerShell - DHCP Renewal

3. Use PowerShell to initiate a DHCP renewal

Wbemtest - Notification Query Result

4. Double-click the resulting event

Wbemtest - Event Object

5. Click the Show MOF button

Wbemtest - Event Object MOF

6. Check out the PreviousInstance and TargetInstance values for the DHCPLeaseObtained property!

This completes the current section, on determining the WMI event query to identify DHCP lease changes. Next, we’ll look at how to use all of this with PowerShell.

PowerShell WMI Event Cmdlets

Now that we’ve talked about WMI event queries, how to test them out by themselves first, and how to build our DHCP lease event query, we are ready to talk about the PowerShell cmdlets that allow us to easily put those queries to good use. The easiest way to get started with PowerShell and events, is to simply issue the command:

help *event*
PowerShell - Help Event Command

Help *event* Command

From this command’s output, you’ll see that there are a number of event-related Cmdlets as well as a HelpFile to get us started. We’ll concern ourselves with just a few of these cmdlets however:

  • Register-WmiEvent – Registers an event subscription and allows you to specify a PowerShell ScriptBlock to respond to the event
  • Get-EventSubscriber – Retrieves a list of all current event subscriptions (not just WMI ones)
  • Unregister-Event – Unregisters event subscriber(s) (not just WMI ones)

Remember, for any PowerShell cmdlets, simply type the following to get full documentation on how to use it:

help <cmdletname> -full
help Register-WmiEvent -full
Register-WmiEvent Help

Register-WmiEvent Help

If you type “Register-WmiEvent” without any parameters, you’ll be prompted for a WMI class name. Unfortunately, for our purposes, this can be deceiving, because as we talked about above, we don’t actually want to register for all event instances, only certain ones. Because of this, we’ll need to specify the “-Query” parameter on the “Register-WmiEvent” cmdlet, which lets us set the event / notification query we want to use. Your event query can be stored in a PowerShell string variable also, but for the sake of this article, we’ll just keep it in-line with the cmdlet. Now technically, we could run this cmdlet using only the “-Query” parameter like this:

Register-WmiEvent no Action

Register-WmiEvent without an action

Running this command yields a new event subscription, but what happens when an event is actually triggered? You guessed it, nothing by default. Actually, what this does is puts events into an event queue, so they can be retrieved using the Get-Event cmdlet, but for now we want an immediate response to our event. So, let’s un-register the event subscription we created and try again.

PowerShell - Unregister-Event

Unregistering PowerShell event handlers

If we want something to actually happen in response to these events we are subscribing to, we’ll need to specify an action using the “-Action” parameter on the “Register-WmiEvent” cmdlet. The action parameter allows us to specify a PowerShell script block to respond to events. This could be as simple as a quick “Write-Host” command, or we could call a pre-defined function as our event handler (eg. function “DhcpLeaseChangeHandler“). Let’s stick with a simple “Write-Host” for now though, and then call a DHCP renewal to test it out. I’ll talk about some more advanced options in the next section.

PowerShell-Register-WmiEvent-Complete

Our completed sample!

So that’s it! If you’ve made it this far, you’ve figured out how to register for WMI events and respond to them using a PowerShell script block! If you’re interested, stick around, and I’ll show you how to do some even cooler stuff in the next section.

Advanced WMI Event Handling

Alright, so you’ve made it far enough to figure out how to register WMI event handlers, but you want a bit more. Maybe you’re asking yourself: “Can I see information about the event from the script, like I did using wbemtest?” If so, then you’ll be glad to hear that the answer is “yes!”

To start off, let’s look at the help for the “-Action” parameter of “Register-WmiEvent“:

-Action <scriptblock>
Specifies commands that handle the events. The commands in the Action parameter run when an event is raised ins
tead of sending the event to the event queue. Enclose the commands in braces ( { } ) to create a script block.

The value of the Action parameter can include the $Event, $EventSubscriber, $Sender, $SourceEventArgs, and $Sou
rceArgs automatic variables, which provide information about the event to the Action script block. For more inf
ormation, see about_Automatic_Variables.

When you specify an action, Register-WmiEvent returns an event job object that represents that action. You can
use the cmdlets that contain the Job noun (the Job cmdlets) to manage the event job.

Required?                    false
Position?                    102
Default value                The event is added to the event queue.
Accept pipeline input?       false
Accept wildcard characters?  false

From this, we see that there are a few built-in variables that enable us to capture event information, with a reference over to the “about_Automatic_Variables” help file. The $Event variable sounds pretty promising, doesn’t it? Let’s take a look at that (in the aforementioned help file):

$Event
Contains a PSEventArgs object that represents the event that is being
processed.  This variable is populated only within the Action block of
an event registration command, such as Register-ObjectEvent. The value
of this variable is the same object that the Get-Event cmdlet returns.
Therefore, you can use the properties of the $Event variable, such as
$Event.TimeGenerated , in an Action script block.

That sounds like what we’re after: information about the event that gets created. So let’s replace our “-Action” script block with the following: { $Global:MyEvent = $Event }. This way, when an event gets created, it will assign the event to the global $MyEvent variable, so we can play with it.

PowerShell-Register-WmiEvent-Advanced-MyEvent

Checking out the contents of $Event

As we can see, the $Event variable (which we reassigned to $MyEvent) contains a PSEventArgs object. If we issue a few more commands to the PowerShell console, we can discover the underlying WMI event object, so we can retrieve relevant information from it.

The underlying WMI event object

The underlying WMI event object

Finally! We’ve gotten to the bottom of our WMI event object, and can now check out the WMI properties that we wanted to compare, in order to determine the differences between the different instances of Win32_NetworkAdapterConfiguration. Let’s take a look at the DHCPLeaseObtained property on both our TargetInstance and PreviousInstance. Also, let’s look at the .NET API to take the TIME_CREATED property and convert it to a readable DateTime format; It’s simple, trust me.

PowerShell - WMI Event Object

The underlying WMI Event Object

There they are … the two CIM_DATETIME values that represent our DHCPLeaseObtained values before and after the event occurred (that we triggered)! The TIME_CREATED property is a 64-bit integer that represents the number of 100 nano-second intervals that have occurred between 12:00:00 AM January 1st, 1601 and the time that the event was generated. Don’t ask.

This example shows that you can dynamically retrieve event information on-the-fly from a PowerShell WMI event subscription. This information could be used in other ways, such as sending an e-mail alert to an administrator, calling an executable, or just about anything else you could think of.

Conclusion

This article stemmed from my interest in learning about event support in PowerShell version 2.0. I hope that by portraying my experiences with PowerShell events, you are able to learn something as well.

Please pass any feedback you may have on to me via pcgeek86@gmail.com or in the comments of this article.

Advertisements

9 Responses to “PowerShell: Getting started with WMI Events”

  1. tojo2000 said

    Excellent write-up. I’ve been putting off learning about WMI events, and I can’t wait to put this into use.

  2. Sitaram said

    Thanks for excellent article on WMI event notifications. I am sure it really helps many sysadmins like me. I thought it deserves a post in my blog and made it.

    http://www.sitaram-pamarthi.com/2009/12/understanding-wmi-event-notification.html

  3. Awesome write up. Thanks for taking the time to put this together.

  4. […] Incredibly nice article on WMI events in PowerShell by Trevor Sullivan: PowerShell: Getting started with WMI Events […]

  5. Robert Robelo said

    Nice!

  6. […] See this link about working with WMI events: http://trevorsullivan.net/2009/11/16/powershell-getting-started-with-wmi-events/ […]

  7. […] as well as For some background on temporary WMI event subscriptions with PowerShell, check out my article on WMI events. If you’re interested in permanent event subscriptions, that don’t depend on PowerShell running […]

  8. […] See this link about working with WMI events: http://trevorsullivan.net/2009/11/16/powershell-getting-started-with-wmi-events/ […]

  9. […] Trevor Sullivan’s getting started with WMI events […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: