Home > Enterprise >  Get-Process -computername pipeline input works only with the first name
Get-Process -computername pipeline input works only with the first name

Time:05-19

Get-Process accepts -computername parameter by pipeline (ByPropertyName). The file I import looks like:

computername
sql01
sql02
sql03

When I pipe it, I'll get the processes only from the first computer in the list (sql01)?

$a = Import-Csv C:\temp\computers.txt
$a | get-process -name dwm # output only from the first computer in the list
get-process dwm -ComputerName $a.computername # here correct output from 3 computers

CodePudding user response:

It looks like you've hit a bug in Windows PowerShell - see bottom section for details.

  • The bug is unlikely to get fixed, given that Windows PowerShell will only see critical fixes going forward.

  • You are already aware of the workaround: pass the array of computer names as an argument to -ComputerName instead of via the pipeline.

    # Windows PowerShell only.
    Get-Process dwm -ComputerName $a.computername
    
  • From a broader perspective, consider switching to using PowerShell's remoting, where only the general-purpose Invoke-Command cmdlet facilitates execution of arbitrary commands remotely, using a modern, firewall-friendly transport. Similarly, the CIM cmdlets (e.g. Get-CimInstance), which use the same transport, should be preferred over the obsolete WMI cmdlets they supersede (e.g., Get-WmiObject).

    # Works in both PowerShell editions, assuming the target computers
    # are set up for PowerShell remoting.
    # Note: Invoke-Command does NOT support passing computer names via the pipeline.
    Invoke-Command -ComputerName $a.computername { Get-Process dwm -ComputerName }
    
    • Note that the -ComputerName parameters on purpose-specific cmdlets such as Get-Process and Restart-Computer aren't available in PowerShell (Core) 7 anymore, and neither are the WMI cmdlets, because they are based on .NET Remoting, a form of remoting unrelated to PowerShell that has been declared obsolete and is therefore not part of .NET Core / .NET 5 . By definition, the bug at hand therefore doesn't affect PowerShell (Core).

Bug details:

The bug is specific to the combination of:

  • providing objects that have a .ComputerName property via the pipeline in order to bind the property values to the -ComputerName parameter

  • targeting processes by name(s), via the (possibly positionally implied) -Name parameter, or targeting all processes (by neither passing a -Name nor an -Id argument)

In other words: targeting processes by PID (process ID), via the -Id parameter is not affected - but using -Id remotely is only useful if you've previously obtained a PID from a given computer and if you're targeting only one computer.

In Windows PowerShell, Get-Process's -ComputerName parameter is designed to bind objects supplied via the pipeline that have a .ComputerName property to the -ComputerName parameter:

WinPS> Get-Help Get-Process -Parameter ComputerName

-ComputerName <System.String[]>
    # ...
    Accept pipeline input?       True (ByPropertyName)
    # ...

However, as you've observed, only the first computer name bound this way is honored by Get-Process in combination with -Name - any remaining ones are quietly ignored:

# Target the local machine (.) and a NON-EXISTENT machine ('NOSUCH')
WinPS> [pscustomobject] @{ ComputerName='.' }, 
       [pscustomobject] @{ ComputerName='NOSUCH' } | 
         Get-Process -Name powershell

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    970      48   170860      29120      56.41   4656   1 powershell

That is, the local machine's PowerShell process(es) are reported, and the non-existent computer name was quietly ignored. Note: Any subsequent computer names are ignored, whether they refer to existing computers or not. Using a non-existent one should conspicuously surface as an error. If you replace -Name powershell with -Id $PID, you will indeed see the error.

The problem is not one of parameter binding, as output from a Trace-Command call shows:

WinPS> Trace-Command -pshost -name ParameterBinding { 
         [pscustomobject] @{ ComputerName='.' }, 
         [pscustomobject] @{ ComputerName='NOSUCH' } | Get-Process -Name powershell 
       }

DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Get-Process]
# ...
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Get-Process]
# ...
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Get-Process]
DEBUG: ParameterBinding Information: 0 :     PIPELINE object TYPE = [System.Management.Automation.PSCustomObject]
# ...
DEBUG: ParameterBinding Information: 0 :     Parameter [ComputerName] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION
DEBUG: ParameterBinding Information: 0 :     BIND arg [.] to parameter [ComputerName]
# ...
DEBUG: ParameterBinding Information: 0 :         BIND arg [System.String[]] to param [ComputerName] SUCCESSFUL
# ...
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to parameters: [Get-Process]
DEBUG: ParameterBinding Information: 0 :     PIPELINE object TYPE = [System.Management.Automation.PSCustomObject]
DEBUG: ParameterBinding Information: 0 :     BIND arg [NOSUCH] to parameter [ComputerName]
# ...
DEBUG: ParameterBinding Information: 0 :         BIND arg [System.String[]] to param [ComputerName] SUCCESSFUL
# ...
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing

BIND arg [.] and BIND arg [NOSUCH], followed by a SUCCESSFUL status, indicate that both computer names were properly bound.

In other words: the bug must be in the code that subsequently uses the correctly bound parameters.

  • Related