Use PowerShell to Monitor and Respond to Events on Your Server

Use PowerShell to Monitor and Respond to Events on Your Server

  • Comments 7
  • Likes

Summary: Learn how to use Windows PowerShell to monitor and to respond to events on your computer or server without the need to run a script.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I am interested in learning how to use Windows PowerShell to create a Windows Management Instrumentation (WMI) permanent event consumer. Basically, what I need is a script that will monitor a particular service and if the status of that service changes I need the script to launch a script that will perform a specific action. Is this something I can do with Windows PowerShell, or do I need to compile a Managed Object Format (MOF) file? Everything I can find on the Internet says I need to use a MOF file, but I do not like MOF files and would rather use a Windows PowerShell script so I can create these permanent event consumers on a remote machine.

-- CD

 

Hey, Scripting Guy! AnswerHello CD, Microsoft Scripting Guy Ed Wilson here. You are absolutely correct. There are two cool things about creating permanent event consumers via script. The first thing is that it can easily be performed remotely. Therefore,, I can target a several machines with the script and create the event consumer on the remote machines. The second cool thing is the fact that permanent event consumers are cool. They monitor for and respond to events without the need for a script to be running. Talk about something cool! I run one script one time, and then my computer will always look for an event and respond to it. Dude (or Dudette) that rocks! This means that almost every one of the event and monitoring scripts that are discussed in the Hey, Scripting Guy! Blog can be converted to a permanent event monitor.

Portions of today’s post are adapted from my WMI book, Microsoft Windows Scripting with WMI: Self-Paced Learning Guide, that was published by Microsoft Press in 2006.

In yesterday’s Hey, Scripting Guy! Blog post, I talked about the concepts involved in creating a permanent event consumer.

In yesterday’s post, the script that was created uses the ActiveScriptEventConsumer WMI class to run a VBScript when the status of the Bits service changes. The ActiveScriptEventConsumer WMI class provides the most flexibility because it enables you to run a script which means that in reality it is the only class that you would actually need. However, some other standard consumer classes might be more efficient. For example, if you have to write to an event log, then the NTEventLogEventConsumer is more efficient than the ActiveScriptEventConsumer (although your script could write to the event log, using a specially tailored consumer is generally better). Table 1 details the standard consumer classes. For more information about these classes, see the MSDN documentation.

Table 1

Class name

Description

ActiveScriptEventConsumer

Executes a predefined script in an arbitrary scripting language when an event is delivered to it. This consumer is available on Windows 2000 and above.

ScriptingStandardConsumerSetting

Provides registration data common to all instances of the ActiveScriptEventConsumer class. This consumer is available on Windows 2000 and above.

LogFileEventConsumer

Writes customized strings to a text log file when events are delivered to it. This consumer is available on Windows XP and above.

NTEventLogEventConsumer

Logs a specific message to the Windows NT event log when an event is delivered to it. This consumer is available on Windows XP and above.

SMTPEventConsumer

Sends an email message using SMTP every time that an event is delivered to it. This consumer is available on Windows 2000 and above.

CommandLineEventConsumer

Launches an arbitrary process in the local system context when an event is delivered to it. This consumer is available on Windows XP and above.

 

The CreatePermenantEventConsumer.ps1 script creates a permanent event consumer that monitors the bits service. If the status of the service changes, it fires an event that is received by the ActiveScriptEventConsumer. When the event is received by the ActiveScriptEventConsumer, it runs a simple VBScript that will toggle the status of the browser service. Therefore, if the browser service is running and an event is received, the browser service will be stopped; if the browser service is currently stopped and an event is received it will be started.

CreatePermenantEventConsumer.ps1

$computer = "mred1"

$filterNS = "root\cimv2"

$wmiNS = "root\subscription"

$query = "Select * from __InstanceModificationEvent within 10 where

  targetInstance isa 'Win32_Service' and targetInstance.Name = 'Bits'"

$filterName = "Win32ServiceModification"

$scriptFileName = "C:\fso\ToggleBrowserService.vbs"

$filterPath = Set-WmiInstance -Class __EventFilter `

 -ComputerName $computer -Namespace $filterNS -Arguments `

  @{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL";

    Query=$query}

