Extending Get-Process to show the Process Chain – Proxy Function


One of the subjects that I am fascinated since the first time I saw (With the Master Jedi Dmitry Sotnikov and  his version of the Export-csv with -append in the Posh 2.0)  definetly are the Proxy Functions.

Generally speaking a proxy function in PowerShell allows you to write your own code and extend the funcionality from a built-in cmdlet with that code. This means that you can customize a new version of some built in cmdlet , by adding a new parameter, and write the code to perform the operation of that parameter. PRETTY COOL HÃ ? !!!

I am not going deeper in the process to create the body of the proxy function, because you can have awesome examples in the following links :

Proxy Functions: Spice Up Your PowerShell Core Cmdlets
Extending and/or Modifing Commands with Proxies
Customizing PowerShell, Proxy functions and a better Select-String

I wanto be clear that I am still learning the concept and this is my first Proxy Function. So if you are seeing some bug/error  in my code, please let me know and any advice is very welcome.

The History

Some time ago the PFE Fabricio Catae posted in his blog an brain teaser

Desafio: Comando KILL demorado (infinito)

In his blog he is asking why if I open a SSMS and type xp_cmdshell “notepad” , the  try to kill that session it will be hanging . Of course that the solution is just kill the process in the OS, but Fabricio goes beyond and explain the SQLOS and how KILL works internally.

In his example he used the Process Explorer to show the Process Chain, and in this specific case there is a SQLSRV Process and the child processes CMD and Notepad.

Lets reproduce the scenario : Open a SSMS and type :

image

If you try to kill this session, you will be hanging.

Then if you open the Process Explorer and find the sqlserv process you will see the complete chain :

image

Based  in this idea, I created the Proxy Function to show the Process chain. Ufortunately the Get-Process does not have the ParentID information, so I had to query the WMI. The code is :

function Get-ProcessChain {

 

       [cmdletbinding()]

    param(

       [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName = $true)] [System.Diagnostics.Process]$ProcessObject

    )

       Begin {

             $DefaultFColor = $Host.UI.RawUI.ForegroundColor

             $ArrayContains = @()

       }

       process { 

             If ($ArrayContains -notcontains  $ProcessObject.ID ) {

                    $ProcessObject

                    if ($ProcessObject.name -ne ‘services’) {

                           $ComputerName = $ProcessObject.MachineName

                           $Beforecaption = ‘-‘

                           $ProcessMotherID = $ProcessObject.ID

                           do {

                                  $ProcessChild = Get-WmiObject win32_process -Filter “ParentProcessId=’$($ProcessMotherID)'” -Property ProcessID,Name -ErrorAction SilentlyContinue -ComputerName $ComputerName

                                  if ($ProcessChild ) {

                                        $Beforecaption += ‘-‘

                                      $Host.UI.RawUI.ForegroundColor = “Yellow”

                                        $ProcessChild | % {

                                               Get-Process -Id $_.Processid -ComputerName $ComputerName -ErrorAction SilentlyContinue  |  Add-Member -MemberType NoteProperty  -name ProcessName -Value “$Beforecaption $($_.Name)” -Force -PassThru          

                                               $ProcessMotherID = $_.Processid

                                               $ArrayContains+= $_.Processid

                                        }

                                  } else {    

                                        $Beforecaption = ‘-‘

                                        Break

                                  }     

                           }  while($true)

                    }     

                    $Host.UI.RawUI.ForegroundColor = $DefaultFColor

             }     

       }

}

The next step is to get the metadata from Get-Process and save into Get-Process.txt :

 

$Cmd = Get-Command Get-Help

$CmdMetadata = New-Object System.Management.Automation.CommandMetaData $Cmd

[System.Management.Automation.ProxyCommand]::Create($CmdMetadata) | Out-File C:\teste\Get-Process.txt

The it is just open the txt, copy and paste to my editor , add the Swicth Parameter ShowChain and add the Get-ProcessChain in the net Get-Process :

