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 asGet-Process
andRestart-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).
- Note that the
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
parametertargeting 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.