Learn What IT Pros Need to Know About Windows 11 - August 24th at 1 PM ET! Learn What IT Pros Need to Know About Windows 11 - August 24th at 1 PM ET!
PowerShell

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.

$Reboot = Get-CimInstance Win32_OperatingSystem -ComputerName chi-dc04 | Select-Object CSName,LastBootUpTime 
$date = Get-Date
New-TimeSpan -Start $Reboot.LastBootUpTime -End $Date | 
Select-Object @{Label = "System Name"; Expression = {$Reboot.CSName}},
@{Label = "Last Reboot Time"; Expression = {$Reboot.LastBootUpTime}},Days,Hours,Minutes,Seconds | 
Format-Table -AutoSize

PowerShell Function to Display System Uptime

Sponsored Content

Read the Best Personal and Business Tech without Ads

Staying updated on what is happening in the technology sector is important to your career and your personal life but ads can make reading news, distracting. With Thurrott Premium, you can enjoy the best coverage in tech without the annoying ads.

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!

Function Get-MyUptime {

Param([string]$Computername = $env:COMPUTERNAME)

$Reboot = Get-CimInstance Win32_OperatingSystem -ComputerName $computername | 
Select-Object CSName,LastBootUpTime

$Date = Get-Date

New-TimeSpan -Start $Reboot.LastBootUpTime -End $Date | 
Select-Object @{Label = "System Name"; Expression = {$Reboot.CSName}},
@{Label = "Last Reboot Time"; Expression = {$Reboot.LastBootUpTime}},Days,Hours,Minutes,Seconds | 
Format-Table -AutoSize
   
} #end function

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:

get-command -ParameterName computername

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.

get-myuptime "chi-dc01"
"chi-dc01","chi-dc04","chi-core01" |  foreach {Get-MyUptime $_}

For the most part, this looks OK.

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

"chi-dc01","chi-dc04","chi-core01" | foreach {Get-MyUptime $_} | Sort Days,Hours,Minutes

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

Why? Ask PowerShell.

get-myuptime "chi-dc01" | get-member

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.

Function Get-MyUptime {

Param([string[]]$Computername = $env:COMPUTERNAME)

$Reboots = Get-CimInstance Win32_OperatingSystem -ComputerName $computername | Select-Object CSName,LastBootUpTime
$Date = Get-Date

Foreach ($reboot in $reboots) {
    New-TimeSpan -Start $Reboot.LastBootUpTime -End $Date | 
    Select-Object @{Name = "SystemName"; Expression = {$Reboot.CSName}},
    @{Name = "LastRebootTime"; Expression = {$Reboot.LastBootUpTime}},Days,Hours,Minutes,Seconds 
}
   
} #end 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.

Param([string[]]$Computername = $env:COMPUTERNAME)

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:

get-myuptime "chi-dc01","chi-dc04","chi-core01"

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:

$Reboots = Get-CimInstance Win32_OperatingSystem -ComputerName $computername | Select-Object CSName,LastBootUpTime

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.

Foreach ($reboot in $reboots) {
    New-TimeSpan -Start $Reboot.LastBootUpTime -End $Date | 
    Select-Object @{Name = "SystemName"; Expression = {$Reboot.CSName}},
    @{Name = "LastRebootTime"; Expression = {$Reboot.LastBootUpTime}},Days,Hours,Minutes,Seconds 
}

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.

get-myuptime "chi-dc01","chi-dc04","chi-core01" | 
where {$_.days -le 7} | 
Sort LastRebootTime | 
format-table

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:

BECOME A PETRI MEMBER:

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

Register
Comments (5)

5 responses to “Create a PowerShell Function to Display System Uptime”

  1. bkoehler

    LastBootUpTime conversion is based on local machine time zone, thus if querying a remote host in another time zone you'll need to account for time zone bias (assuming LastRebootTime is expected to be in the same time zone as the host).
  2. Jeffery Hicks

    I keep forgetting that as I always have everything in the same time zone. One thing you can do is that instead of using Get-Date, use the LocalDateTime property from the Win32_OperatingSystem class. $Reboot = Get-CimInstance Win32_OperatingSystem -ComputerName $computername | Select-Object CSName,LastBootUpTime,LocalDateTime $Date = $reboot.LocalDateTime Now all calculations are relative to the local server.
    • bkoehler

      Function Get-MyUptime { Param([string[]]$Computername) $Boot = Get-CimInstance Win32_OperatingSystem -ComputerName $computername | Select-Object CSName,LastBootUpTime $ComputerTimeZone = Get-CimInstance -Class win32_timezone -ComputerName $computername $LocalTimeZone = Get-CimInstance -Class win32_timezone $Date = Get-Date $bootenum = $boot.getenumerator() $tzenum = $ComputerTimeZone.getenumerator() while($bootenum.MoveNext() -and $tzenum.MoveNext()) { New-TimeSpan -Start $bootenum.Current.LastBootUpTime -End $Date | Select-Object @{Label = "System Name"; Expression = {$bootenum.Current.CSName}}, @{Name = "Last Boot Time"; Expression = {$bootenum.Current.LastBootUpTime.AddMinutes($tzenum.current.Bias - $LocalTimeZone.Bias)}}, @{Name = "System Time Zone"; Expression = {$($tzenum.current.Caption)}},Days,Hours,Minutes,Seconds } }#end function
    • bkoehler

      Time calculations are correct with your original script, just the LastBootUpTime is offset.
  3. Luis Yax

    I'm posting again, in case it didn't take the first time, the re-direct from Google did not bring me back here. Anyway, I have a similar function, except that I look in a particular computer OU for computer names, if the Flag -AllServers is $false, it will only check uptime on the local host: Here is my function: function Get-MyServersUptime([bool]$AllServers) { $hostname=$AllServers $myhostname = hostname function MUpTime { $lastboottime = (Get-WmiObject -Class Win32_OperatingSystem -computername $computer).LastBootUpTime $sysuptime = (Get-Date) – [System.Management.ManagementDateTimeconverter]::ToDateTime($lastboottime) Write-Host "$computer has been up for: " $sysuptime.days "days" $sysuptime.hours "hours" $sysuptime.minutes "minutes" $sysuptime.seconds "seconds" } clear if ($hostname) { if ($hostname -eq $true ) { $VMS = Get-ADComputer -Filter * -SearchBase 'OU=TS Servers,DC=MyDomain,DC=local' | %{$_.Name} foreach ($computer in $VMS) { MUpTime } } else { $computer = $hostname MUpTime } } else { $computer = $myhostname MUpTime } }

Leave a Reply

Register for Advanced Microsoft 365 Day!

GET-IT: Advanced Microsoft 365 1-Day Virtual Conference - Live August 24th!

Join us on Tuesday, August 24th and hear from Microsoft MVPs and industry experts about how to take advantage of Microsoft 365 at a technical level and dive deep into the features and functionality that will make your environment more secure and compliant.

RSVP Now

Sponsored By