Pipeline input by property name

Welcome Forums General PowerShell Q&A Pipeline input by property name

This topic contains 6 replies, has 5 voices, and was last updated by

js
 
Participant
2 weeks, 3 days ago.

  • Author
    Posts
  • #113081

    Participant
    Points: 0
    Rank: Member

    Hi,

    I've been using PowerShell for around 4 years but this week for the first time I'm attending some classroom training and I came across some behaviour that we couldn't explain completely.
    The exercise was based around creating a calculated property in order for it to be accepted through the pipeline as input for the next cmdlet.

    This is the example given, and it works but in my understanding it should not

    Get-AdComputer -filter * | select @{n='ComputerName'; e={$_.name}} | Get-Service -name *

    I'm basing my assumption that it should not work on this line

    Get-Adcomputer -filter * | select @{n='ComputerName'; e={$_.name}} | Get-Service

    I understand that the property name needs changed to match the accepted parameter on Get-Service, hence the calculated property. Also that it effectively adds "-Computername [value]" invisibly when it runs Get-Service, for each iteration. However, in the second line of code the object type that comes over the pipe is an ADComputer object and so will not be accepted by Get-Service. I verified this using Get-Member.

    The first line of code, given in the course material works fine. It's like in the first example the ADComputer object is being expanded out to the strings it contains and in the second, it is not. The only difference is the "-name *" which refers to the service name and as I understand it should have no effect.

    Normally I would ignore this and find another way to write the code but this is purely an academic question because I'm looking for a deeper understanding. Have I understood this incorrectly? Is it a bug? Something to do with positional parameters? Ghosts?

    Thanks in advance

  • #113093

    Participant
    Points: 1
    Rank: Member

    Get-Service is a bit bugged in that respect. It should work like that (just piping an object into the cmdlet with the right property names) but I believe (not sure whether it's a bug in the pipeline logic or the cmdlet itself) it also inserts the input value as the -Name parameter of Get-Service despite you specifying it as ComputerName by the property name.

    I'll have to remember to file an issue in the PS Core repo; pretty sure that issue is still around.

    In the meantime, there's something else you can do with ByPropertyName-enabled parameters:

    Get-Adcomputer -filter * | Get-Service -ComputerName {$_.Name}

    Note that here I'm passing a script block to the parameter. This essentially forces the pipeline logic to bind the requested value in that place. Within that script block, $_ refers to the input objects as they come over the pipeline, and you can access any of their properties to pass to the parameter.

    This can be done for any number of parameters, each taking a separate script block, but only if they're declared as supporting input by property name. 🙂

    However, if the bug is in the cmdlet itself... it's possible that it'll exhibit the same behaviour.

    • #113096

      Participant
      Points: 0
      Rank: Member

      Awesome, thanks for the reply. Glad to know I did understand what should be happening.

      I'll have a play with passing script blocks in the lab tomorrow as that's much nicer syntax.

       

  • #113111

    Participant
    Points: 1
    Rank: Member

    So, interestingly enough, PS Core actually removed the -ComputerName parameter from Get-Service; not sure why. Perhaps related... and there aren't any other ByPropertyName parameters on that cmdlet. I would generally assume that only -Name will work correctly with pipeline input (it does seem to be what the help documentation states, with the exception of -ComputerName).

    So for whatever reason, the declared attributes of that parameter don't seem to match the established code paths.

    A relatively clean workaround looks like this:

    Get-ADComputer -Filter * | ForEach-Object {
        Get-Service -ComputerName $_.Name
    }
  • #113137

    Participant
    Points: 0
    Rank: Member

    Somewhere, I heard that "hostname" is more unversal, than "computername". That change is for multiplatform use of PSCore 🙂

  • #113156
    js

    Participant
    Points: 6
    Rank: Member

    This is a known issue with ALL powershell cmdlets. If a parameter that can be sent over the pipe "byvalue" isn't specified by argument (like "name"), other parameters sent "bypropertyname" over the pipe get messed up.

    basic problem mixing byvalue and bypropertyname parameters in pipes #7159 https://github.com/PowerShell/PowerShell/issues/7159

    It helps to include the error messages.

    [pscustomobject]@{computername='comp1'} | get-service
    get-service : Cannot find any service with service name '@{computername=comp1}'.
    At line:1 char:43
    + [pscustomobject]@{computername='comp1'} | get-service
    +                                           ~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (@{computername=comp1}:String) [Get-Service], ServiceCommandException
        + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
    

You must be logged in to reply to this topic.