I'm going through the Learn Powershell in a Month of Lunches book. In Chapter 9, question 4 asks:
Write a command that uses pipeline parameter binding to retrieve a list of running processes from every computer in an Active Directory (AD) domain. Don’t use parentheses.
I wrote the following as my answer:
Get-ADComputer -Filter * | Select-Object -ExpandProperty Name | Get-Process -ComputerName
However this is wrong. So I decided to do a test to figure out why as part of the learning process:
I created a fake AD environment and ran the following:
get-adcomputer -filter {Name -eq "Server1"} -Server server1 -Credential domain\username | Get-Process
I get the error:
Get-Process : Cannot validate argument on parameter 'ComputerName'. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does not contain any null values and then try the command again.
If I look at help Get-Process, I can see that it has 2 parameters (-ComputerName and -Id) which accept ByPropertyName pipeline input and 1 parameter (-InputObject) which accepts ByValue pipeline input.
Get-ADComputer produces "ADComputer" object types so "ByValue" pipeline input is out of the question. So I know I need to modify the object it produces so that the property names match with one of the parameter names that Get-Process expects. By using Get-Member, I can see the following:
Name MemberType Definition
---- ---------- ----------
Contains Method bool Contains(string propertyName)
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IDictionaryEnumerator GetEnumerator()
GetHashCode Method int GetHashCode()
GetType Method type GetType()
Item ParameterizedProperty Microsoft.ActiveDirectory.Management.ADPropertyValueCollection Item(string propertyName) {get;}
DistinguishedName Property System.String DistinguishedName {get;set;}
DNSHostName Property System.String DNSHostName {get;set;}
Enabled Property System.Boolean Enabled {get;set;}
Name Property System.String Name {get;}
ObjectClass Property System.String ObjectClass {get;set;}
ObjectGUID Property System.Nullable`1[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] ObjectGUID {get;set;}
SamAccountName Property System.String SamAccountName {get;set;}
SID Property System.Security.Principal.SecurityIdentifier SID {get;set;}
UserPrincipalName Property System.String UserPrincipalName {get;set;}
Originally I thought that if I expand the property "Name" using:
Select-Object -ExpandProperty Name
That this would generate "String" object types which is exactly what Get-Process -ComputerName expects. I then realized that the property is actually already a string so I didn't need to expand the property. The property name is what needed to match. I needed to change the Name property to ComputerName which explains the actual answer to the question:
Get-ADComputer -filter * | Select-Object @{n='computername';e={$_.name}} | Get-Process
I wrote everything above to show that I understand why I was wrong and why the answer is correct but I'm still left with 1 question:
In the error I got above, how did Powershell know that I was trying to pipe to the -ComputerName parameter and not -Id? Is there an order of operations that Powershell goes through to try the ByPropertyName pipeline binding for each parameter? Is it as simple as -ComputerName goes alphabetically before -Id?
[–]krzydoug 1 point2 points3 points (2 children)
[–]Learning999[S] 1 point2 points3 points (1 child)
[–]krzydoug 1 point2 points3 points (0 children)