PowerShell and Events: WMI Temporary Event Subscriptions

It’s been a while since my last post in my series on PowerShell and Events (February to be exact), but now that I have some projects caught up, it is time to make a return to wrap up this series in what will be 2 or 3 articles, including this one.

Something that I consider to be a powerful technique is using WMI events to monitor some part a system such as disk space, processes or even the event logs. The nice part about this is that you can configure some sort of action to occur when this is tripped such as an email alert or just a display on the console. Considering the massive scope of the WMI repository, you can see that the options are almost limitless with what you can monitor and report on!

Before I show off some examples, we first need to know what is used as far as classes go and how we even get to the point of setting up a temporary subscription.

What do you mean temporary?

When creating a WMI event in PowerShell, you are merely creating something that exists only in the PowerShell console that is running as a background job. This means that as soon as you close the console, the event monitoring goes away with it. Same for rebooting the system and removing the background job or using Unregister-Event to stop the event subscription. Obviously not a permanent monitoring solution by any means, but it does get the job done if you want something that isn’t meant to last forever (or a day Winking smile).

Cmdlets used for WMI Events

Really, the only cmdlet that is required for creating a WMI event is Register-Event. This cmdlet will return a background job object showing that it is now performing the monitoring that you specified and will also perform an action as well if specified. This cmdlet has the same type of parameters as Register-ObjectEvent and Register-EngineEvent, both of which I have explained in detail here and here. Refer to these for more on the parameters such as –Forward, –MessageData and –SupportEvent. If you have used either of the other Register* cmdlets before, then you should feel closer to home as far as the syntax goes. The big thing to grasp is the WMI filter query used to register the event.

Useful classes to know

Working with WMI Events means that you need to know what kind of classes are available to initiate the subscriptions. The following classes are the ones that I have used or read about and will prove useful when registering for the WMI Events:

Win32_ProcessStartTrace http://msdn.microsoft.com/en-us/library/windows/desktop/aa394374(v=vs.85).aspx
Win32_ProcessStopTrace http://msdn.microsoft.com/en-us/library/windows/desktop/aa394376(v=vs.85).aspx
__InstanceModificationEvent http://msdn.microsoft.com/en-us/library/windows/desktop/aa394651(v=vs.85).aspx
__InstanceCreationEvent http://msdn.microsoft.com/en-us/library/windows/desktop/aa394649(v=vs.85).aspx
__InstanceDeletionEvent http://msdn.microsoft.com/en-us/library/windows/desktop/aa394650(v=vs.85).aspx
RegistryTreeChangeEvent http://msdn.microsoft.com/en-us/library/windows/desktop/aa393041(v=vs.85).aspx
RegistryKeyChangeEvent http://msdn.microsoft.com/en-us/library/windows/desktop/aa393040(v=vs.85).aspx
RegistryValueChangeEvent http://msdn.microsoft.com/en-us/library/windows/desktop/aa393042(v=vs.85).aspx

Working down the line of classes, the ProcessTrace classes are used to report when a process has started or stopped.

The __Instance* classes are intrinsic event classes which reports a change to the WMI repository, such as a creation or modification. An example of this would be to use one of the __Instance* classes with Win32_Service to track when a service starts or stops. More on this with examples later.

Lastly on this list, the Registry* classes are extrinsic events which cannot be linked directly to the WMI model. Regardless, you can use this to receive a notification if a part of the registry that has already been defined has been modified of deleted.

With all of these classes that can be used in various combinations, you could say that possibilities on what can be monitored are endless!

I won’t be covering all of these classes but from the examples that I show you later, you can use the other classes to perform various types of monitoring.

Windows Query Language (WQL)

In order to configure the event monitoring with WMI, you must know how to use WQL to setup the filter so the event subscription actually knows what it is looking for. Here is one example:

"select * from __instanceModificationEvent within 5 where targetInstance isa 'win32_Service'"

Breaking it down, I will show you what is happening with this filter.

“select * from __instanceModificationEvent” Specify properties which are returned from query.
“within 5” Poll every 5 seconds for event (this doesn’t mean something will be missed, it just means that it may take 5 seconds to acknowledge the event).
“where targetInstance isa ‘win32_Service'” Where filters the scope down and isa applies a query to the subclasses of a specified class

 

