PowerShell Journeyman - PSObject

Learn about accessing some of the underlying power of PowerShell

Introduction

In traditional trades, the Journeyman (often called Jman) is someone who has experience and can get stuff done effectively. They will forsee problems and adequately protect assets (especially coworkers) to keep things running smoothly. My expectation is that before you can be a “PowerShell Journeyman”, you should have experience with many aspects that can help your projects get done quickly and effectively. This entry deals with taking advantage of the PSObject intrinsic property.

Intrinsic Properties

PowerShell has some property members that get added to all objects. These properties are documented in the About intrinsic members article on Microsoft Learn.

You really should know about at least the PSObject property in order to inspect objects in PowerShell.

When You Need to Inspect Objects

A user on reddit recently asked how to dynamically select column names for use with Out-GridView after having spent days trying to figure it out. As a sample of what he was seeing, objects with different property names didn’t show up when using Out-GridView or Select-Object.

To illustrate what the was, consider this array of objects with differing properties:

[PSCustomObject]@{a=1;b=2},[PSCustomObject]@{b=2;c=3}

if you view this on the console, you’ll only see the properties a and b by default. Even if you use Select-Object * as shown here:

column C hidden!

But if you view as a list, you can see the c property. What gives?!

C visible from format-list

The user wanted some way to see all of these columns at one without having to use list format.

Understanding PowerShell’s Helpful Mentality

PowerShell is trying to help you out. It really is. And most of the time, its very handy. When you send a list of objects down the pipeline to something like Select-Object or Out-GridView, PowerShell will inspect the first pipeline object that comes down and get all the property names from that object. It will assume that all the rest of the objects to come down the pipeline will also have the same property names.

You need to access the PSObject properties and pull them out manually so that you can feed those properties to Select-Object isntead.

Accessing PSObject Properties

Enter PSObject. It contains a property called Properties that has all the properties that are defined on that object. Every object in PowerShell has the intrinsic property PSObject. Even simple primitives and other value type objects like 1. Ex: (1).PSObject.

Note that accessing PSObject on an array of objects will access the ARRAY PSObject, not the PSObject of the first item in the array. Here’s how you can get all the property names from the first item in an array:

$array = [PSCustomObject]@{a=1;b=2},[PSCustomObject]@{b=2;c=3}
$array[0].PSObject.Properties.Name

In picture form!

Cultivating a List of All Property Names From an Array

To get a list of all the property names from all objects in an array, you just need to iterate through all objects in the array and then select only the unique property names. Then supply that list of properties to Select-Object to prevent the default/helper property selection behavior:

$columns = $Array |
ForEach-Object { $_.PSObject.Properties.Name } |
Select-Object -Unique

$Array | Select-Object $columns

With ALL property names included

Going Further

This approach relies on Select-Object -Unique, which is case sensitive. There was a bug filed just before COVID that called out how Select-Object -Unique being case sensitive is inconsistent and unexpected in PowerShell (like Sort-Object -Unique). In that issue, a workaround I used was improved on by mklement0 that allows you to maintain order of objects while also selecting unique values with case insensitivity.

Good news, that issue is now closed because the upcoming PowerShell Core 7.4 release provides a -CaseInsensitive switch that you can use with Select-Object -Unique -CaseInsensitive to achieve all of this easily. Hopefully it will reach GA status here soon :D

RANT: MS SHOULD BE OPEN TO MORE BREAKING CHANGES THAT BENEFIT THE COMMUNITY

Wrapping Up

You can tell that u/Sentenced4Life is a happy camper now that he can dynamically have the AppPool names show up in his Out-GridView table:

Happiness

If you want to say something about this blog post, come say hi in the comment thread.