Function Get-Process {

 

       [CmdletBinding(DefaultParameterSetName=‘Name’, HelpUri=http://go.microsoft.com/fwlink/?LinkID=113324’, RemotingCapability=‘SupportedByCommand’)]

       param(

           [Parameter(ParameterSetName=‘Name’, Position=0, ValueFromPipelineByPropertyName=$true)]

           [Alias(‘ProcessName’)]

           [ValidateNotNullOrEmpty()]

           [string[]]

           ${Name},

 

           [Parameter(ParameterSetName=‘Id’, Mandatory=$true, ValueFromPipelineByPropertyName=$true)]

           [Alias(‘PID’)]

           [int[]]

           ${Id},

 

           [Parameter(ValueFromPipelineByPropertyName=$true)]

           [Alias(‘Cn’)]

           [ValidateNotNullOrEmpty()]

           [string[]]

           ${ComputerName},

 

           [ValidateNotNull()]

           [switch]

           ${Module},

 

           [Alias(‘FV’,‘FVI’)]

           [ValidateNotNull()]

           [switch]

           ${FileVersionInfo},

 

           [Parameter(ParameterSetName=‘InputObject’, Mandatory=$true, ValueFromPipeline=$true)]

           [System.Diagnostics.Process[]]

           ${InputObject},

            

             [ValidateNotNull()]

           [switch]

           ${ShowChain})

 

       begin

       {

             function Get-ProcessChain {

 

                    [cmdletbinding()]

                 param(

                    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true,ValueFromPipelineByPropertyName = $true)] [System.Diagnostics.Process]$ProcessObject

                 )

                    Begin {

                           $DefaultFColor = $Host.UI.RawUI.ForegroundColor

                           $ArrayContains = @()

                    }

                    process { 

                           If ($ArrayContains -notcontains  $ProcessObject.ID ) {

                                  $ProcessObject

                                  if ($ProcessObject.name -ne ‘services’) {

                                        $ComputerName = $ProcessObject.MachineName

                                        $Beforecaption = ‘-‘

                                        $ProcessMotherID = $ProcessObject.ID

                                        do {

                                               $ProcessChild = Get-WmiObject win32_process -Filter “ParentProcessId=’$($ProcessMotherID)'” -Property ProcessID,Name -ErrorAction SilentlyContinue -ComputerName $ComputerName

                                               if ($ProcessChild ) {

                                                      $Beforecaption += ‘-‘

                                                   $Host.UI.RawUI.ForegroundColor = “Yellow”

                                                      $ProcessChild | % {

                                                            Get-Process -Id $_.Processid -ComputerName $ComputerName -ErrorAction SilentlyContinue  |  Add-Member -MemberType NoteProperty  -name ProcessName -Value “$Beforecaption $($_.Name)” -Force -PassThru            

                                                            $ProcessMotherID = $_.Processid

                                                            $ArrayContains+= $_.Processid

                                                      }

                                               } else {    

                                                      $Beforecaption = ‘-‘

                                                      Break

                                               }     

                                        }  while($true)

                                  }     

                                  $Host.UI.RawUI.ForegroundColor = $DefaultFColor

                           }     

                    }

             }

           try {

               $outBuffer = $null

               if ($PSBoundParameters.TryGetValue(‘OutBuffer’, [ref]$outBuffer))

               {

                   $PSBoundParameters[‘OutBuffer’] = 1

               }

               $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(‘Get-Process’, [System.Management.Automation.CommandTypes]::Cmdlet)

                    if ($PSBoundParameters[‘ShowChain’]) {

            [Void]$PSBoundParameters.Remove(“ShowChain”)

                     $scriptCmd = {& $wrappedCmd @PSBoundParameters | Get-ProcessChain}

                    } else {

                     $scriptCmd = {& $wrappedCmd @PSBoundParameters}

                    }     

               $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)

               $steppablePipeline.Begin($PSCmdlet)

           } catch {

               throw

           }

       }

 

       process

       {

           try {

               $steppablePipeline.Process($_)

           } catch {

               throw

           }

       }

 

       end

       {

           try {

               $steppablePipeline.End()

           } catch {

               throw

           }

       }

}     

<#

 

.ForwardHelpTargetName Get-Process

.ForwardHelpCategory Cmdlet

 

#>

Then I added in my profile file and now I can use the new Get-Process preserving the live object that is returning by the Get-Process. The ouptut is :

image

and in the Scpecific SQL Server Process :

image

PowerShell scares me all the time hehehe.

 

Thanks to the Master Jedi Tobias Weltner for help me (the PowerShell MVP list is AWESOME !!!!)

About Laerte Junior

Laerte Junior Laerte Junior is a SQL Server specialist and an active member of WW SQL Server and the Windows PowerShell community. He also is a huge Star Wars fan (yes, he has the Darth Vader´s Helmet with the voice changer). He has a passion for DC comics and living the simple life. "May The Force be with all of us"
This entry was posted in Algo que Esqueci de Categorizar. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s