Recently I helping out on a post in the forums at ScriptingAnswers.com. The question centered around identifying processes on a computer and their parent process. There are many ways you could slice and dice this problem using WMI and Get-WmiObject. Getting the parent process ID is pretty simple, but going backwards from there to identify the parent process takes a little PowerShell jujitsu.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Because I love objects (geeky, I know), one angle I pursued created a custom object for each parent process. The object included a few key (at least to me) properties as well as a property that held an array of all child processes.
Here’s an example.
ParentID : 916
Children : {@{ProcessID=4068; ProcessName=Moe.exe; Description=Moe.exe
; Created=5/19/2010 8:52:59 AM}, @{ProcessID=4716; ProcessN
ame=SkypeNames2.exe; Description=SkypeNames2.exe; Created=5
/20/2010 8:13:47 AM}, @{ProcessID=1600; ProcessName=VBoxSVC
.exe; Description=VBoxSVC.exe; Created=5/20/2010 11:59:31 A
M}, @{ProcessID=6828; ProcessName=WmiPrvSE.exe; Description
=WmiPrvSE.exe; Created=5/21/2010 7:57:22 AM}}
NumberofChildren : 4
ParentCreated : 5/19/2010 8:51:46 AM
ParentProcess : svchost.exe
Computername : SERENITY
To get here, I called a function I wrote called Get-ParentProcess.
function Get-ParentProcess { #requires -version 2.0 [cmdletBinding()] Param( [Parameter(Position=0,ValueFromPipeline=$True,
HelpMessage="Enter a computername")] [string[]]$computername=$env:computername) Begin { write-verbose "Starting $($myinvocation.mycommand)" #capture some performance metrics $start=Get-Date $total=0 } Process {foreach ($computer in $computername) { Write-Verbose "Testing $computer" if (Test-Connection -ComputerName $computer -quiet) { #only process if ping returns true Write-Verbose "Connecting to $computer" Get-WmiObject -Class win32_process -ComputerName $computer `-outvariable data | Sort-Object -Property ParentProcessID | Group-Object -Property ParentProcessID | foreach { $ParentID=$_.Name $ParentProcess=$data | where {$_.processID -eq $parentID} if (-not $parentProcess) { write-verbose "nothing found for $parentID" } Write-Verbose "Parent process $($parentProcess.name)" #convert creationdate to a friendly format if found if ($parentProcess.CreationDate) { $ParentCreation=`$ParentProcess.ConvertToDateTime($parentProcess.CreationDate) } else { $ParentCreation=$null } #use computername from process object if it exists, #otherwise use the value from $computer if ($parentProcess.CSName) { $ParentComputerName=$parentProcess.CSName } else { $ParentComputerName=$computer } $NumberChildren=$_.group.count Write-Verbose "Found $NumberChildren child process(es)" $Children=$_.group | Select -property ProcessID,ProcessName,` @{Name="Created"; Expression={$_.ConvertToDateTime($_.CreationDate)}} Write-Verbose "Creating custom object" New-Object PSObject -Property @{ Computername=$ParentComputerName ParentID=$ParentID ParentProcess=$ParentProcess.Name ParentCreated=$ParentCreation NumberofChildren=$NumberChildren Children=$Children } } #end foreach } #end if Test-Connection else { Write-Warning "Failed to connect to $computer" } $total+=$data.count } #end foreach $computer}#Process
End {$finish=Get-Date
$msg="Processed {0} processes from {1} computers in {2}" -f `$total,$computername.count,($finish-$start) write-verbose $msgwrite-verbose "Ending $($myinvocation.mycommand)" } } #end FunctionThis is an advanced function so it requires PowerShell 2.0. The actual function includes comment based help. I’ve omitted here.
The function takes a computername as a parameter, defaulting to the local computer. You can pipe names to the function as well. Here’s what happens when you connect to the remote computer using Get-WmiObject. The collection of Win32_Process objects is sorted by the ParentProcessID and then piped to Group-Object. This created a GroupInfo object. The name property is the parent process ID and the corresponding group are all the child processes.
Each group is then parsed and processed pulling out process information and defining new properties. To get the parent process name I search the saved WMI results looking for a process id that matches the parent process ID I’m checking. If you look back at the Get-WmiObject expression you’ll see I’m taking advantage of the –OutVariable common parameter. The cmdlet’s result is not only passed down the pipeline but also stored in the variable. In this case, $data. When you use –OutVariable just specify the variable name you want to use. You don’t need the $.
For the child processes that are in the group I select a few key properties. $Children will be a collection of custom process objects. This value is used as a property value, among others, in New-Object.
When you run the function, you most likely will notice some parent processes with no name. More than likely this is because the parent process is no longer running.
PS C:\> $r=get-parentprocess
PS C:\Scripts> $r | where {-not $_.parentProcess} | select-object -ExpandProperty Children | Sort Created | format-table –autosize
ProcessID ProcessName Created
--------- ----------- -------
596 csrss.exe 5/19/2010 8:51:43 AM676 wininit.exe 5/19/2010 8:51:44 AM
696 csrss.exe 5/19/2010 8:51:44 AM
852 winlogon.exe 5/19/2010 8:51:45 AM
3396 explorer.exe 5/19/2010 8:52:39 AM
3988 ToshibaServiceStation.exe 5/19/2010 8:53:07 AM
3564 TWebCamera.exe 5/19/2010 8:53:12 AM
3792 TUSBSleepChargeSrv.exe 5/19/2010 8:53:12 AM
2208 VCDDaemon.exe 5/19/2010 8:53:12 AM
4112 jusched.exe 5/19/2010 8:53:13 AM
4164 vmware-tray.exe 5/19/2010 8:53:14 AM
3656 SWin.exe 5/19/2010 9:03:57 AM
Even if you don’t have a use for the function, I hope you find some useful scripting techniques.
Update: I've been using the PowerShell ISE to create the script files on an x64 platform. The Unicode default can be problematic. Here is an ANSI version of the file. I'll try to remember to stick to ANSI from now on.
Jeff – Nice function. Good demo of how to build a full function.
The text file is in unicode format with no line feeds. Very hard to translates on non-64 bit platforms for some reason.
The file is created in the PowerShell ISE which I think is Unicode by default. Let me see if I can post an ASCII version.
I updated the original post with a second download link.,