Create a PowerShell Function to Display System Uptime

Recently I saw a post on Twitter about a PowerShell script to display system uptime. Since I’m always curious to see how people are using PowerShell I followed the link the script, which was posted on the TechNet Script Gallery site. As expected the author used Windows Management Instrumentation (WMI) to query the Win32_OperatingSystem class.

This class includes a property that indicates when the computer last booted. If you use Get-CIMInstance, this value is automatically formatted into as a datetime value. I appreciated that he used a Timespan object to display the uptime. But I did have one comment about his code, and something that I see quite often. His original code may be updated by the time you read this. By the way, the author was very receptive to my suggestions and open to me about using his code as a learning device, as he is still learning PowerShell. Even if you don’t have a need for the end result, the process I want to explore from command to tool should be very useful.

To begin, here’s the original code.

PowerShell Function to Display System Uptime

Creating a PowerShell Function

The one major drawback to this example is that it includes formatting via the Format-Table cmdlet. This code can be turned into a useful tool, which is what I intend to show you, but including formatting limits you as you’ll see in a moment. First, let’s turn this into a simple function. I have a set of commands that work just fine from the prompt. I can easily paste them inside a function, turn the computername into a parameter and I’m done!

A quick word on names: don’t re-invent the wheel. You should be able to use a common parameter name that you see elsewhere. If your parameter needs to reflect the name of a computer, then use Computername since that is commonly used in other cmdlets. Don’t use something like System or MachineName. Doing so makes your command less intuitive. If you need a parameter like SysName say to support your corporate culture, you can add it as an alias. You’ll see an example of that later. Aren’t sure about your parameter name choice? Ask PowerShell for other cmdlets that might be using it:

The name of your command should use one of the standard verbs. Run the Get-Verb cmdlet to discover them. The noun should be singular and reflective of the “thing” you are working with. To avoid possible naming collisions you might consider using a noun prefix to reflect you or your organization.

With my function, it becomes much easier to get system uptime.

For the most part, this looks OK.

But what happens when you try to do something like sort the output?

Remember, the default sort is ascending which isn’t what I got.

Why? Ask PowerShell.

This is why you never include format cmdlets in our scripts and functions. They write formatting objects to the pipeline. The only thing you can do is pipe to Out-File or Out-Printer. Instead, write your function so that it writes objects to the pipeline. Then if you need to format it, run the command with Format-Table. Maybe tomorrow you need Format-List or to export to a CSV file. If you include formatting inside your function, you’re done. That’s all your command will ever be able to do.

With that in mind here’s my revised function.

While I was removing Format-Table I made a few other changes. First, I modified the Computername parameter so it could accept a collection of computer names.

The parameter is expecting a string and inserting [] tells PowerShell to expect multiple values, usually separated by commas. Now I can run the command like this:

This works because the Computername parameter from Get-CimInstance, also takes an array of strings for a computer name. This also means that when this part of the function runs:

The $Reboots variable will be an array or collection of CIM instances. This means I will need to process each one individually in order to get the uptime. That’s why I’m using a ForEach enumerator.

If you compare these two versions closely you’ll see that I changed the name of the variable holding the Get-CimInstance command from $Reboot to $Reboots. I did this so that I wouldn’t have to revise the timespan code that already worked. I simply wrapped it in a ForEach enumerator and used the same variable name.

I also changed the custom hash table in Select-Object that defines new properties. While you can use Label and Name interchangeably, I prefer to use Name because it helps remind me that I am defining a property name and not a formatting label. That is also why I modified the names to remove the spaces. My function is going to write an object to the pipeline and it is much easier to work with property names without spaces.

My result is a simple object

Need formatting? Use PowerShell.

Now that I have objects, I can sort, filter or whatever I need.

I want to only see servers that have been up for 7 days or less, sorted on the last reboot time.

Look how far we’ve come from a few lines of PowerShell commands to a re-usable function. And we’re not done yet! There’s much more that I want to show you but that will have to wait for another article. In the meantime, I would encourage you to get your PowerShell functions at least to this level. Think writing objects to the pipeline and leave formatting to PowerShell.

Related Topics:

  • PowerShell

    Don't have a login but want to join the conversation? Sign up for a Petri Account