For more information regarding WMI queries, check out the following article on more keywords related to WQL: http://msdn.microsoft.com/en-us/library/windows/desktop/aa394606(v=vs.85).aspx

Example using Win32_Service

I already began with my query above using Win32_Service, so lets flesh this out and make an actual subscription using Register-WMIEvent. Some of this may seem familiar if you have checked out my other eventing articles.

##Service change
$WMI = @{
    Query = "select * from __instanceModificationEvent within 5 where targetInstance isa 'win32_Service'"
    Action = {
            If ($Event.SourceEventArgs.NewEvent.PreviousInstance.State -ne $event.SourceEventArgs.NewEvent.TargetInstance.State) {
            $Global:Data = $Event
            Write-Host ("Service: {0}({1}) changed from {2} to {3}" -f $event.SourceEventArgs.NewEvent.TargetInstance.DisplayName,
                                                                            $event.SourceEventArgs.NewEvent.TargetInstance.Name,
                                                                            $event.SourceEventArgs.NewEvent.PreviousInstance.State,
                                                                            $event.SourceEventArgs.NewEvent.TargetInstance.State) -Back Black -Fore Green
            }
    }
    SourceIdentifier = "Service.Action"
}
$Null = Register-WMIEvent @WMI

Nothing really different here, I am using splatting to setup my WMI parameters for Register-WMIEvent cmdlet. I use the same query as mentioned above for the filtering and then my action statement is a scriptblock which tells the subscription what to do when an event is tripped. The action block is set in its own scope and by that I mean when it uses the automatic variable $Event, it is limited only to that script block. You can work around this by setting a Global variable to save $Event to so it can be viewed later.

image

There is a lot happening here with this particular object for the Win32_Service class and the change of the Firewall service from Running to Stopped. From the picture above, you can see where the Action block specified in the code wrote a message on screen saying that the service went from a Running to Stopped state. But how was I able to figure this out? Diving into the $Data variable that was created to hold the Event object, we can begin to see where all of this data resides.

$Data.SourceEventArgs.NewEvent

image

Looking at the SourceEventArgs.NewEvent property, you can see the TIME_CREATED which lets us know when the service change took place. There are two other properties which hold a lot of value to me: PreviousInstance and TargetInstance. The PreviousInstance shows the state of the service prior to a change while the TargetInstance shows the current state of the service. This is why I chose to use an If statement in my Action scriptblock to only worry about the State changes. If I had not done this, I would receive notifications for other changes in services, such as the startmode changing or something else.

PreviousInstance (Previous State)

image

TargetInstance (Current state)

image

Knowing this, I can easily show the previous state of the service as well as show its current state in my display. By the way, the TargetInstance object returned is not a live WMI object, in this case it is System.Management.ManagementBaseObject#root\CIMV2\Win32_Service. But what happens if I try to use the Start() method of this object? Watch and see.

image

If I wanted to restart this service, then I would either use Invoke-WMIMethod or Start-Service using the service name given to achieve that goal. Cool, right? Lets see an example using the Win32_ProcessStartTrace and Win32_ProcessStopTrace to track the processes on my system.

Process Watcher

This example uses the same Register-WMIEvent cmdlet but instead of working with the __Instance* classes for the intrinsic events, I will instead use the readily available Process*Trace classes instead to track processes which have started and stopped running on my local system.

##New Process
$WMI = @{
    Query = "select * from Win32_ProcessStartTrace"
    Action = {
        Write-Host ("Process: {0}({1}) started at {2}" -f $event.SourceEventArgs.NewEvent.ProcessName,
                                                        $event.SourceEventArgs.NewEvent.ProcessID,
                                                        [datetime]::FromFileTime($event.SourceEventArgs.NewEvent.TIME_CREATED)) -Back Black -Fore Green
    }
    SourceIdentifier = "Process.Created"
}
$Null = Register-WMIEvent @WMI
 
##Process End
$WMI = @{
    Query = "select * from Win32_ProcessStopTrace"
    Action = {
        Write-Host ("Process: {0}({1}) was terminated at {2}" -f $event.SourceEventArgs.NewEvent.ProcessName,
                                                                $event.SourceEventArgs.NewEvent.ProcessID,
                                                                [datetime]::FromFileTime($event.SourceEventArgs.NewEvent.TIME_CREATED)) -Back Black -Fore Yellow
    }
    SourceIdentifier = "Process.Deleted"
}
$Null = Register-WMIEvent @WMI

