Pipeline input by property name


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

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. :slight_smile:

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

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.


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

PSCore was changed from computer name to hostname. Only the PS Team will know why (though it does not matter, it’s what we’re stuck with now), but this was also talked about in the ‘BRK3069 - What’s new in PowerShell’ breakout session at MSIgnite 2018. I know this because I was in that session. Specifically shown at this time marker:

Somewhere, I heard that “hostname” is more unversal, than “computername”. That change is for multiplatform use of PSCore :slight_smile:

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 basic problem mixing byvalue and bypropertyname parameters in pipes · Issue #7159 · PowerShell/PowerShell · GitHub

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