Tales from the Script - January 2003

A Brief Introduction to WMI Events

scrptguy

By The Scripting Guys

We're going to get a bit radical this month. Normally, we base this column around answering one of the most commonly asked questions that we receive on the scripter alias. We aren't going to do that this time. Instead, we're going to answer a question that we never, or at least far too rarely, see on scripter.

Very few people are asking us questions about WMI Events. That just doesn't seem right. Here's this really cool feature of WMI - it can be a bit complicated - and yet we aren't getting any questions about it. Could it be that you folks aren't familiar with WMI's ability to handle events? Could it be that no one has told you anything about it and you haven't stumbled across the right book?

Well, just in case, this month's column will give you a brief introduction to the topic. Although you should be able to just plow ahead with this column and learn something about WMI events, it would be helpful if you had at least a nodding acquaintance with the general workings of WMI. If you feel like getting up to speed on WMI before proceeding, we highly recommend the WMI Primer series over on MSDN; reading the first one should be enough to get you primed and ready to learn about events.

OK, before we learn about WMI Events, we should probably be sure we know what we mean by events. Events are things that occur within a computer environment. Actually, those are potential events. When you actually register an interest in an occurence and it takes place, that's an event.

In simple terms, a WMI event is a notification that something of interest – that is, something you want to be notified about – has occurred. For example, you might want to be notified when your web server's total processor utilization exceeds the 90% threshold for some number of intervals. Or you might want to be notified when your company's file server drops below 10% of its total storage capacity. WMI events are the mechanism WMI provides that let you configure, receive and respond to system changes using a script.

Another way to think of WMI events is that WMI events are to WMI what alerts are to Performance Monitor, what traps are to SNMP, and what rules are to Microsoft Operations Manager (MOM). All four represent ways to monitor and respond to system and/or network changes.

Enough talk already. Let's script. The following script monitors the processes on your local machine (the machine on which you run the script.) The event it is looking for is the creation of the notepad.exe process. To try the script, type it into Notepad and save it as MonitorNotepad.vbs. Then open a command-prompt window, navigate to the directory where you saved the script and type C:\scripts>cscript MonitorNotepad.vbs.

strComputer = "."
Set objWMIService = GetObject("winmgmts://" & strComputer & "/root/cimv2")
strWQL = "SELECT * " & _
         "FROM __InstanceCreationEvent " & _
         "WITHIN 2 " & _
         "WHERE TargetInstance ISA 'Win32_Process' " & _
         "AND   TargetInstance.Name = 'notepad.exe'"
WScript.Echo "Waiting for a new instance of Notepad to start..."
Set objEventSource = objWMIService.ExecNotificationQuery(strWQL)
Set objEventObject = objEventSource.NextEvent()
WScript.Echo "A new instance of Notepad was just started."

When you first run the script you should see something like the following:

sg010301

If you then run a new instance of Notepad, the script will respond with the following:

sg010302

So, you're now equipped with the ability to monitor Notepad. That should save you hours of frustration, right? OK, maybe not. But the fact that you can use a script similar to the one above to monitor any WMI managed resource on any WMI-enabled computer, well, that could save you hours of frustration.

Suppose you have a really busy intranet server. Suppose, further, that you have a corporate web development department that occasionally posts some nasty code to that intranet server that somehow manages to kill the web service. When that web service stops, your users start. The immediate, albeit temporary, fix is pretty straightforward - restart the web service. It would be useful if you could have this automated. The following script demonstrates how you can do just that. It detects the stopped web service and attempts to restart it. As you're typing it in, take note of how it differs from the previous Notepad monitoring script.

strComputer = "."
Set objWMIService = GetObject("winmgmts://" & strComputer & "/root/cimv2")
strWQL = "SELECT * " & _
         "FROM __InstanceModificationEvent " & _
         "WITHIN 2 " & _
         "WHERE TargetInstance ISA 'Win32_Service' " & _
         "AND   TargetInstance.Name = 'w3svc'" & _
         "AND   TargetInstance.State = 'Stopped'"
Set objEventSource = objWMIService.ExecNotificationQuery(strWQL)
WScript.Echo "Monitoring the web service..."
Set objEventObject = objEventSource.NextEvent()
WScript.Echo "Web service just stopped....waiting for a few seconds."
WScript.Sleep(5000)
WScript.Echo "Attempting to restart the web service using the net.exe tool."
Set objShell = CreateObject("WScript.Shell")
objShell.Run "%COMSPEC% /c net start w3svc",,1
WScript.Echo "Restarted the web service"

To experience the script in action, type it into Notepad and save it as MonitorWeb.vbs. Then ensure that the W3SVC service on your local computer is running (net start w3svc works nicely). Next, run the script by typing C:\scripts>cscript MonitorWeb.vbs at a command prompt. You should see something like the following:

sg010303

Then open another command prompt and stop the web service by typing C:\>net stop w3svc. The script should note that the web service has stopped and display a corresponding message:

sg010304

At this point, when the script detects that the web service has stopped, it simply uses the line:

WScript.Echo "The web service just stopped."

to display a message; but you could easily add code to restart the web service, check it again to ensure the restart worked and send you an email summarizing all that occurred. For those of you who read our first Tales from the Script column, you could easily run net start w3svc using the Run or Exec method. For those of you who have delved into WMI by reading our WMI Primer series, you could use WMI to restart the service. In any case, it's fairly straightforward. Here is one way to do it by using the Run method, for example:

strComputer = "."
Set objWMIService = GetObject("winmgmts://" & strComputer & "/root/cimv2")
strWQL = "SELECT * " & _
         "FROM __InstanceModificationEvent " & _
         "WITHIN 2 " &_
         "WHERE TargetInstance ISA 'Win32_Service' " & _
         "AND   TargetInstance.Name = 'w3svc'" & _
         "AND   TargetInstance.State = 'Stopped'"
Set objEventSource = objWMIService.ExecNotificationQuery(strWQL)
WScript.Echo "Monitoring the web service..."
Set objEventObject = objEventSource.NextEvent()
WScript.Echo "Web service just stopped....waiting for a few seconds."
WScript.Sleep(5000)
WScript.Echo "Attempting to restart the web service using the net.exe tool."
Set objShell = CreateObject("WScript.Shell")
objShell.Run "%COMSPEC% /c net start w3svc",,1
WScript.Echo "Restarted the web service"

To give this script a try, start by ensuring that the web service (w3svc) is running. Next, run the script. Once it displays the message, Monitoring the web service, open a second command prompt window and type C:\>net stop w3svc to stop the service. The script should notice that the web service was stopped and, after pausing for a few seconds to ensure the service is finished shutting down, try to restart it by issuing a net start w3svc command. The following shows output from the script:

sg010305

Hopefully, you're starting to see some potential here. If that's the case, you're probably also interested in a line-by-line explanation of one of these scripts - and that's exactly how we're going to wrap up for this month.

The line:

strComputer = "."

just stores the name of the computer that you will attach to in the next line of the script. The dot just means use the local computer. If you're in a networked environment and you're a local administrator on another nearby computer, try setting the strComputer variable to the name of that computer instead of the dot (ex. strComputer = WebServer01). The monitoring script will then be monitoring things on that remote computer instead of the local one from which you're actually running the script.

This line:

Set objWMIService = GetObject("winmgmts://" & strComputer & "/root/cimv2")

Establishes a connection with the WMI Service and stores a reference to that connection in the objWMIService variable. Actually, it goes a bit further and specifies a specific WMI namespace for the connection as well - the /root/cimv2 namespace. You can think of a namespace as being much like a folder. Instead of grouping together a certain set of files, as a folder does, a WMI namespace groups together a set of WMI classes. The /root/cimv2 namespace happens to house both the Win32_Service WMI class as well as the Win32_Process WMI class, so it was used in all of our examples so far. In fact, the /root/cimv2 namespace houses most of WMI classes of interest to system admins - so you will see it, and use it, often in your scripts. Given that you're bound to run into it, we should probably tell you what the cimv2 bit stands for - it's 2. If you're interested in learning more about the origins of that name, along with the origins of WMI itself, you'll want to get ready for some not-so-light-hearted reading, and head over to the Distributed Management Task Force (DMTF) website.

The next set of lines are used to construct a WMI Query Language (WQL) query. Because this particular query is being used to initiate event monitoring, it is known as an event notification query. If you're familiar with SQL, then the query should like quite familiar. If not, don't worry about it, you just need to understand what a few of the elements of the query are referring to, so that you can change them and thereby modify what the query does.

The first important bit is __InstanceModificationEvent. This can be changed to __InstanceCreationEvent or __InstanceDeletionEvent. Hopefully it's pretty clear that changing this to __InstanceDeletionEvent, for example, would cause the script to monitor for the deletion of something instead of the modification of something.

The WITHIN 2 part specifies how frequently, in seconds, the polling of a resource should take place. Checking something every 2 seconds is probably a bit much in most cases, but we've done it here just to make the examples snappy.

The 'ISA Win32_Service' part specifies the WMI class that represents what you want to monitor. If you don't know much about WMI classes, read the WMI Primer article on MSDN. If you want to find out which WMI classes are available for you to use, download the Scriptomatic and experiment with it.

The .Name = 'w3svc' and .State = 'Stopped' parts are just property specifications that narrow the monitoring to a particular resource - in this case a Win32_Service with a Name property equal to w3svc and a State property equal to Stopped.

strWQL = "SELECT * " & _
         "FROM __InstanceModificationEvent " & _
         "WITHIN 2 " & _
         "WHERE TargetInstance ISA 'Win32_Service' " & _
         "AND   TargetInstance.Name = 'w3svc'" & _
         "AND   TargetInstance.State = 'Stopped'"

The next line actually starts the monitoring process by running the notification query.

Set objEventSource = objWMIService.ExecNotificationQuery(strWQL)

This line just lets the user know that the monitoring has begun.

WScript.Echo "Monitoring the web service..."

This next line is an important one. It pauses the script until the next event is received. Things stop at this point, and the next part of the script will not be run until the event we've registered to monitor occurs and we receive notification.

Set objEventObject = objEventSource.NextEvent()

Once notification is received, the script moves beyond the pausing or blocking line and runs whatever comes next. What comes next should be your response to the notification. In this case, we are simply letting the user know that the event was received.

WScript.Echo "The web service just stopped."

That might be useful in some cases, but more often than not, you'll want to take some action at this point.

So there you have it - consider yourself introduced to WMI Events. We hate to be clich, but sometimes you have to go the extra mile for your fellow scripters: 'we've only just scratched the surface of this really useful management technology. That said, we hope that we've given you enough here to get you thinking about how you can put WMIs eventing capabilities to work in your organization. Tune in next month, when we'll dig a bit deeper and try to answer some of the questions that we're hoping this article will invite. As always, write to us and let us know if you do anything cool and/or useful with this stuff.

We hope you had a safe and happy holiday season,

The Scripting Guys

That's all we have time (and space) for this month. Any questions or comments, please write to us at scripter@microsoft.com (in English, if possible).

For a list and additional information on all Tales from the Script columns, click here.