Since my system is not really active right now, I have some self-inflicted processes starting and stopping to give you an idea as to what you will see on the console.

image

With some extra work, this could be a very simple procmon to track what is running.

Tracking the Event Log for Local Account Creations

The last example in the __Instance* classes are going to highlight monitoring the event log on a local system for a account creation attempt. This is being done on a Windows 2012 server so the event log ids may be different if running a 2003 server. In this case, the event id is 4720 for a new account creation (same id for 2008).  What is interesting is that even if I input a password that doesn’t meet the complexity requirements, it will actually create the account, attempt to reset the password to the password given, then upon failure remove the account. With that, lets kick off the monitoring.

##Event log watch -- New User Creation on local system
$WMI = @{
    Query = "select * from __InstanceCreationEvent where TargetInstance isa 'Win32_NtLogEvent' and TargetInstance.logfile = 'Security' and (TargetInstance.EventCode = '4720')"
    Action = {
        $AccountCreated = $event.SourceEventArgs.NewEvent.TargetInstance.insertionstrings[0]
        $CreatedBy = ("{0}\{1}" -f $event.SourceEventArgs.NewEvent.TargetInstance.insertionstrings[5],$event.SourceEventArgs.NewEvent.TargetInstance.insertionstrings[4])
        Write-Host -Foreground Green -Back Black ('New Account: {0} was created by: {1}' -f $ACcountCreated,$CreatedBy)
        $Global:data = $Event
    }
    SourceIdentifier = "Account.Created"
}
$Null = Register-WMIEvent @WMI
 

 

image

Works like a champ! Diving deeper into the object reveals more information about the account itself:

image

As I did with my little console alert, you can narrow down the scope of what you want to report on just by picking what properties are important to you. With the event log monitoring, you have a pretty robust way of monitoring any event that you want on a system.

I’ve shown examples for the Instance* and Process*Trace events and now I will wrap this up with a Registry* class example just to give you an idea as to what it is capable of.

Registry Monitoring

This example will demonstrate how to monitor a registry tree using the RegistryChangeEvent class for the extrinsic event.

##Registry Change
$WMI = @{
    Query ="Select * from RegistryTreeChangeEvent where Hive='HKEY_LOCAL_MACHINE' AND RootPath='Software\\'"
    Action = {
        $Global:Data = $Event
        Write-Host 'Something happened!' -ForegroundColor Green -BackgroundColor Black
    }
    SourceIdentifier = "RegistryTree.Changed"
}
$Null = Register-WMIEvent @WMI

I can make a change anywhere down the HKLM:\Software tree and it will register an event.

image

What I ended up doing is deleting a registry value under Software\Test and that tripped the event. What you can’t see is what was deleted or where it was deleted at! Kind of unfortunate but at least it is something.

The truth is that this is the same type of output you will receive with the other Registry*ChangeEvent classes. The difference is that you have to fine-tune what you are looking at with either the Key or Value that you wish to monitor. At least with these you can tie in some more granular action block items using Get-Item or Get-ItemProperty under the registry provider to verify the new values or check to see if the Key or Value has been removed.

As shown in this article, using WMI temporary events can prove to be a useful thing if you just need a quick monitor with no care about what happens if the console closes or something else happens that causes the event monitoring to stop. While this wasn’t an exhaustive article on temporary WMI events, I hope that it will provide a decent road map in your future scripts to utilize this technique.

My last article regarding PowerShell and Events will conclude with working with permanent WMI events and how they will survive a reboot and keep on monitoring.  It won’t be my next article because I have others already planned to work on, but it will get done!

This entry was posted in powershell and tagged , , , . Bookmark the permalink.

4 Responses to PowerShell and Events: WMI Temporary Event Subscriptions

  1. mbourgon says:

    Boe, I just wanted to say THANK YOU. I’ve been digging into WMI Eventing lately, and these posts (and PoSHeventUI) have been freaking invaluable. If you run into me at PASS or a SQL Sat, I owe you a beer!

  2. Pingback: PowerShell eventing – Subscribing to WMI events - 4sysops

  3. Pingback: Introducing PoshEventUI: A UI Way to Create Permanent WMI Events | Learn Powershell | Achieve More

  4. Pingback: PowerShell and Events: Permanent WMI Event Subscriptions | Learn Powershell | Achieve More

Leave a comment