Exploring WMI with PowerShell V2

Exploring WMI with PowerShell V2

  • Comments 5

I was messing around with my son’s computer today trying to figure out why one of his programs wouldn’t work.  I was going to use ProcMon to monitor the processes but wasn’t sure what the processes where.  So first I had to figure out what processes were getting started when I ran his program.  I guessed that there was a WMI event for this but as always with WMI – WHAT IS THE NAME OF THE EVENT?

In the past, this is where things stopped.  WMI was awesome but god help you if you didn’t know exactly what you wanted.  We made this a ton easier in PowerShell V2 so I decided to give it a try.  First thing I did was to LIST all the WMIOBJECTS that had the term PROCESS in the name.  I did that with this:

PS> Get-WMIObject -NameSpace root -Recurse -List  *Process*             

 

 

That gave me too many results to wade through so I needed to filter it down some more.  Every WMI object as a property called __Derivation which is a list of strings showing the class hierarchy for that object.  The point of that is that all WMI Event classes will contain the string “__Event” in this property.  So then I did this:

PS> Get-WMIObject -NameSpace root -Recurse -List  *Process* |Where {$_.Derivation -contains "__Event"}            

   NameSpace: ROOT\CIMV2

Name                                Methods              Properties                                          
----                                -------              ----------                                          
Win32_ProcessTrace                  {}                   {ParentProcessID, ProcessID, ProcessName, SECURITY...
Win32_ProcessStartTrace             {}                   {ParentProcessID, ProcessID, ProcessName, SECURITY...
Win32_ProcessStopTrace              {}                   {ExitStatus, ParentProcessID, ProcessID, ProcessNa...

   NameSpace: ROOT\WMI

Name                                Methods              Properties                                          
----                                -------              ----------                                          
ProcessorCStateEvent                {}                   {Active, InstanceName, SECURITY_DESCRIPTOR, TIME_C...
ProcessorPerfStateEvent             {}                   {Active, HighestState, InstanceName, SECURITY_DESC...
ProcessorThrottleStateEvent         {}                   {Active, HighestState, InstanceName, SECURITY_DESC...
PortCls_IrpProcessing               {}                   {SECURITY_DESCRIPTOR, TIME_CREATED}                 

 

 

 

 

That made it pretty easy to figure out that I wanted to look at Win32_ProcessStartTrace.  Just to make sure I got the class using the –Amended parameter.  This adds documentation to the returned object in the QUALIFIERS property.  This information is normally not provided because it can be expensive to retrieve so WMI only provides it if/when you ask for it.

$Class = Get-WmiObject -List Win32_ProcessStartTrace -Amended            
$Class.Qualifiers |ft Name,Value -auto            

This returned:

Name                                                                         Value
----                                                                         -----
abstract                                                                      True
Description The ProcessStartTrace event class indicates a new process has started.
Locale                                                                        1033

 

 

 

With that I was ready to go.  I used the new Register-WMIEvent class to solve the problem.  This takes a WMI query and can optionally take an ACTION.  I decided to format the event and write the results to the host whenever a process was started.  This looks a little chewy but is actually pretty straight forward.

$Query = 'SELECT * FROM Win32_ProcessStartTrace'            
$action = {            
    $e = $Event.SourceEventArgs.NewEvent            
    $fmt = 'ProcessStarted: (ID={0,5}, Parent={1,5}, Time={2,20}, Name="{3}")'            
    $msg = $fmt -f $e.ProcessId, $e.ParentProcessId, $event.TimeGenerated, $e.ProcessName            
    Write-host -ForegroundColor Red $msg            
}            
Register-WmiEvent -Query $Query -SourceIdentifier ProcessStart -Action $Action             

 

This then worked a treat.  Every time a process was created, I got output like this on my host:

ProcessStarted: (ID= 8740, Parent= 1140, Time=8/30/2009 3:41:54 PM, Name="Magnify.exe")
ProcessStarted: (ID=13748, Parent= 1068, Time=8/30/2009 3:41:54 PM, Name="Utilman.exe")
ProcessStarted: (ID=11964, Parent=13748, Time=8/30/2009 3:41:54 PM, Name="Magnify.exe")
ProcessStarted: (ID= 9872, Parent= 1068, Time=8/30/2009 3:41:58 PM, Name="Utilman.exe")


This worked a treat.  When I was done, I just unregistered from the event.

Unregister-Event -SourceIdentifier ProcessStart            

 

 

 

I love to tell the story from years ago about a customer that told me they had a love/hate relationship with WMI. 
When I asked them why they loved  it, they said, “Everything you could ever want to know is available in WMI”.
When I asked them why they hated it, they said, “We can’t FIND IT!”

 

I strongly encourage you to spend some time learning and using the new WMI cmdlets we put into PowerShell V2, they will pay you back over and over again.

 

Enjoy!

Jeffrey Snover [MSFT]
Distinguished Engineer
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Leave a Comment
  • Please add 4 and 8 and type the answer here:
  • Post
  • Great script. I wrapped this in a function and executed it against a remote system. Worked great. However, I'd like to run this against multiple systems and have the "ProcessStarted" line include the name of the computer that generated the event. So far, I have had trouble doing this. Does $Event or NewEvent ($e) include the name of the computer in any way, I could not find anything on TechNet and $e.__SERVER and $Event.__SERVER are not available.

    Is there a way to include the remote computername in the "ProcessStarted" output line?

  • Black

    Try using __server for the computer name. I have found that attribute to be the attribute that will give you the computer name whenever you need it.

  • Twometer,

    Unfortunately, __SERVER was not a property I could access. I posted over at ScriptingAnswers and JHicks was able to point me in the right direction. Inside the Action script code, I can access the $Event object which has $Event.Sender.Scope.Path.Server as a property.

    Worked great. Props to JHicks.

    http://www.scriptinganswers.com/forum2/forum_posts.asp?TID=3257&PID=18466#18466

  • Are there any reason you didn't make a ps-drive extension of WMI? It seems to be a good candidate. AD also seems like a good candidate to be a ps-drive.

  • JohannesH,

    AD is available as a ps-drive, but there are a number of steps.

    1. You have to install the Active Directory powershell module.  I got that by installing the "Remote Server Administration Tools for Windows 7" from download.microsoft.com

    2. In powershell run the command:

    import-module ActiveDirectory

    3. You must have a Windows Server 2008 R2 domain controller.  The AD powershell cmdlets require Active Directory Web Services.

    Does anyone know if "Active Directory Web Services" will be released to install on down level versions of Windows?

Page 1 of 1 (5 items)