Understanding Trace-Command Output

This topic contains 5 replies, has 2 voices, and was last updated by  Kevyn 2 weeks, 3 days ago.

  • Author
    Posts
  • #80713

    Kevyn
    Participant

    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.

  • #80752

    Kevyn
    Participant

    Where it says "notonline", that should say "localhost". I actually had both server names in my computers.txt file when I ran the code.

  • #80810

    Max Kozlov
    Participant

    Take a look to Get-Service syntax:

    PS C:\> Get-Help Get-Service -Full
    
    NAME
        Get-Service
    
    SYNOPSIS
        Gets the services on a local or remote computer.
    
    
    SYNTAX
        Get-Service [-ComputerName {String[]}] [-DependentServices] -DisplayName {String[]} [-Exclude {String[]}] [-Include {String[]}] [-RequiredServices] [{CommonParameters}]
    
        Get-Service [-ComputerName {String[]}] [-DependentServices] [-Exclude {String[]}] [-Include {String[]}] [-InputObject {ServiceController[]}] [-RequiredServices] [{CommonParameters}]
    
        Get-Service [[-Name] {String[]}] [-ComputerName {String[]}] [-DependentServices] [-Exclude {String[]}] [-Include {String[]}] [-RequiredServices] [{CommonParameters}]
    
    -InputObject 
        Specifies ServiceController objects representing the services to be retrieved. Enter a variable that contains the objects
        , or type a command or expression that gets the objects. You can also pipe a service object to this cmdlet.
    
        Required?                    false
        Position?                    named
        Default value                None
        Accept pipeline input?       True (ByValue)
        Accept wildcard characters?  false
    
    -Name 
        Specifies the service names of services to be retrieved. Wildcards are permitted. By default, this cmdlet gets all of the
         services on the computer.
    
        Required?                    false
        Position?                    0
        Default value                None
        Accept pipeline input?       True (ByPropertyName, ByValue)
        Accept wildcard characters?  false
    
    

    There are 3 variants of a set of parameters. You do not use any of the named parameters, therefore PS should determine the necessary set of parameters by yourself.
    Each of them has the parameter -ComputerName, so it can not be the determinant in which a set of parameters should be used.
    There are only two Pipeline / ByValue: -InputObject and -Name parameters
    -InputObject must be a ServiceController[] type, and you send a different type that can not be converted to a ServiceController
    -Name must be String[], and your type can be converted to a String, so it is used as a qualifier

    and (finally) may be just the DefaultParameterSet is a third set?

  • #80924

    Kevyn
    Participant

    Thanks, Max. I'm still curious though why we try to bind to the -Name parameter ByValue (we fail, as expected), then to the -ComputerName parameter (we succeed), before going back to the -Name parameter (we succeed).

  • #80965

    Max Kozlov
    Participant

    I think this is because -Computername -non-qualifier parameter and -Name – is qualifier for parameterset.
    and (may be) ByValue have precedence before ByPropertyName (when all other equal)

  • #80992

    Kevyn
    Participant

    I know we try ByValue first, for sure.

You must be logged in to reply to this topic.