Creating a Better PowerShell Module

Earlier this year we published a series of articles that went through the process of taking a simple command to a complete PowerShell tool wrapped up in a module. Recently I had a reason to revisit that module and ended up making a number of changes, many of which I consider improvements. I thought I would take a few minutes to explain the changes and why I made them.

The primary function uses Get-CimInstance to retrieve information from WMI so that I can calculate a server’s uptime. The original command only accepts a string for a computer name. However, Get-CimInstance can also accept a CIMSession. This is especially useful if the remote computer is still running PowerShell 2.0 or if you need to setup sessions with alternate credentials. This was impossible in the original command. So I added a parameter for CIMSession.

​
Note that I didn't re-invent the wheel. I used the same parameter that Get-CimInstance uses.
The CimSession parameter (Image Credit: Jeff Hicks)
The CimSession parameter (Image Credit: Jeff Hicks)
You'll also notice that each parameter in the new version of my function belongs to a parameter set. This is necessary because my command can connect using a computername or a CIMSession but not both. How does the command know which one to use? In my case, there are a few indicators. First, I defined default parameter set in the cmdletbinding tag.
​
If your function will be using parameter sets, you will need to do the same. Another trick I am using is that the Computername parameter is explicitly positional.
​
This means I have to use the –CimSession parameter explicitly.
​
But I don't have to if I am piping CIMSessions to the command.
Piping CimSessions to the command (Image Credit: Jeff Hicks)
Piping CimSessions to the command (Image Credit: Jeff Hicks)
The other major change relates to the actual output. In the previous version, I used custom hashtables with Select-Object to define a number of properties. The downside is that these properties become Noteproperties and are essentially static. As I was working with the command, I realized I wanted an easy way to refresh uptime data without having to completely re-run the original command. Because I was already using a custom type name so that I could use a custom format file, I decided it made sense to create a custom type extension file as well. The file includes a number of ScriptProperty definitions.
ScriptProperty definitions (Image Credit: Jeff Hicks)
ScriptProperty definitions (Image Credit: Jeff Hicks)
The code in the GetScriptBlock section is essentially the same thing I was doing with Select-Object. But this code will be executed each time I look at the object. The $this variable means "this object". I also created a few alias properties.
Defined alias properties (Image Credit: Jeff Hicks)
Defined alias properties (Image Credit: Jeff Hicks)
This simplifies the PowerShell command inside the function.
​
Select-Object -property CSName,LastBootUpTime,@{Name="CimSession";Expression={$True}}

I also decided to include a few script methods, including one to get the local date and time on the server.

Defining a script method (Image Credit: Jeff Hicks)
Defining a script method (Image Credit: Jeff Hicks)

I had to take one extra step in customizing the object. If the object was created with a CIMSession I wanted to re-use that session for the method. That’s the whole point of having a CIMSession. So in the command that defines $obj I define a Boolean property I call CimSession. If the object was created using a CimSession, then this value will be true. You can see all of these new properties and methods with Get-Member.

Viewing new property members (Image Credit: Jeff Hicks)
Viewing new property members (Image Credit: Jeff Hicks)

The default display remains unchanged.

Viewing Uptime (Image Credit: Jeff Hicks)
Viewing Uptime (Image Credit: Jeff Hicks)

However, because the properties are no longer static, I can get updated values simply by looking at the data again.

Live data (Image Credit: Jeff Hicks)
Live data (Image Credit: Jeff Hicks)

Any my methods work very nicely on a collection:

Invoking a custom method (Image Credit: Jeff Hicks)
Invoking a custom method (Image Credit: Jeff Hicks)

I’ll let you try out the other methods.

So where can you get all of this magical deliciousness? I have started publishing some of my PowerShell projects on GitHub. You can download all of the files you need from the MyUptime repository. I hope the project can at least serve as a guide to help you build better PowerShell tools and modules.