PowerShell Uptime Report: Check Last Boot Time of Servers

After Patch Tuesday, how do you confirm every server actually came back up and has been running since the expected reboot? Checking each server manually through RDP or SCCM is slow. A PowerShell server uptime script queries last boot time across all your servers in parallel, calculates uptime duration, flags anything that rebooted unexpectedly in the last 24 hours, and exports a structured report in under a minute. This post builds the complete uptime reporting solution using CIM instances.
Get Last Boot Time with CIM
Get-CimInstance with the Win32_OperatingSystem class returns the LastBootUpTime property as a proper .NET DateTime object — much easier to work with than the old WMI string format:
$os = Get-CimInstance -ClassName Win32_OperatingSystem
Write-Host "Last boot time: $($os.LastBootUpTime)"
Write-Host "System name: $($os.CSName)"
Last boot time: 5/2/2026 2:34:17 AM
System name: SERVER01
Calculate Uptime Duration
Subtract the last boot time from now to get the uptime as a TimeSpan, then format it readably:
function Get-ServerUptime {
param([string]$ComputerName = $env:COMPUTERNAME)
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $ComputerName
$uptime = (Get-Date) - $os.LastBootUpTime
$uptimeStr = "{0} days, {1:D2}h {2:D2}m" -f $uptime.Days, $uptime.Hours, $uptime.Minutes
[PSCustomObject]@{
Server = $ComputerName
LastBootTime = $os.LastBootUpTime
Uptime = $uptimeStr
UptimeDays = [Math]::Round($uptime.TotalDays, 1)
}
}
Get-ServerUptime -ComputerName "Server01"
Server LastBootTime Uptime UptimeDays
------ ------------ ------ ----------
Server01 5/2/2026 2:34:17 AM 2 days, 07h 22m 2.3
Check Multiple Servers
Query uptime across a server list using Invoke-Command for parallel execution, which is dramatically faster than sequential CIM calls:
$servers = Get-Content "C:\Scripts\servers.txt"
$uptimeResults = Invoke-Command -ComputerName $servers -ThrottleLimit 20 -ScriptBlock {
$os = Get-CimInstance -ClassName Win32_OperatingSystem
$uptime = (Get-Date) - $os.LastBootUpTime
[PSCustomObject]@{
Server = $env:COMPUTERNAME
LastBootTime = $os.LastBootUpTime
UptimeDays = [Math]::Round($uptime.TotalDays, 1)
UptimeStr = "{0}d {1:D2}h {2:D2}m" -f $uptime.Days, $uptime.Hours, $uptime.Minutes
OS = $os.Caption
}
} -ErrorAction SilentlyContinue
$uptimeResults | Sort-Object UptimeDays | Format-Table -AutoSize
Flag Servers Rebooted in Last 24 Hours
After a patch cycle, identify all servers that rebooted within the expected window and flag any that rebooted outside it (potential unexpected restarts):
$patchWindow = [datetime]"2026-05-03 20:00:00"
$patchWindowEnd = [datetime]"2026-05-04 06:00:00"
$now = Get-Date
$recentReboots = $uptimeResults | Where-Object { $_.LastBootTime -gt $now.AddDays(-1) }
Write-Host "Servers rebooted in last 24 hours: $($recentReboots.Count)"
foreach ($svr in $recentReboots) {
$inWindow = $svr.LastBootTime -ge $patchWindow -and $svr.LastBootTime -le $patchWindowEnd
$flag = if ($inWindow) { "Expected (patch window)" } else { "UNEXPECTED REBOOT" }
Write-Host " $($svr.Server): $($svr.LastBootTime) — $flag"
}
Servers rebooted in last 24 hours: 3
SERVER01: 5/3/2026 11:45 PM — Expected (patch window)
SERVER02: 5/4/2026 2:12 AM — Expected (patch window)
SERVER03: 5/4/2026 8:30 AM — UNEXPECTED REBOOT
Export Uptime Report to CSV
Export the full uptime data to CSV for inclusion in post-patching documentation or operational reports:
$reportPath = "C:\Reports\Uptime_$(Get-Date -Format 'yyyyMMdd_HHmm').csv"
$uptimeResults |
Select-Object Server, LastBootTime, UptimeDays, UptimeStr, OS |
Sort-Object UptimeDays |
Export-Csv -Path $reportPath -NoTypeInformation
Write-Host "Uptime report: $reportPath ($($uptimeResults.Count) servers)"
Alert on Unexpected Reboots
Send an email alert when servers reboot outside the expected maintenance window:
$unexpected = $uptimeResults | Where-Object {
$_.LastBootTime -gt (Get-Date).AddHours(-6) -and
($_.LastBootTime -lt $patchWindow -or $_.LastBootTime -gt $patchWindowEnd)
}
if ($unexpected) {
$body = "Unexpected reboots detected:`n" +
($unexpected | ForEach-Object { "$($_.Server): $($_.LastBootTime)" } | Out-String)
Send-MailMessage -From "[email protected]" -To "[email protected]" `
-Subject "ALERT: Unexpected reboots on $($unexpected.Count) server(s)" `
-Body $body -SmtpServer "smtp-relay.corp.com"
}
Common Errors and Fixes
-
Time zone differences affect uptime calculation. If servers are in different time zones,
LastBootUpTimereturns local time on the remote server. Use$os.LastBootUpTime.ToUniversalTime()before comparison when querying servers across time zones, then convert back to local time for display. -
CIM query requires WinRM or DCOM access.
Get-CimInstance -ComputerNameuses WinRM by default. If WinRM is blocked, create a CIM session with DCOM fallback usingNew-CimSessionOption -Protocol Dcom. Always test connectivity withTest-WSMan -ComputerName $serverbefore running bulk uptime queries.
Related Cmdlets / See Also
Wrapping Up
Use Get-CimInstance Win32_OperatingSystem for last boot time, Invoke-Command with -ThrottleLimit for parallel multi-server queries, and compare boot times against known patch windows to separate expected from unexpected reboots. Schedule this script to run automatically after every Patch Tuesday maintenance window.


