This was an interesting puzzle and I’ll be curious to see if anyone else adds some insight. We look at the parameters:
PS C:\> Get-Help Get-Service -Parameter Name
-Name
Specifies the service names of services to be retrieved. Wildcards are permitted. By default, Get-Service gets all of the services on the computer.
Required? false
Position? 1
Default value All services
Accept pipeline input? true (ByPropertyName, ByValue)
Accept wildcard characters? true
PS C:\> Get-Help Get-Service -Parameter ComputerName
-ComputerName
Gets the services running on the specified computers. The default is the local computer.
Type the NetBIOS name, an IP address, or a fully qualified domain name of a remote computer. To specify the local computer, type the computer name, a dot (.), or "localhost".
This parameter does not rely on Windows PowerShell remoting. You can use the ComputerName parameter of Get-Service even if your computer is not configured to run remote commands.
Required? false
Position? named
Default value Local computer
Accept pipeline input? true (ByPropertyName)
Accept wildcard characters? false
Observations are Name is ByPropertyName or ByValue and ComputerName accepting ByPropertyName. Both parameters have Default values. The error message shows:
PS C:\> Get-ADComputer MyPuter| Select @{Label="ComputerName";Expression={$_.NAME}} | Get-Service
Get-Service : Cannot find any service with service name '@{ComputerName=MyPuter}'.
At line:1 char:86
+ ... n={$_.NAME}} | Get-Service
+ ~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (@{ComputerName=MyPuter}:String) [Get-Service], ServiceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand
I test this with Get-Process and it works fine. I built a function with I would assume is the same logic in Get-Service:
function Test-This{
param(
[Parameter(ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string[]]
$ComputerName = "Local Computer",
[Parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string[]]
$Name = "All Services"
)
begin{}
process{
foreach ($computer in $computername) {
"Processing {0}" -f $computer
foreach ($service in $name) {
"Processing {0} in {1}" -f $service, $computer
}
}
}
end{}
}
So, now we try the same logic and pass it to this function:
PS C:\> Get-ADComputer -Filter * | Select @{Label="ComputerName";Expression={$_.NAME}} -First 3 | Test-This
Processing MyServer1
Processing @{ComputerName=MyServer1} in MyServer1
Processing MyServer2
Processing @{ComputerName=MyServer2} in MyServer2
Processing MyServer3
Processing @{ComputerName=MyServer3} in MyServer3
It appears that because Name is stipulated as ByValue that since data is being piped to it that it overrides what is set as the default parameter and attempts to use the piped data. See how it’s searching for a service name of “@{ComputerName=MyServer1}” just like the error states above. If we add the Name=“*”, it works because now the pipe contains a value for Name and doesn’t use the data from the pipe line:
PS C:\> Get-ADComputer -Filter * | Select @{Label="ComputerName";Expression={$_.NAME}}, @{Label="Name";Expression={"*"}} -First 3 | Test-This
Processing MyServer1
Processing * in MyServer1
Processing MyServer2
Processing * in MyServer2
Processing MyServer3
Processing * in MyServer3
I think that explains the behavior, but I would like to know the proper way to actually fix it. What is different in Get-Process versus Get-Service? I tried putting this:
if (!($PSBoundParameters.ContainsKey('Name'))) {$Name="*"}
In the begin and process blocks but it still uses the piped data. Hopefully someone can tell us the proper ways to handle and override this behavior. I’ll research more later, but gotta get some work done.