Extending Objects in Windows PowerShell, Part 1

If you have followed my work for a while you know that I am constantly talking about objects in PowerShell. During my training classes, I stress the importance of thinking about objects in the pipeline. When I look at problems in forums, I look to see if the poster is thinking about objects or still working with text. Once I have the data I want to work with as a collection of objects, then there’s practically no limit to what I can do with it.

With that in mind, probably one of the most compelling features in PowerShell is how easy it is to extend it. You’re not limited to whatever a cmdlet writes to the pipeline. You can easily extend the objects to meet your business needs, perhaps in ways you have never even considered before. Here’s what I am talking about.

We can start with a simple command.

PowerShell

1

$dc1=Get-CimInstancewin32_operatingsystem-ComputerNamechi-dc01

The value of $dc1 is an object that contains WMI information from the win32_operatingsystem class.

The $dc1 object in PowerShell. (Image Credit: Jeff Hicks)

Remember that you can pipe to Select-Object to see all of the properties.

PowerShell

1

$dc1|select*

Let’s say you want to know how long the computer has been running. It’s important to note before we get too far that even though I’m running a command for a single computer object, nothing would change if I had 100 objects. You might use Select-Object to create a new property on the fly.

Although that’s useful, our new property only exists for the duration of the command. Imagine that I would like my uptime property to be persistent because I want to do several things with the $dc1 variable. This is where the Add-Member cmdlet comes into play.

In PowerShell, an object’s properties and methods are referred to as its members, which is why we have a Get-Member cmdlet. With Add-Member, we can dynamically add new properties and methods. Here’s a command to add a custom property to $dc1.

There are three parts to PowerShell’s Add-Member cmdlet. First, you need to specify what type of member to add. Although the help content indicates a number different types, several of them don’t seem to work in PowerShell. You’ll most likely be using member types of Noteproperty, ScriptProperty, AliasProperty and possibly ScriptMethod.

In this situation, I’m using a ScriptProperty, which means that I’m going to use a piece of PowerShell script to create a new property value. My new property will have a name of uptime, and the value will be whatever result I get from executing the scriptblock. The code in your scriptblock can be as long or complex as you need it to be. My script block is taking the result of Get-Date and subtracting the LastBootUpTime value from the current object in the pipeline. In scriptblocks for Where-Object or ForEach-Object, we use $_ to reference the current object in the pipeline. With this type of operation, we use $this.

When you run this command, nothing will be written to the pipeline by default, unless you use the –Passthru parameter. Although, you probably still won’t see the custom property, because PowerShell has a predefined set of rules on how to display a CIM Win32_OperatingSystem object and those rules know nothing about a property called Uptime. With that said, the property is there, and you can reference it.

That is probably not the result for the Uptime property that you were expecting, but it is correct as subtracting two dates creates a TimeSpan object. But PowerShell is quite helpful and will format the result the way it thinks you need it.

Formatting our result. (Image Credit: Jeff Hicks)

I don’t know about you, but that looks pretty helpful to me. However, for the sake of demonstration, let’s say you always want the uptime value to be a string

I want to make sure you notice the –Force parameter I added. If you try to define a member that already exists, PowerShell will complain and refuse to add it, unless you specify –Force. Now that I have, the uptime property is formatted the way I want it.

Newly formatted dc1.uptime property. (Image Credit: Jeff Hicks)

This property will exist for the duration of my PowerShell session or until I remove the $dc1 variable. If I always wanted to have this property, I could create a custom type extension using Update-TypeData. But that’s a topic for a different article.

To wrap up, let me quickly demonstrate that this technique works for multiple objects as well. I have a group of CIMSessions to a few servers. I’m going to get Win32_OperatingSystem instances and add my custom property.

MEMBER LOGIN:

BECOME A PETRI MEMBER:

About the Contributor

Jeffery Hicks is an IT veteran with over 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He is a multi-year recipient of the Microsoft MVP Award in Windows PowerShell. He works today as an independent author, teacher and consultant. Jeff has written for numerous online sites and print publications and is a frequent speaker at technology conferences and user groups. His latest book is PowerShell Scripting and Toolmaking.