June 26, 2017

My script to inspect PowerShell objects

About two years ago a friend from work asked me to help him with finding an object’s properties path that holds a specific value he knew existed there somewhere.

To clarify what I’m calling a properties path is this:

$obj.FirstLevelProperty.SecondLevelProperty.ThirdLevelProperty…

etc.

The first idea that came to my mind was to use Show-Object cmdlet by Lee Holmes. In short Show-Object opens a window with the object’s properties tree that you can inspect (you can read more on how to use this cmdlet on The Scripting Guy’s blog post).
My friend didn’t like the idea that of inspecting the object property by property, branch by branch. He wanted a fast way, a function if possible, to search for a value and get the properties path so he could copy-paste into his scripts.

I accepted his request as a challenge and started writing.
My initial idea was to inspect the object using Get-Member to get the names of all the object’s properties, print their names and dynamically call them, like this:

$InputObject = Get-Process winlogon
Get-Member -InputObject $InputObject |
    Where-Object {$_.MemberType -eq 'Property'} |
    Select-Object -ExpandProperty Name | Foreach-Object {
    "[ORIGIN].$_ : $($InputObject.$_)"
}

But this is only to get the first level properties, if I want to get deeper it will be easy to write a recursion:

function Get-ObjectProperty {
    param($InputObject, $Path="[ORIGIN]")

    Get-Member -InputObject $InputObject |
        Where-Object {$_.MemberType -eq 'Property'} |
        Select-Object -ExpandProperty Name | Foreach-Object {
            "[ORIGIN].$_ : $($InputObject.$_)"
            Get-ObjectProperty -InputObject $InputObject.$_ -Path "$Path.$_"
    }
}

As you can see above, each line of the output start with [ORIGIN], which is a representation of the $InputObject parameter, joined by dot (‘.’) with the properties path, a colon and the corresponding value.
With that its easy to look for a specific value using Select-String:

PS C:\> Get-ObjectProperty (Get-NetAdapter)[0] | Select-String ': .*MSFT'
[ORIGIN].CreationClassName: MSFT_NetAdapter
[ORIGIN].CimClass: ROOT/StandardCimv2:MSFT_NetAdapter
[ORIGIN].CimClass.CimClassName: MSFT_NetAdapter
[ORIGIN].CimClass.CimSystemProperties.ClassName: MSFT_NetAdapter
[ORIGIN].CimSystemProperties.ClassName: MSFT_NetAdapter

But there might be a problem with the function as written above. There are objects that have one of their properties points to another object, and its properties points to other objects and so on…
And somewhere along the way there will be a pointer back to the first object. In that case calling the function will cause it to run in an infinite loop.

To solve that I added 2 more parameters, $Depth and $Table:

  • $Depth sets a limit to the amount of recursion calls in a branch.
  • $Table is an array that holds all the objects hash codes that were processed along the way. If an object has the same hash as another object that was processed before, it will be skipped.

Note that this is not the perfect solution as different objects can have the same hash code. As explained on MSDN, it depends on the implementation of the GetHashCode() function.

Yesterday I’ve uploaded the script function Get-ObjectProperty to my Github repository, but not before I made some improvements:

  • Replaced the implementation of ($obj | Get-Member) with ($obj.psobject.Properties). It produces the same results but I feel that it’s more fluent to write. Each property in psobject.Properties has value property, so instead of extracting all the property names and calling $obj.$propertyName I’m just calling $obj.psobject.Properties.foreach({$_.Value}).
  • Added the ability to filter the leaf properties by regular expression.

Until next time,
have a great week,
sincerely,
Amir.


psobject inspect reflection


Previous post
First Post So this is my first blog post! Finally! I was struggling with writing this post for a while now. I opened this blog over 6 months ago… My name is
Next post
How to manage concurrent jobs in PowerShell There had been many times in the past two months where I had to write scripts that run multiple jobs concurrently. Since PowerShell doesn’t have a