2

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
Purclot
  • 483
  • 7
  • 22

1 Answers1

2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    I don't think this is the first cmdlet that I see failing when bound by property name in Windows PS. Also, not WinPS but AD Module (`New-ADUser` has a similar issue). Thanks for answering – Santiago Squarzon May 18 '22 at 15:58
  • @mklement0: you're my absolute hero! Thanks a lot! I'm kind of proud of myself either: discovered 2 bugs (you remember the one with $error[]?), you solved two of them, nice job! ;-) P.S. I'm looking for a working example of a test unit for PowerShell (I need it for an unattended SQL-Server Installation, checking for many parameters, the must be true), can you advice where to look for it? Thanks for your help. – Purclot May 18 '22 at 19:31
  • I'm glad to hear it was helpful, @Purclot; my pleasure; indeed, nice job on discovering two obscure bugs. I'm not sure I understand your question, but in case you're looking for a test framework for PowerShell, use [Pester](https://www.powershellgallery.com/packages/Pester/). – mklement0 May 18 '22 at 20:01
  • 1
    the pleasure is mine! Yes, I need a test framework for PS and I'll give a try with Pester. – Purclot May 18 '22 at 20:09