Skip to the main content.

ScriptRunner Blog

AD objects and more — How to search in properties of complex objects

Table of Contents 

Post Featured Image

While working with PowerShell, you probably have encountered objects with a lot of properties. It’s quite challenging to find values among them. The most common are objects such as WMI objects and Active Directory objects. How can we find all properties that contain a certain string or string pattern? How can we find the properties of a WMI object that contain the name of our server? Which AD attributes contain the last name of a user? Let’s put together a PowerShell function to help answering these questions! 

Enumerating the properties of objects 

One of the first commands that a beginner PowerShell scripter learns is Get-Member. This command shows us the members of objects, including property members. Even though our function can very well be built upon it, in this case let’s take a look at an alternative, the ‘psobject.properties’ hidden attributes of objects. The following expression lists the properties of the current PowerShell process object (the result is cropped for better readability): 

get-process id

Here, we see all properties with their names, types, and values. There are some further ‘properties of properties’, but we need to focus on these three. If we look at the types of the properties, we can notice that there is an ‘AliasProperty’ type. We don’t really need that kind of properties because they just store redundant information of other properties.

Let’s create an expression that lists the properties of our PowerShell process object that contain the string “Windows”:

string Windows

 

Create the search-property function

As we already have a rudimentary solution, let’s place that inside a function and assign parameters to that, namely  ‑object and ‑pattern. We may get our objects to search on through the pipeline so the function should accept pipeline input. We also need a ForEach loop, so that our function can cope with an array of objects as well:



function Search-Property {
param(
[parameter(ValueFromPipeline)][psobject[]] $object,
[string] $pattern = "."
)
process{
foreach($o in $object){
$o.psobject.properties |
Where-Object {
$_.membertype -ne 'AliasProperty' -and
$_.value -match $pattern
} | Select-Object -Property @{n = "Object"; e = {$o.tostring()}}, Name, Value
}
}
}

 

The results are extended with a new column containing the string representation of the input object, so that we can see which object a certain property belongs to. This is important in case we feed in multiple objects. If we run this, we get the desired results:

Search-Property object

 

Make the function more capable

With a little effort, we can make the function even better. First, we may want to exclude certain properties from the result set. For example, when searching for the occurrences of the ‘last name’ among the properties of an AD user account, we want only those that can be updated by the Set-ADUser cmdlet. We probably want to exclude the distinguishedName and CN, as we need another command, the Rename-ADObject to have them updated. For excluding certain properties, let’s introduce the ‑ExcludeProperty parameter that can accept an array of strings with even wildcards.

We may also want to restrict the scope of properties in which we want to search. For example, we want to search only within properties that contain the string “Name”. Let’s have the ‑Property parameter that can also accept an array of strings with wildcards to define the set of properties to search in. If we don’t use this parameter, the default is all properties.

We may also want to apply our search pattern to the names of the properties. For that we can use a SearchInPropertyNames switch parameter. Or we may want to search only among the property names, not in the property values, so let’s have an ‑ExcludeValues switch parameter as well.

If we don’t want to bother with regular expressions but rather use literal searches, we can use a ‑LiteralSearch switch parameter as well.

Here is the improved function:



function Search-Property {
param(
[parameter(ValueFromPipeline)][psobject[]] $object,
[string] $pattern = ".",
[switch] $SearchInPropertyNames,
[switch] $ExcludeValues,
[switch] $LiteralSearch,
[string[]] $Property = "*",
[string[]] $ExcludeProperty
)
begin{
if($LiteralSearch -and $pattern -ne "."){
$pattern = [regex]::Escape($pattern)
}
}
process{
foreach($o in $object){
$o.psobject.properties |
Where-Object {
$propname = $_.name
$_.membertype -ne 'AliasProperty' -and
(
$(if(!$ExcludeValues){$_.value -match $pattern}) -or
$(if($SearchInPropertyNames){$_.name -match $pattern})
) -and
!($ExcludeProperty | Where-Object {$propname -like $_}) -and
($Property | Where-Object {$propname -like $_})
} | Select-Object -Property @{n = "Object"; e = {$o.tostring()}}, Name, Value
}
}
}

The new function is just a couple of lines longer than the original one, the gist of it remained the same, so it was worth pondering over the possible, more complex scenarios in which we could use our function and add some new capabilities.

And here’s the new function in action. In the first example, we want to see those properties from the Win32_OperatingSystem and Win32_ComputerSystem WMI objects that contain either the string "DNS" or the name of our computer. In addition to that, we want to exclude those properties which start with 2 underscores, because they are just part of the WMI schema:

win operating system objects-1

In the next example, we return to the original problem. Let’s see those AD user attributes from the user account that contain the last name, but exclude the distinguishedName and other names that need the Rename-ADObject cmdlet to update them:

get-ADUser identity-1

 

What Next?

Of course, we could improve the function further by adding some help, examples, or explanation for the parameters. There could be a feature to search for those properties which contain values of other properties dynamically. For example, search all properties of all user accounts that contain the last name of the actual user.

We hope you have learned something new - or at least got a new handy tool in your PowerShell toolkit. What are your ideas on how to improve this function?

 

About the author: