Windows PowerShell: The Many Ways to a Custom Object

It doesn’t really matter which approach you choose for outputting custom objects, as long as a custom object is your end result.

Don Jones

Recently, I wrote a blog post that outlines a set of 12 best practices for using Windows PowerShell. The No. 12 best practice directs folks to output objects from their scripts and functions, instead of outputting text. I went so far as to suggest that if you were ever using the Write-Host cmdlet, you should step back and think about what you were doing. All Write-Host does is produce text.

Not long after that post went live, a reader contacted me about the code I’d used to actually create the custom object. He said he had never seen that approach before. That was hardly surprising, as Windows PowerShell seems to offer a dozen ways to do anything. He suggested I write an article about the different ways to produce a custom object, so here we are.

The Full-Form Way

This is the way most people would probably choose to create a custom object. It’s what I call the “textbook approach.” It has the advantage of being pretty clear, although it involves a lot of typing. Assuming I have an object in the variable $os, and another in $bios, I could combine selected pieces of information from them like this:

$object = New-Object –TypeNamePSObject
$object | Add-Member –MemberTypeNoteProperty –Name OSBuild –Value $os.BuildNumber
$object | Add-Member –MemberTypeNoteProperty –Name OSVersion –Value $os.Version
$object | Add-Member –MemberTypeNoteProperty –Name BIOSSerial –Value $bios.SerialNumber
Write-Output $object

You can continue that pattern to add whatever other information you need to the final output object, prior to writing it to the pipeline.

-PassThru: A Brief Shortcut

You can make that first approach a bit more concise by telling Add-Member to put the object back into the pipeline:

$object = New-Object –TypeNamePSObject
$object | Add-Member –MemberTypeNoteProperty –Name OSBuild –Value $os.BuildNumber –PassThru |
Add-Member –MemberTypeNoteProperty –Name OSVersion –Value $os.Version –PassThru |
Add-Member –MemberTypeNoteProperty –Name BIOSSerial –Value $bios.SerialNumber
Write-Output $object

When you end a line with a pipe character, Windows PowerShell knows to go to the next physical line to look for the next command in the pipeline. Essentially, it’s a way of breaking up a long command into multiple physical lines. That trick, combined with the –PassThru switch, makes this a series of three distinct commands.

Hashtable a Go-Go

While the previous approaches are effective, they’re also very wordy. In a script, it can actually be hard for you to visually determine what’s happening. Using the New-Object feature is a more concise approach. This lets you create a hashtable (or associative array) that contains the property names and values you want to add to the newly created object. These properties are each automatically created as a NoteProperty:

$properties = @{'OSBuild'=$os.BuildNumber;
                'OSVersion'=$os.version;
                'BIOSSerial'=$bios.SerialNumber}
$object = New-Object –TypeNamePSObject –Prop $properties
Write-Output $object

This has the same effect, but is much more concise. Some clever folks will use a parenthetical expression to make it even shorter. However, I think this makes it a bit more difficult to read:

$object = New-Object –TypeNamePSObject –Prop
(@{'OSBuild'=$os.BuildNumber;
                'OSVersion'=$os.version;
                'BIOSSerial'=$bios.SerialNumber})
Write-Output $object

Going a Bit Further

You’ll notice that in all these examples, I save the custom object to a variable ($object) before writing it to the pipeline. The reason for this is simple. You might want to manipulate the object a bit more. For example, you might want to give your object a custom type name:

$object.PSObject.TypeNames.Insert(0,'My.Custom.Name')

Doing this lets you create a custom default formatting layout for displaying your object. I used this trick to great effect in “Windows PowerShell Scripting and Toolmaking” (Concentrated Technology and Interface Technical Training, 2011), a short book I wrote that focuses on things such as using custom objects as output from custom-made Windows PowerShell tools.

Different Strokes for Different Folks

There’s no “wrong” way to do anything in Windows PowerShell, as long as your approach gets the job done. That said, there are certain approaches I avoid, mostly because they’re less readable and harder to teach—especially to newcomers. Here’s one, still assuming that my $os and $bios variables contain the raw objects I want to extract information from:

$os | Select-Object –Property @{n='OSVersion';e={$_.Version}},
@{n='OSBuild';e={$_.BuildNumber}},
@{n='BIOSSerial';e={$bios.SerialNumber}}

You’ll generate the same results as the previous examples, but the syntax is appalling. There’s lots of punctuation, lots of structure, and you have to understand a lot about what Select-Object is doing with those three hashtables.

It’s basically generating custom properties using a syntax unique to that cmdlet (and shared by the Format cmdlets). You’ll see that syntax used by folks with a software development-style background. It’s harder for me (and many other folks) to mentally parse, so I tend to not use it as much.

PowerShell Is Nothing If Not Flexible

Windows PowerShell is unique in that it lets you get away with some crazy stuff. For example, go back to my hashtable example. You could also do it this way:

$info = @{}
$info.OSBuild=$os.BuildNumber
$info.OSVersion=$os.version
$info.BIOSSerial=$bios.SerialNumber
$object = New-Object –TypeNamePSObject –Prop $info
Write-Output $object

You’re creating an empty hashtable, then adding information by referring to properties that don’t exist. When you first try to access OSBuild, for example, Windows PowerShell realizes that no such property exists in the $info object (which was an empty hashtable). It implicitly creates the property and assigns your value. Crazy stuff—but it works.

The Moral Stands: Objects, Not Text

However you choose to create your custom objects, you can create custom objects instead of outputting mere text to the console window. Objects are infinitely more flexible, and they let your script or function output integrate with everything else Windows PowerShell.

Don Jones

Don Jones is a Microsoft MVP Award recipient and author of “Learn Windows PowerShell in a Month of Lunches” (Manning Publications, 2011), a book designed to help any administrator become effective with Windows PowerShell. Jones also offers public and on-site Windows PowerShell training. Contact him through his Web site at ConcentratedTech.com.