PowerShell WMI events
In my previous post ( http://richardsiddaway.spaces.live.com/blog/cns!43CFA46A74CF3E96!2598.entry or http://richardsiddaway.spaces.live.com/blog/cns!43CFA46A74CF3E96!2598.entry ) I started to look at WMI events in PowerShell v2. The win32_process class was used but all that showed us was that a process had started. We need a bit more information. A bit of digging brought up the Win32_ProcessStartTrace class that seems to do what we want.
Register-WMIEvent allows us to specify the class we want to use rather than a query – however if we try that we don’t get anything returned - oops.
Looking through the help for Register-WMIEvent shows that we have the possibility of performing an action when the event occurs. The action scriptblock can use a number of variables including $Event, $EventSubscriber, $Sender, $SourceEventArgs, and $SourceArgs automatic variables. Wanting to understand these variables I tried dumping it though get-member.
PS> Register-WmiEvent -Query "Select * FROM Win32_ProcessStartTrace" -Action {$Event | gm}
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
2 c1016218-f80... NotStarted False $Event | gm
The subscription runs as a PowerShell job. Using the opening of Notepad to trigger the event we can see that data is returned.
PS> Get-Job
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
2 c1016218-f80... Running True $Event | gm
And see that we have a few properties to play with. ComputerName may come in useful if we are dealing with remote machines.
PS> Receive-Job -Id 2
TypeName: System.Management.Automation.PSEventArgs
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
ComputerName Property System.String ComputerName {get;}
EventIdentifier Property System.Int32 EventIdentifier {get;}
MessageData Property System.Management.Automation.PSObject MessageData {get;}
RunspaceId Property System.Guid RunspaceId {get;}
Sender Property System.Object Sender {get;}
SourceArgs Property System.Object[] SourceArgs {get;}
SourceEventArgs Property System.EventArgs SourceEventArgs {get;}
SourceIdentifier Property System.String SourceIdentifier {get;}
TimeGenerated Property System.DateTime TimeGenerated {get;}
The properties look similar to those we saw in the last post. Lets dig into SourceEventArgs
PS> Register-WmiEvent -Query "Select * FROM Win32_ProcessStartTrace" -Action {$Event.SourceEventArgs | gm}
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
3 8d4246a5-5f8... NotStarted False $Event.SourceEventArgs...
PS> Get-Job
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
3 8d4246a5-5f8... Running True $Event.SourceEventArgs...
PS> Receive-Job -Id 3
TypeName: System.Management.EventArrivedEventArgs
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Context Property System.Object Context {get;}
NewEvent Property System.Management.ManagementBaseObject NewEvent {get;}
Only thing here that look interesting is NewEvent
PS> Register-WmiEvent -Query "Select * FROM Win32_ProcessStartTrace" -Action {$Event.SourceEventArgs.NewEvent | gm}
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
4 0857a744-1d3... NotStarted False $Event.SourceEventArgs...
PS> Receive-Job -Id 4
TypeName: System.Management.ManagementBaseObject#\Win32_ProcessStartTrace
Name MemberType Definition
---- ---------- ----------
ParentProcessID Property System.UInt32 ParentProcessID {get;set;}
ProcessID Property System.UInt32 ProcessID {get;set;}
ProcessName Property System.String ProcessName {get;set;}
SECURITY_DESCRIPTOR Property System.Byte[] SECURITY_DESCRIPTOR {get;set;}
SessionID Property System.UInt32 SessionID {get;set;}
Sid Property System.Byte[] Sid {get;set;}
TIME_CREATED Property System.UInt64 TIME_CREATED {get;set;}
__CLASS Property System.String __CLASS {get;set;}
__DERIVATION Property System.String[] __DERIVATION {get;set;}
__DYNASTY Property System.String __DYNASTY {get;set;}
__GENUS Property System.Int32 __GENUS {get;set;}
__NAMESPACE Property System.String __NAMESPACE {get;set;}
__PATH Property System.String __PATH {get;set;}
__PROPERTY_COUNT Property System.Int32 __PROPERTY_COUNT {get;set;}
__RELPATH Property System.String __RELPATH {get;set;}
__SERVER Property System.String __SERVER {get;set;}
__SUPERCLASS Property System.String __SUPERCLASS {get;set;}
Now we have got to the information we need. So how can we use this. Up to now we have just allowed the job to run and then picked the data from the job. One option is to write the data to the prompt as shown in this example http://blogs.msdn.com/powershell/archive/2009/08/30/exploring-wmi-with-powershell-v2.aspx. A lot of this digging was because I didn’t understand how this was put together. PowerShell really is the best way to discover how to use PowerShell!!
This gets us to this script which is modified from the PowerShell Team blog
| 001 002 003 004 005 006 007 008 009 010 011
| ## query $q = "Select * from Win32_ProcessStartTrace" ## action script block $a = { $eSEANE = $Event.SourceEventArgs.NewEvent $str = 'Computer {0},ID {1}, Name "{2}", Time {3}, Source {4}' $data = $str -f $Event.Sender.Scope.Path.Server, $eSEANE.ProcessId, ` $eSEANE.ProcessName, $Event.TimeGenerated, $Event.SourceIdentifier Write-Host $data } Register-WmiEvent -Query $q -SourceIdentifier "Process Start" -Action $a |
Turns out the ComputerName parameter doesn’t work but a comment on the blog shows how Jeffrey Hicks solved the problem.
What we get now is a listing at our PowerShell prompt when a new process starts. We can keep working and the data comes through when the prompt is idle.
Next we will look at closing a process and recording the data in a log