Recently, I was playing with Intune devices using graph API and managed devices. I was able to search for things like the device name with no issues, but when it came to the unique codes, I started having beautiful dyslexia issues. As I struggled, I asked myself how I could search this whole PowerShell object all at once, every value and every index. The first thought was to go property by property with a switch. But I asked myself, How can I reuse this function later? Let’s Search PowerShell Objects together.

The Script

function Search-Object {
    param (
        [Parameter(Mandatory=$true)][pscustomobject]$Object,
        [Parameter(Mandatory=$true)][string]$SearchString,
        [switch]$Expand
    )
    $Results = $Object | ForEach-Object -Begin { $index = 0 } -Process {
        $_.PSObject.Properties | ForEach-Object {
            if ($_.Value -like "*$SearchString*") {
                [pscustomobject][ordered]@{
                    Index = $index
                    PropertyName = $_.Name
                    Value = $_.Value
                }
            }
        }
        $index++
    }
    if ($Expand) {
        $IndexesToPresent = ($results | group-object -Property index).name
        foreach ($itp in $IndexesToPresent) {
            $Object[$itp]
        }
    } else {
        $Results
    }
}

The Breakdown

The concept is that we feed the script the ps object we want to search in. This function will search the top level. Searching recursively is dangerous for weaker machines, but it’s fully doable with the above function. It’s time to break down this function.

Parameters

First, we want to look at the parameters. We need the Powershell object, then the string, and finally a nice little flag to give us more information. We are requiring the object and the search are both required because that’s what we need. The expand isn’t because we may not need everything.

The Search PowerShell Object Loop

This step is the meat and potato of the function. Here is how we are going to search the full object. We start off by looping the object. We want to use a foreach-object loop because of the beginning and simplicity of it all.

The foreach-object is treated like a function in some context. We have a beginning, process and ending flags. So we begin the loop with our index of zero. We do this to keep track of the index of the powershell object we are analizing. The process is where we begin.

$Results = $Object | ForEach-Object -Begin { $index = 0 } -Process {}

The next step is to open the door of the PSobject. Once we crack the door we select the Properties information and start another loop through that.

$_.PSObject.Properties | ForEach-Object {}

This is where we can now search each object inside the object. At this point we are looking at a properity of an object that has been opened that is inside the main object. Confused yet, that’s ok. We are at the end of that trail. Next, we need to ask if the value of the current property is our search string. If it’s true, then we send back a ps object.

if ($_.Value -like "*$SearchString*") {
    [pscustomobject][ordered]@{
        Index = $index
        PropertyName = $_.Name
        Value = $_.Value
    }
}

Objects making objects, we are so in the .net work for sure.

What’s cool at this is the ps object is being returned to the Results. Finally, we do an index increase by using the index++.

$index++

Expanding

Now we have searched the powershell object, lets expand it. The code above grabs the index number. So we want to expand upon that. If you trigger the flag “Expand” we want to grab all the index number. First we take the results from the above and group them by group-object. We select the property of index. This will give us all of the options in a nice pretty package. So we then select the names. This will give us all the indexes for each item that was found and not multiple of the same. I

From there we start a for each loop. Here we display the objects index. We do this by having the main object with [#]. If expanding isn’t set, we just display the results.

if ($Expand) {
        $IndexesToPresent = ($results | group-object -Property index).name
        foreach ($itp in $IndexesToPresent) {
            $Object[$itp]
        }
    } else {
        $Results
    }

Multiople levels

Since this function does one level, you can use another powershell script that will search each level if it has an object. The only problem with this is, processing power. Powershell is not designed to be database software. So it’s best not to deep dive into an object like that.

What can we learn as a person?

It’s ok to find other solutions. Doing it the hard way only adds stress. But there is a point where you can spend more time automating than doing the work itself. So keep it balanced. You deserve that. Just like we search PowerShell objects, we should search for different ways to keep the balance in our life.

Additional Reading