As always, I’m running through an exercise on one of Don Jones’ books (Learn PowerShell Toolmaking In A Month Of Lunches, in this case). Below is the command that I’m running as part of the exercise.
Trace-Command -Name ParameterBinding -PSHost -Expression {Import-Csv C:\Data\computers.txt | Invoke-Command -ScriptBlock {Get-Service}}
To keep it simple, I have just the following in my computers.txt file.
ComputerName localhost
From the debug output of the Trace-Command cmdlet, I see that the Import-Csv objects get piped to the Invoke-Command cmdlet as PSCustomObject. Invoke-Command has the -InputObject parameter which will take PSObject objects ByValue. The details I’ve read so far on that parameter aren’t really clear, but I see that the PSCustomObject objects then get passed to the Get-Service cmdlet inside the script block. In the end, I see that the objects get bound to the -Name parameter for the Get-Service cmdlet. Here is the part I am confused on.
After attempting to bind to the -Name parameter ByValue…
DEBUG: ParameterBinding Information: 0 : Parameter [Name] PIPELINE INPUT ValueFromPipeline NO COERCION DEBUG: ParameterBinding Information: 0 : BIND arg [@{ComputerName=notonline}] to parameter [Name] DEBUG: ParameterBinding Information: 0 : Binding collection parameter Name: argument type [PSObject], parameter type [System.String[]], collection type Array, element type [S ystem.String], no coerceElementType DEBUG: ParameterBinding Information: 0 : Creating array with element type [System.String] and 1 elements DEBUG: ParameterBinding Information: 0 : Argument type PSObject is not IList, treating this as scalar DEBUG: ParameterBinding Information: 0 : BIND arg [@{ComputerName=notonline}] to param [Name] SKIPPED
…which naturally would fail, I see a successful binding to the -ComputerName parameter.
DEBUG: ParameterBinding Information: 0 : Parameter [ComputerName] PIPELINE INPUT ValueFromPipelineByPropertyName NO COERCION DEBUG: ParameterBinding Information: 0 : BIND arg [notonline] to parameter [ComputerName] DEBUG: ParameterBinding Information: 0 : Binding collection parameter ComputerName: argument type [String], parameter type [System.String[]], collection type Array, element t ype [System.String], no coerceElementType DEBUG: ParameterBinding Information: 0 : Creating array with element type [System.String] and 1 elements DEBUG: ParameterBinding Information: 0 : Argument type String is not IList, treating this as scalar DEBUG: ParameterBinding Information: 0 : Adding scalar element of type String to array position 0 DEBUG: ParameterBinding Information: 0 : Executing VALIDATION metadata: [System.Management.Automation.ValidateNotNullOrEmptyAttribute] DEBUG: ParameterBinding Information: 0 : BIND arg [System.String[]] to param [ComputerName] SUCCESSFUL
Despite the successful binding to the -ComputerName parameter, PowerShell then successfully attempts to bind to the -Name parameter.
DEBUG: ParameterBinding Information: 0 : BIND arg [@{ComputerName=notonline}] to parameter [Name] DEBUG: ParameterBinding Information: 0 : COERCE arg to [System.String[]] DEBUG: ParameterBinding Information: 0 : Trying to convert argument value from System.Management.Automation.PSObject to System.String[] DEBUG: ParameterBinding Information: 0 : ENCODING arg into collection DEBUG: ParameterBinding Information: 0 : Binding collection parameter Name: argument type [PSObject], parameter type [System.String[]], collection type Array, element typ e [System.String], coerceElementType DEBUG: ParameterBinding Information: 0 : Creating array with element type [System.String] and 1 elements DEBUG: ParameterBinding Information: 0 : Argument type PSObject is not IList, treating this as scalar DEBUG: ParameterBinding Information: 0 : COERCE arg to [System.String] DEBUG: ParameterBinding Information: 0 : Trying to convert argument value from System.Management.Automation.PSObject to System.String DEBUG: ParameterBinding Information: 0 : CONVERT arg type to param type using LanguagePrimitives.ConvertTo DEBUG: ParameterBinding Information: 0 : CONVERT SUCCESSFUL using LanguagePrimitives.ConvertTo: [@{ComputerName=notonline}] DEBUG: ParameterBinding Information: 0 : Adding scalar element of type String to array position 0 DEBUG: ParameterBinding Information: 0 : BIND arg [System.String[]] to param [Name] SUCCESSFUL
After this occurs, the mandatory validation, and some other stuff, occurs and the error occurs saying:
Get-Service : Cannot find any service with service name '@{ComputerName=notonline}'. At line:1 char:123 + ... sv C:\Data\computers.txt | Invoke-Command -ScriptBlock {Get-Service}} + ~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (@{ComputerName=notonline}:String) [Get-Service], ServiceCommandException + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
So, here is my question. Why after the successful binding to the -ComputerName parameter, of the Get-Service cmdlet, do we then ultimately bind to the -Name parameter? Both the -ComputerName & -Name parameters take string objects. Is it because -Name is a positional parameter (position 0) and because we didn’t specify any parameters for Get-Service, in the script block? If so, then why did PowerShell first bind to the -ComputerName parameter?
Also, if anyone can explain more about how the -InputObject parameter of the Invoke-Command cmdlet works in this scenario, that would be nice. Thanks.