$consumerPath = Set-WmiInstance -Class ActiveScriptEventConsumer `

 -ComputerName $computer -Namespace $wmiNS `

 -Arguments @{name="ToggleBits"; ScriptFileName=$scriptFileName;

  ScriptingEngine="VBScript"}

Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `

  -Namespace $wmiNS -arguments @{Filter=$filterPath; Consumer=$consumerPath} |

  out-null

The CreatePermenantEventConsumer.ps1 must be run with administrator rights, and before running the script, I right-click the Windows PowerShell ISE icon and select Run as administrator. When the CreatePermenantEventConsumer.ps1 script runs no output is generated. This output of the script and the use of the Get-Service cmdlet to check the status of the bits and the browser service is seen in the following figure.

The command I used to check the status of both services is seen here.

Get-Service -Name browser, bits

 

When checking the status of the services, as well as toggling the bits service, I use the bottom execution pane. This is seen in following figure.

To create an instance of the __EventFilter, I use the Set-WmiInstance. The VBScript code that was discussed yesterday is seen here.

'Create the event filter

set objfilterclass=objActiveScriptConsumer.get("__EventFilter")

set objfilter=objfilterclass.spawninstance_()

objfilter.name="win32ServiceModification"

objFilter.EventNamespace="\root\cimv2" 'NOTE:IS NOT IN THE BOOK! Write at BTM pg.110

objfilter.querylanguage="wql"

objfilter.query=(qQuery)

set filterpath = objfilter.put_

 

When you use the Set-WmiInstance Windows PowerShell cmdlet, each of the __EventFilter WMI class properties are assigned their values via a HashTable. This means the Windows PowerShell command to create the new instance of the __EventFilter WMI class can be done by using one logical line of code. The code is seen here.

$filterPath = Set-WmiInstance -Class __EventFilter `

 -ComputerName $computer -Namespace $filterNS -Arguments `

  @{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL";

    Query=$query}

 

The new instance of the __EventFilter class is stored in the $FilterPath variable. The returned instance of the class is used in the command to bind the filter to the consumer.

The next step is to create the consumer. The VBScript code from yesterday is seen here.

'Create the active script consumer

set  objConsumerClass=objActiveScriptConsumer.get("ActiveScriptEventConsumer")

set objconsumer=objconsumerclass.spawninstance_()

objconsumer.name="ToggleBits"

objconsumer.scriptfilename="C:\FSO\ToggleBrowserService.vbs"

objconsumer.scriptingengine="vbscript"

set consumerpath = objconsumer.put_

 

Again, the Set-WmiInstance Windows PowerShell cmdlet is used to create a new instance of the ActiveScriptEventConsumer WMI class. As before each property receives its value via the hash table. The Windows PowerShell command is seen here. (As before, this is one logical line of Windows PowerShell code. I had to break it down into four lines to display it correctly on the blog platform. If you want to convert it to a single line, copy the following code and back each line back up to the previous line while taking care to remove the backtick, grave, reverse quote, backward apostrophe, or whatever you want to call the ` symbol).

$consumerPath = Set-WmiInstance -Class ActiveScriptEventConsumer `

 -ComputerName $computer -Namespace $wmiNS `

 -Arguments @{name="ToggleBits"; ScriptFileName=$scriptFileName;

  ScriptingEngine="VBScript"}

 

Finally, I have to bind the consumer to the event. To do this, I have to create an instance of the __FilterToConsumerBinding WMI class. The VBScript code to do this (seen yesterday) is seen here.

'Do the binding

FTCB = "__filterToConsumerBinding"

set objbindingclass=objActiveScriptConsumer.get(FTCB)

set objbinding=objbindingclass.spawninstance_()

objbinding.filter=filterpath

objbinding.consumer=consumerpath

objbinding.put_

 

