On the PowerShell newsgroup I often (really often) see users new to PowerShell getting stuck transitioning from the point where they have a set of objects to creating an array of the values of one of the properties on those objects. For example, if you are using the free Quest AD cmdlets and you want to retrieve a list of group names to which a member belongs, you can do this:
(Get-QADUser “Poshoholic”).memberOf | Get-QADGroup | ForEach-Object {$_.Name}
This seems simple enough but many people get stuck going from a collection such as that returned by Get-QADGroup to an array of names such as that returned by the ForEach-Object cmdlet shown in this exanple. The missing piece of knowledge is usually understanding how to use ForEach-Object to extract the member you want. Also, even when you know how to use ForEach-Object, I find it a little inconvenient to use ForEach-Object with a script block just to pull out a property name, or to put brackets around part of a command so that I can access members on the result. And I find myself doing this quite regularly in my PowerShell work.
To both save myself from having to use ForEach-Object or brackets when all I want is to invoke a member on an individual object or a collection of objects, and to share this convenience with others, I have created an Invoke-Member function that makes the one-liner I listed above a little more elegant and PowerShelly. Here’s an updated version of the one-liner I used above, revised to use the Invoke-Member function:
Get-QADUser “Poshoholic” | Invoke-Member memberOf | Get-QADGroup | Invoke-Member name
This one-liner retrieves an AD user named Poshoholic and from that user it retrieves the memberOf member value. That value contains an array of DNs for groups to which the user belongs. For each of those DNs, the one-liner then retrieves the AD group object and once it has that it extracts the name of that group.
The Invoke-Member function can be used to invoke any type of member, including both properties and methods (e.g. Invoke-Member “Open()”). It can also be used to invoke nested members (e.g. Invoke-Member parameters.parameter). And it can take input from the pipeline or alternatively through the InputObject parameter. When using the InputObject parameter, the object is passed in as one object, even if it is a collection. When using the pipeline collections are passed into Invoke-Member one item at a time.
[Update] Since this was originally posted, Xaegr pointed out in the comments that the PowerShell team already had published a function on their blog that does this, and that he has modified that function to support multiple members. You can find that blog entry here, and Xaegr’s modifications can be found in the comments in this blog entry. The problem with those two implementations of the … function is that they don’t support calling methods and they don’t support nesting members, both of which are supported by the Invoke-Member function. I liked Xaegr’s idea to support retrieving multiple members at once though, which I think could be useful for simplified output of member values without member names attached to that output. I also think it would be useful when working with certain datasets that have different members with the same type that you want to do something with in one pass through that data set, or when you want to execute multiple methods in sequence during one pass through a dataset. I added support to the Invoke-Member function so that it would support multiple members as well.
Here is the updated source code for the Invoke-Member function as well as a script to create im and … aliases for the Invoke-Member function:
Function Invoke-Member {
param (
[string[]]$name = $(throw $($(Get-PSResourceString -BaseName ‘ParameterBinderStrings’ -ResourceId ‘ParameterArgumentValidationErrorNullNotAllowed’) -f $null,‘Name’)),
$inputObject = $null
)
BEGIN {
}
PROCESS {
if ($inputObject -and $_) {
throw $(Get-PSResourceString -BaseName ‘ParameterBinderStrings’ -ResourceId ‘AmbiguousParameterSet’)
break
} elseif ($inputObject) {
foreach ($member in $name) {
Invoke-Expression “`$inputObject.$member”
}
} elseif ($_) {
foreach ($member in $name) {
Invoke-Expression “`$_.$member”
}
} else {
throw $(Get-PSResourceString -BaseName ‘ParameterBinderStrings’ -ResourceId ‘InputObjectNotBound’)
}
}
END {
}
}
if (-not (Get-Alias -Name im -ErrorAction SilentlyContinue)) {
New-Alias -Name im -Value Invoke-Member -Description ‘Invokes a member of each of a set of input objects.’
}
if (-not (Get-Alias -Name … -ErrorAction SilentlyContinue)) {
New-Alias -Name … -Value Invoke-Member -Description ‘Invokes a member of each of a set of input objects.’
}
Note that this function uses the Get-PSResourceString function that I published recently in my Cmdlet Extension Library (you can read more about what this library contains here). If you want to use the Invoke-Member function by itself, you will have to change the calls to Get-PSResourceString with static strings that you want to throw on error instead. To simplify sharing the Invoke-Member function with the community I have added it to my Cmdlet Extension Library and uploaded the updated version to the PowerShell Community site.
Thanks to Joel Bennett for his article about writing functions that can also be executed in the pipeline. I didn’t end up using Joel’s template as is because I wanted Invoke-Member to follow the model of other cmdlets that have the InputObject parameter, but it was my starting point before I went through numerous revisions and ended up with the final version shown above in the Invoke-Member function code.
Comments/suggestions are welcome and appreciated, as always.
Kirk out.
Technorati Tags: PowerShell, PoSh, Poshoholic, Quest AD cmdlets, Invoke-Member, Cmdlet Extension Library