Creating Custom Objects in PowerShell with the Original Object

As I often say, PowerShell is all about the objects. Getting your head around this object nature is one of the primary hurdlers for PowerShell newcomers. Cmdlets write objects to the pipeline, but you can also create your own custom objects either from a script or directly in the console.
This is a four-part series. In part one, I discussed the basics of creating custom objects in PowerShell to meet your needs; however, in this version, we lost the original object. Today I’ll show you how to modify and create the custom object while retaining the original.
Later, in part three we’ll learn how to create a custom object from scratch. Finally, in part four, I’ll go deeper and include the enhancements found in PowerShell 3.0.

Lost and Found : Original Object

In Part 1 of this series, I demonstrated how to use Select-Object to create a custom object in Windows PowerShell. One potential downside to that technique is that you lose the original object. Assuming Select-Object is the last part of your expression, you end up with a Selected object type as you see in Figure 1.
Fig1: Creating custom objects - Selected Object Type
Figure 1: A selected object type
Here’s what the actual result looks like:

Computername                   RequiredCount Name            DisplayName
------------                   ------------- ----            -----------
SERENITY                                   2 spooler         Print Spooler

 

Defining a New Object Member Using Add-Member

But what if I wanted to keep the original object type yet still add these properties? The solution is to define a new object member using the Add-Member cmdlet. If you just need to add a single member, you can do it with a one-line command:

PS C:\> Get-Service Spooler | Add-Member -MemberType NoteProperty -Name “Foo” -Value 123  -PassThru Status   Name               DisplayName ——   —-               ———– Running  Spooler            Print Spooler

You can specify a number of member types with Add-Member (typically you just need NoteProperty). I give it a new name and assign it a value. You must use –Passthru so that the cmdlet writes the object to the pipeline. But where’s the Foo property? It’s there, but you can’t see it because the default format for this type of object only uses Status, Name, and Displayname. But I can still use the property.

PS C:\> Get-Service Spooler | Add-Member -MemberType NoteProperty -Name “Foo” -Value 123  -PassThru | Select Name,Displayname,Status,Foo   Name               DisplayName                  Status               Foo —-               ———–                  ——               — Spooler            Print Spooler                Running              123

If I had piped this to Get-Member I’d also see the property. Things get a bit trickier when you want to add multiple properties or use something from the existing object. Here’s how I normally handle this situation:

Get-Service Spooler | foreach { $_ | Add-Member -MemberType NoteProperty -Name “RequiredCount” -Value (($_.RequiredServices | Measure).count) $computer=If ($_.Machinename -eq “.”) { $env:computername } else { $_.Machinename.ToUpper() } $_ | Add-Member -MemberType NoteProperty -Name “Computername” -Value $computer -PassThru }


The service object is piped to Foreach-Object, which in turns passes each object to Add-Member. The first expression adds a property that shows the number of required objects. Then I define a variable for the computername depending on the value of Machinename. Notice that only on the last Add-Member do I need to use –Passthru.
Again, I won’t see these properties unless I pipe the expression to Get-Member or Select-Object and grab all properties. But there’s nothing preventing me from using it like this:

Get-Service -computer “quark”,”.” | foreach { $_ | Add-Member -MemberType NoteProperty -Name “RequiredCount” -Value (($_.RequiredServices | Measure).count) $computer=If ($_.Machinename -eq “.”) { $env:computername } else { $_.Machinename.ToUpper() } $_ | Add-Member -MemberType NoteProperty -Name “Computername” -Value $computer -PassThru } | Where {$_.RequiredCount -gt 3} | Sort Computername,RequiredCount -descending | Format-Table -GroupBy Computername -Property DisplayName,Status,RequiredCount

By creating a new type of object, I can look at service information in ways the author of Get-Service never imagined. I can filter, sort, and format the results based on my new properties. You can see my results below in Figure 2.
Fig 2: Custom Properties in Powershell
Figure 2 : Using Add-Member Results
The other way you’ll see this used is when creating entirely new objects out of thin air.

PS C:\> $obj=New-Object PSObject PS C:\> $obj | Add-Member Noteproperty -Name Name -value “Jeff Hicks” PS C:\> $obj | Add-Member Noteproperty -Name Size -value 12345 PS C:\> $obj | Add-Member Noteproperty -Name Title -value “Author & Trainer” PS C:\> $obj | Add-Member Noteproperty -Name MVP -value $True PS C:\> $obj | Add-Member Noteproperty -Name Blog -value “http://jdhitsolutions.com/blog” PS C:\> $obj Name : Jeff Hicks Size : 12345 Title : Author & Trainer MVP : True Blog : http://jdhitsolutions.com/blog

Custom Objects Cliffhanger

I’ve seen variations on this idea over the years. There’s nothing wrong with this approach and if you are using it, stick with it. I would certainly encourage you to look at full help and examples for Add-Member, as it can be a valuable scripting tool. I’ll admit there are situations where I use this technique. But generally, when I’m writing custom objects to the pipeline, I think there is an even better way. I’ll cover that in the next part of this series.