In Windows PowerShell, I use the Set-WmiInstance cmdlet to create and to configure a new instance of the class. In this script, I do not have to save the object that is returned (like I did in each of the two previous commands) and therefore I pipeline the results to the Out-Null Windows PowerShell cmdlet. One thing that I did not mention yesterday is that if a suitable event filter (__EventFilter) already exists, or if a suitable event consumer class already exists, you do not have to create a new instance. All that must occur is for the event and the consumer to be bound together via the __FilterToConsumerBinding class. Although it is unlikely that both a suitable consumer and a suitable filter would already exist, it is definitely possible that one or the other might exist. Here is the Windows PowerShell code to do the binding.

Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `

  -Namespace $wmiNS -arguments @{Filter=$filterPath; Consumer=$consumerPath} |

  out-null

 

One thing I like to do when I am finished creating everything for the new permanent event consumer is to check whether everything looks like it is created correctly. To do this, I use Windows PowerShell’s Get-WmiObject cmdlet. With the gwmi alias, this is short work, although I have to create a simple script to do this. As this is a quick script, I incorporated the gwmi alias instead of typing Get-WmiObject.

ReportPermenantEventConsumer.ps1

gwmi __EventFilter

gwmi ActiveScriptEventConsumer -Namespace root\subscription

gwmi __FilterToConsumerBinding -Namespace root\subscription

 

The report from these three commands is seen here.

PS C:\Windows\system32> C:\data\ScriptingGuys\2010\HSG_12_6_10\ReportPermenantEventConsumer.ps1

 

__GENUS          : 2

__CLASS          : __EventFilter

__SUPERCLASS     : __IndicationRelated

__DYNASTY        : __SystemClass

__RELPATH        : __EventFilter.Name="Win32ServiceModification"

__PROPERTY_COUNT : 6

__DERIVATION     : {__IndicationRelated, __SystemClass}

__SERVER         : MRED1

__NAMESPACE      : ROOT\cimv2

__PATH           : \\MRED1\ROOT\cimv2:__EventFilter.Name="Win32ServiceModification"

CreatorSID       : {1, 5, 0, 0...}

EventAccess      :

EventNamespace   : root\cimv2

Name             : Win32ServiceModification

Query            : Select * from __InstanceModificationEvent within 10 where

                     targetInstance isa 'Win32_Service' and targetInstance.Name = 'Bits'

QueryLanguage    : WQL

 

__GENUS          : 2

__CLASS          : ActiveScriptEventConsumer

__SUPERCLASS     : __EventConsumer

__DYNASTY        : __SystemClass

__RELPATH        : ActiveScriptEventConsumer.Name="ToggleBits"

__PROPERTY_COUNT : 8

__DERIVATION     : {__EventConsumer, __IndicationRelated, __SystemClass}

__SERVER         : MRED1

__NAMESPACE      : ROOT\subscription

__PATH           : \\MRED1\ROOT\subscription:ActiveScriptEventConsumer.Name="ToggleBits"

CreatorSID       : {1, 5, 0, 0...}

KillTimeout      : 0

MachineName      :

MaximumQueueSize :

Name             : ToggleBits

ScriptFilename   : C:\fso\ToggleBrowserService.vbs

ScriptingEngine  : VBScript

ScriptText       :

 

__GENUS                 : 2

__CLASS                 : __FilterToConsumerBinding

__SUPERCLASS            : __IndicationRelated

__DYNASTY               : __SystemClass

__RELPATH               : __FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"ToggleBits\"",Filter="__EventFilter.Name=\"Win32ServiceModification\""

__PROPERTY_COUNT        : 7

__DERIVATION            : {__IndicationRelated, __SystemClass}

__SERVER                : MRED1

__NAMESPACE             : ROOT\subscription

__PATH                  : \\MRED1\ROOT\subscription:__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"ToggleBits\"",Filter="__EventFilter.Name=\"Win32ServiceModification\""

Consumer                : ActiveScriptEventConsumer.Name="ToggleBits"

CreatorSID              : {1, 5, 0, 0...}

DeliverSynchronously    : False

DeliveryQoS             :

Filter                  : __EventFilter.Name="Win32ServiceModification"

MaintainSecurityContext : False

SlowDownProviders       : False

 

Cleanup involves deleting each of the three new instances. In Windows PowerShell it is easy to delete these instances. I use the Remove-WMIObject cmdlet. I have to be very careful doing this to make sure that I delete only the instances I previously created. It turns out that my machine already has an instance of one of the classes installed. I use the Where-Object to filter the one I created. Proper WMI methodology would be to connect to the specific instance of the __FilterToConsumer binding class. But because this is an association class, it has two keys in its path to the instance. The Path to this class is seen here.

__PATH                  : \\MRED1\ROOT\subscription:__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"ToggleBits\"",Filter="__EventFilter.Name=\"Win32ServiceModification\""

 

Using Windows PowerShell and the where-object I filter the name of the __EventFilter, and pipeline the object to the Remove-WmiObject cmdlet and I am done; no fussing with embedded quotation marks, no worrying about line wrapping or any of the typical problems confronting composite keys in WMI. Here is my cleanup code.

gwmi __EventFilter | Remove-WmiObject

gwmi ActiveScriptEventConsumer -Namespace root\subscription | Remove-WmiObject

gwmi __FilterToConsumerBinding -Namespace root\subscription |

Where-Object { $_.filter -match 'Win32ServiceModification'} | Remove-WmiObject

 

CD, that is all there is to working with permanent WMI event consumers. Permanent event consumer week will continue tomorrow when guest blogger Trevor Sullivanwill discuss a Windows PowerShell permanent event consumer module.

I invite you to follow me on Twitter or Facebook. If you have any questions, send email to me at scripter@microsoft.com or post them on the Official Scripting Guys Forum.. See you tomorrow. Until then, peace.

 

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed Thanks for all I learned from your lessons. One little question: Why is the cmdlet called Set-WmiInstance, not Set-WmiObject? The answer will broaden/deepen my understanding of what the Powershell / WMI connection is all about. I hope.

  • @Jacques - The cmdlet is called Set-WmiInstance because it only works with instances of the class. For example, the Win32_Service WMI class returns 173 instances of the class on my Windows 7 computer. If I want to change the start password of one of those instances, I would use Set-WmiInstance because I am only working with an instance of the class. Now, on the other hand, if I want to create a new service (a new instance of the Win32_Service WMI Class) I would need to use the [wmiclass] type accelerator. It would look like the following [wmiclass]"Win32_Service" this is because there is no instance of the class to work with. I am therefore working with the class itself, and not an instance of the class.

  • What if I'm using Powershell v1.0 ?

  • @Tony the Set-WmiInstance WMI cmdlet was introduced in Windows PowerShell 2.0, and therefore you would have to use a different technique to do this.

  • Hi Ed,

    Please help

    I ran your code on powershell 2.

    Os: window 7 both 32 bit and 64 bit

    Got below error   any solution ?

    # -----------------------------------------------------------------------------

    # Script: CreatePermenantEventConsumer.ps1

    # Author: ed wilson, msft

    # Date: 7/13/2012 14:42:09

    # Keywords: Scripting Techniques, WMI, Events and Monitoring

    # comments: Creates permenant event consumer that monitors a folder for new

    # file creation. It then fires a VBS that launches a Windows PowerShell script

    # HSG-7-18-2012

    # -----------------------------------------------------------------------------

    $computer = "edlt"

    $filterNS = "root\cimv2"

    $wmiNS = "root\subscription"

    $query = @"

    Select * from __InstanceCreationEvent within 30

    where targetInstance isa 'Cim_DirectoryContainsFile'

    and targetInstance.GroupComponent = 'Win32_Directory.Name="c:\\\\test"'

    "@

    $filterName = "NewFile"

    $scriptFileName = "C:\fso\LaunchPowerShell.vbs"

    $filterPath = Set-WmiInstance -Class __EventFilter `

    -ComputerName $computer -Namespace $wmiNS -Arguments `

     @{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL";

       Query=$query}

    $consumerPath = Set-WmiInstance -Class ActiveScriptEventConsumer `

    -ComputerName $computer -Namespace $wmiNS `

    -Arguments @{name="CleanupFileNames"; ScriptFileName=$scriptFileName;

     ScriptingEngine="VBScript"}

    Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `

     -Namespace $wmiNS -arguments @{Filter=$filterPath; Consumer=$consumerPath} |

     out-null

     Error :

     Set-WmiInstance : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)

    At line:21 char:30

    + $filterPath = Set-WmiInstance <<<<  -Class __EventFilter `

       + CategoryInfo          : InvalidOperation: (:) [Set-WmiInstance], COMException

       + FullyQualifiedErrorId : SetWMICOMException,Microsoft.PowerShell.Commands.SetWmiInstance

    Set-WmiInstance : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)

    At line:27 char:32

    + $consumerPath = Set-WmiInstance <<<<  -Class ActiveScriptEventConsumer `

       + CategoryInfo          : InvalidOperation: (:) [Set-WmiInstance], COMException

       + FullyQualifiedErrorId : SetWMICOMException,Microsoft.PowerShell.Commands.SetWmiInstance

    Set-WmiInstance : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)

    At line:32 char:16

    + Set-WmiInstance <<<<  -Class __FilterToConsumerBinding -ComputerName $computer `

       + CategoryInfo          : InvalidOperation: (:) [Set-WmiInstance], COMException

       + FullyQualifiedErrorId : SetWMICOMException,Microsoft.PowerShell.Commands.SetWmiInstance

    Solution tried : nothing worked

    1) no firewall running

    2)Host name or IP address is wrong or the remote computer is shutdown.--Not required

    3)The "TCP/IP NetBIOS Helper" service  running.

    4)The "Remote Procedure Call (RPC)" service is  running

    5)The "Windows Management Instrumentation" service is  running

    6) Key: HKLM\Software\Microsoft\OLE, value: EnableDCOM --y