How to Get System Uptime with PowerShell
Screenshot of PowerShell window showing command to retrieve system uptime (Get-Uptime) and formatted uptime and boot time, plus sample usage for scripting and monitoring for alerts.
Sponsor message — This article is made possible by Dargslan.com, a publisher of practical, no-fluff IT & developer workbooks.
Why Dargslan.com?
If you prefer doing over endless theory, Dargslan’s titles are built for you. Every workbook focuses on skills you can apply the same day—server hardening, Linux one-liners, PowerShell for admins, Python automation, cloud basics, and more.
How to Get System Uptime with PowerShell
System uptime represents one of the most fundamental metrics for understanding server stability, maintenance schedules, and operational health. Whether you're managing a single workstation or an entire enterprise infrastructure, knowing exactly how long your systems have been running without interruption provides critical insights into performance patterns, patch management effectiveness, and potential stability issues that might otherwise go unnoticed.
PowerShell offers administrators and IT professionals a powerful, flexible toolkit for retrieving system uptime information across Windows environments. This scripting language transforms what could be a tedious manual process into an automated, repeatable task that can scale from checking a single machine to monitoring thousands of endpoints simultaneously.
Throughout this comprehensive guide, you'll discover multiple proven methods for retrieving system uptime using PowerShell, learn how to format and export this data for reporting purposes, understand the underlying Windows Management Instrumentation classes that make these queries possible, and explore practical real-world scenarios where uptime monitoring becomes essential for maintaining operational excellence.
Understanding System Uptime and Why It Matters
System uptime refers to the continuous period during which a computer or server has been operational since its last boot. This seemingly simple metric carries profound implications for IT operations, security compliance, and business continuity planning. Organizations rely on uptime data to verify that critical patches requiring reboots have been applied, to identify systems that might be experiencing stability problems, and to ensure that maintenance windows are being properly utilized.
In enterprise environments, extended uptime periods can actually indicate problems rather than stability. Systems running for months without reboots may have missed critical security patches, accumulated memory leaks, or developed performance degradation that only a restart would resolve. Conversely, systems with unexpectedly short uptimes might be experiencing crashes, power issues, or unauthorized reboots that warrant investigation.
"Monitoring system uptime isn't just about celebrating long run times—it's about understanding your infrastructure's behavior patterns and identifying anomalies before they become critical incidents."
PowerShell provides multiple pathways to retrieve this information because different Windows versions and system configurations may require different approaches. Some methods query the Windows Management Instrumentation repository, others interact directly with the operating system kernel, and still others parse event logs to reconstruct boot history. Understanding these various approaches ensures you'll always have a reliable method regardless of your environment's specific characteristics.
The Classic WMI Approach Using Win32_OperatingSystem
The traditional method for retrieving system uptime leverages the Win32_OperatingSystem WMI class, which exposes the LastBootUpTime property. This property stores the exact date and time when the operating system last started, providing a foundation for calculating current uptime. While this approach has been the standard for years, it requires some date manipulation to convert the raw timestamp into a human-readable format.
The basic syntax retrieves the LastBootUpTime property and then calculates the difference between that timestamp and the current time:
$os = Get-WmiObject Win32_OperatingSystem
$uptime = (Get-Date) - $os.ConvertToDateTime($os.LastBootUpTime)
Write-Output "System Uptime: $($uptime.Days) days, $($uptime.Hours) hours, $($uptime.Minutes) minutes"This method works reliably across Windows Server 2008 R2 through current versions, making it an excellent choice for environments with mixed operating system deployments. The ConvertToDateTime method handles the WMI date format conversion automatically, eliminating the need for manual parsing of the timestamp string.
For administrators managing multiple systems, this approach can be extended to query remote computers by adding the -ComputerName parameter:
$computers = @("Server01", "Server02", "Workstation03")
foreach ($computer in $computers) {
$os = Get-WmiObject Win32_OperatingSystem -ComputerName $computer
$uptime = (Get-Date) - $os.ConvertToDateTime($os.LastBootUpTime)
[PSCustomObject]@{
ComputerName = $computer
LastBootTime = $os.ConvertToDateTime($os.LastBootUpTime)
UptimeDays = $uptime.Days
UptimeHours = $uptime.Hours
UptimeMinutes = $uptime.Minutes
}
}"The Win32_OperatingSystem class has been the workhorse of system information retrieval for decades, providing consistent, reliable data across virtually every Windows environment you'll encounter."
Formatting Uptime Output for Readability
Raw timespan objects contain far more precision than most reporting scenarios require. Converting uptime calculations into formatted strings improves readability and makes the information more accessible to non-technical stakeholders. PowerShell's string formatting capabilities allow you to present uptime data in various styles depending on your audience and requirements.
A common approach creates a formatted string that includes only the most relevant time components:
$os = Get-WmiObject Win32_OperatingSystem
$uptime = (Get-Date) - $os.ConvertToDateTime($os.LastBootUpTime)
$formattedUptime = "{0} days, {1} hours, {2} minutes" -f $uptime.Days, $uptime.Hours, $uptime.Minutes
Write-Output $formattedUptimeFor situations where you need to include uptime in automated reports or dashboards, converting the timespan to total hours or total days provides a single numeric value that's easier to graph and analyze over time. This approach proves particularly valuable when tracking uptime trends across large server populations.
Modern CIM Cmdlets for Enhanced Performance
PowerShell 3.0 introduced the Common Information Model (CIM) cmdlets as a modern replacement for the older WMI cmdlets. The Get-CimInstance cmdlet offers improved performance, better error handling, and native support for PowerShell remoting protocols. For uptime retrieval, this translates to faster queries and more reliable results, especially when working with remote systems.
The CIM approach mirrors the WMI methodology but with cleaner syntax and better integration with PowerShell's object pipeline:
$os = Get-CimInstance Win32_OperatingSystem
$uptime = (Get-Date) - $os.LastBootUpTime
Write-Output "System has been running for: $($uptime.Days) days, $($uptime.Hours) hours"Notice that the LastBootUpTime property is already returned as a DateTime object when using Get-CimInstance, eliminating the need for the ConvertToDateTime method required by the WMI approach. This simplification reduces potential errors and makes scripts more maintainable.
| Method | Cmdlet | DateTime Conversion | Remote Capability | Performance |
|---|---|---|---|---|
| WMI | Get-WmiObject | Required (ConvertToDateTime) | DCOM-based | Moderate |
| CIM | Get-CimInstance | Automatic | WS-Management | Fast |
| Direct Property | (Get-Uptime) | Not needed | Limited | Very Fast |
| Event Log | Get-EventLog | Automatic | Yes | Slower |
When querying multiple remote systems, CIM sessions provide significant performance advantages by establishing persistent connections that can be reused across multiple queries. This approach reduces overhead and improves script execution times in large-scale environments:
$computers = @("Server01", "Server02", "Server03")
$sessions = New-CimSession -ComputerName $computers
$uptimeData = foreach ($session in $sessions) {
$os = Get-CimInstance Win32_OperatingSystem -CimSession $session
$uptime = (Get-Date) - $os.LastBootUpTime
[PSCustomObject]@{
ComputerName = $session.ComputerName
LastBoot = $os.LastBootUpTime
UptimeDays = [math]::Round($uptime.TotalDays, 2)
}
}
$sessions | Remove-CimSession
$uptimeData | Format-Table -AutoSize"CIM cmdlets represent the future of Windows management in PowerShell, offering superior performance and better integration with modern PowerShell features while maintaining backward compatibility with existing WMI infrastructure."
Handling Errors and Unavailable Systems
Production environments inevitably include systems that are offline, unreachable, or experiencing connectivity issues. Robust uptime scripts must account for these scenarios to prevent execution failures and provide meaningful feedback about which systems couldn't be queried. PowerShell's error handling mechanisms allow you to gracefully manage these situations while continuing to process remaining systems.
Implementing try-catch blocks around CIM queries ensures that individual system failures don't halt the entire script:
$computers = Get-Content "C:\servers.txt"
$results = foreach ($computer in $computers) {
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
$uptime = (Get-Date) - $os.LastBootUpTime
[PSCustomObject]@{
ComputerName = $computer
Status = "Online"
UptimeDays = [math]::Round($uptime.TotalDays, 2)
LastBootTime = $os.LastBootUpTime
}
}
catch {
[PSCustomObject]@{
ComputerName = $computer
Status = "Unreachable"
UptimeDays = $null
LastBootTime = $null
}
}
}
$results | Export-Csv "C:\uptime_report.csv" -NoTypeInformationThis pattern creates a complete inventory that clearly identifies which systems were successfully queried and which require attention, making it ideal for scheduled reporting tasks and compliance documentation.
The Get-Uptime Cmdlet in PowerShell 6 and Later
PowerShell 6.0 introduced a dedicated Get-Uptime cmdlet that simplifies uptime retrieval to a single command. This native cmdlet returns a timespan object representing the system's uptime without requiring WMI or CIM queries, offering the fastest and most straightforward method for modern PowerShell environments.
The basic usage couldn't be simpler:
Get-UptimeThis command returns output in the format "Days:Hours:Minutes:Seconds.Milliseconds" by default. For more detailed information, you can access the properties of the returned timespan object:
$uptime = Get-Uptime
Write-Output "System uptime: $($uptime.Days) days, $($uptime.Hours) hours, $($uptime.Minutes) minutes"The Get-Uptime cmdlet also supports the -Since parameter, which returns the exact date and time when the system last booted:
Get-Uptime -SinceThis parameter proves particularly useful when you need the actual boot timestamp rather than a calculated duration, such as when correlating system events with specific timeframes or creating detailed audit logs.
Important consideration: The Get-Uptime cmdlet is only available in PowerShell 6.0 and later (PowerShell Core and PowerShell 7+). It does not exist in Windows PowerShell 5.1 or earlier versions. Scripts intended for environments with mixed PowerShell versions should include version detection and fallback logic:
if ($PSVersionTable.PSVersion.Major -ge 6) {
$uptime = Get-Uptime
} else {
$os = Get-CimInstance Win32_OperatingSystem
$uptime = (Get-Date) - $os.LastBootUpTime
}
Write-Output "Uptime: $($uptime.Days) days, $($uptime.Hours) hours"Cross-Platform Considerations
PowerShell 7's cross-platform capabilities extend to the Get-Uptime cmdlet, which functions on Windows, Linux, and macOS systems. This consistency makes it possible to create unified monitoring scripts that work across heterogeneous environments without platform-specific code branches.
On Linux systems, Get-Uptime internally reads from /proc/uptime, while on Windows it queries the system performance counter. These implementation details remain transparent to the script author, allowing you to write platform-agnostic code:
$systems = @(
@{Name="WindowsServer"; OS="Windows"},
@{Name="LinuxServer"; OS="Linux"},
@{Name="MacWorkstation"; OS="macOS"}
)
foreach ($system in $systems) {
$session = New-PSSession -HostName $system.Name
$uptime = Invoke-Command -Session $session -ScriptBlock { Get-Uptime }
[PSCustomObject]@{
SystemName = $system.Name
OperatingSystem = $system.OS
UptimeDays = $uptime.Days
}
Remove-PSSession $session
}"The Get-Uptime cmdlet represents PowerShell's evolution toward platform-agnostic system management, enabling administrators to use identical syntax whether managing Windows servers, Linux containers, or macOS endpoints."
Retrieving Boot History from Event Logs
Windows Event Logs maintain a comprehensive record of system boots and shutdowns, providing an alternative method for determining uptime and offering the additional benefit of historical boot data. The System event log records Event ID 6005 whenever the Event Log service starts, which occurs during system boot, and Event ID 6006 when it stops during shutdown.
Querying these events allows you to construct a complete boot history:
$bootEvents = Get-EventLog -LogName System -Source EventLog | Where-Object {$_.EventID -eq 6005}
$latestBoot = $bootEvents | Select-Object -First 1
$uptime = (Get-Date) - $latestBoot.TimeGenerated
Write-Output "Last boot: $($latestBoot.TimeGenerated)"
Write-Output "Uptime: $($uptime.Days) days, $($uptime.Hours) hours"This approach proves particularly valuable when investigating system stability issues or creating compliance reports that require documented proof of reboot history. The event log method can retrieve boot times even after the system has been restarted multiple times, whereas WMI and CIM methods only provide information about the current boot session.
For comprehensive boot history analysis, you can retrieve all boot events within a specific timeframe:
$startDate = (Get-Date).AddDays(-30)
$bootHistory = Get-EventLog -LogName System -Source EventLog -After $startDate |
Where-Object {$_.EventID -eq 6005} |
Select-Object TimeGenerated, @{Name="EventType";Expression={"System Boot"}}
$shutdownHistory = Get-EventLog -LogName System -Source EventLog -After $startDate |
Where-Object {$_.EventID -eq 6006} |
Select-Object TimeGenerated, @{Name="EventType";Expression={"System Shutdown"}}
$completeHistory = $bootHistory + $shutdownHistory | Sort-Object TimeGenerated -Descending
$completeHistory | Format-Table -AutoSizeModern Event Log Queries with Get-WinEvent
The newer Get-WinEvent cmdlet offers improved performance and more flexible filtering options compared to Get-EventLog, making it the preferred choice for modern PowerShell scripts. This cmdlet supports structured XML queries and can process event logs significantly faster, especially when working with large log files.
Using Get-WinEvent to retrieve boot information:
$filterHash = @{
LogName = 'System'
ID = 6005
}
$bootEvents = Get-WinEvent -FilterHashtable $filterHash -MaxEvents 1
$lastBoot = $bootEvents[0].TimeCreated
$uptime = (Get-Date) - $lastBoot
Write-Output "System booted: $lastBoot"
Write-Output "Current uptime: $($uptime.Days) days, $($uptime.Hours) hours, $($uptime.Minutes) minutes"The filter hashtable approach provides excellent performance because it applies filtering at the event log query level rather than retrieving all events and then filtering in PowerShell. This distinction becomes critical when working with large event logs or querying multiple remote systems simultaneously.
| Event ID | Event Type | Description | Use Case |
|---|---|---|---|
| 6005 | Information | Event Log service started (system boot) | Determining boot time and history |
| 6006 | Information | Event Log service stopped (shutdown) | Tracking shutdown events |
| 6008 | Warning | Unexpected shutdown detected | Identifying crashes or power failures |
| 6009 | Information | System boot with OS version information | Detailed boot logging |
| 6013 | Information | System uptime in seconds | Alternative uptime verification |
"Event logs provide an immutable audit trail of system activity that can verify uptime claims, identify unexpected reboots, and provide forensic evidence when investigating system stability issues."
Creating Automated Uptime Monitoring Solutions
Transforming individual uptime queries into comprehensive monitoring solutions requires combining PowerShell's data retrieval capabilities with scheduling, reporting, and alerting mechanisms. Organizations typically need to track uptime across dozens or hundreds of systems, identify outliers that require attention, and generate regular reports for management and compliance purposes.
A production-ready uptime monitoring script incorporates several key components: credential management for accessing remote systems, parallel processing to improve performance, error handling to manage unavailable systems, data persistence for trend analysis, and formatted output suitable for various audiences.
Here's a comprehensive example that demonstrates these principles:
# Define target systems
$computers = Get-Content "C:\Scripts\servers.txt"
# Create results array
$results = [System.Collections.ArrayList]::new()
# Process systems in parallel using workflows or ForEach-Object -Parallel (PS 7+)
$computers | ForEach-Object -ThrottleLimit 10 -Parallel {
$computer = $_
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
$uptime = (Get-Date) - $os.LastBootUpTime
# Determine status based on uptime thresholds
$status = switch ($uptime.TotalDays) {
{$_ -lt 7} { "Recently Rebooted" }
{$_ -ge 7 -and $_ -lt 90} { "Normal" }
{$_ -ge 90} { "Requires Attention" }
}
[PSCustomObject]@{
ComputerName = $computer
Status = "Online"
LastBootTime = $os.LastBootUpTime
UptimeDays = [math]::Round($uptime.TotalDays, 2)
UptimeStatus = $status
OSVersion = $os.Caption
QueryTime = Get-Date
}
}
catch {
[PSCustomObject]@{
ComputerName = $computer
Status = "Offline"
LastBootTime = $null
UptimeDays = $null
UptimeStatus = "Unreachable"
OSVersion = $null
QueryTime = Get-Date
}
}
} | ForEach-Object { [void]$results.Add($_) }
# Generate reports
$results | Export-Csv "C:\Reports\Uptime_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
# Identify systems requiring attention
$attentionRequired = $results | Where-Object {$_.UptimeStatus -eq "Requires Attention"}
if ($attentionRequired) {
$emailBody = $attentionRequired | ConvertTo-Html -Property ComputerName, UptimeDays, LastBootTime | Out-String
# Send-MailMessage implementation here
}Integrating with Monitoring Platforms
Enterprise monitoring platforms like Nagios, PRTG, Zabbix, and Azure Monitor can consume PowerShell output to incorporate uptime data into broader infrastructure monitoring dashboards. The key to successful integration lies in formatting PowerShell output to match the expected input format of your monitoring system.
For JSON-based systems, PowerShell's ConvertTo-Json cmdlet provides seamless conversion:
$computers = @("Server01", "Server02", "Server03")
$uptimeData = foreach ($computer in $computers) {
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
$uptime = (Get-Date) - $os.LastBootUpTime
@{
host = $computer
metric = "system.uptime"
value = [math]::Round($uptime.TotalSeconds, 0)
timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
tags = @{
os = $os.Caption
status = "online"
}
}
}
catch {
@{
host = $computer
metric = "system.uptime"
value = -1
timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
tags = @{
status = "offline"
}
}
}
}
$jsonOutput = $uptimeData | ConvertTo-Json -Depth 3
# Post to monitoring API
Invoke-RestMethod -Uri "https://monitoring.company.com/api/metrics" -Method Post -Body $jsonOutput -ContentType "application/json"This approach transforms PowerShell into a data collection agent that feeds your existing monitoring infrastructure, eliminating the need for separate monitoring agents while leveraging PowerShell's native Windows management capabilities.
Advanced Techniques and Performance Optimization
When scaling uptime monitoring to hundreds or thousands of systems, performance optimization becomes critical. Several techniques can dramatically improve script execution times and reduce resource consumption on both the monitoring server and target systems.
Parallel processing represents the most significant performance improvement opportunity. PowerShell 7's ForEach-Object -Parallel parameter enables concurrent processing of multiple systems, reducing total execution time from minutes to seconds in large environments. However, this parallelization requires careful throttle limit configuration to avoid overwhelming network resources or the monitoring system itself.
Consider this performance comparison:
# Sequential processing (slow)
$computers = 1..100 | ForEach-Object { "Server$_" }
Measure-Command {
$results = foreach ($computer in $computers) {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer
# Process result
}
}
# Parallel processing (fast)
Measure-Command {
$results = $computers | ForEach-Object -ThrottleLimit 20 -Parallel {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $_
# Process result
}
}In testing environments with 100 systems, sequential processing typically requires 5-10 minutes, while parallel processing with appropriate throttle limits completes the same task in 30-60 seconds. The optimal throttle limit depends on network capacity, target system responsiveness, and monitoring server resources.
Caching and Incremental Updates
For monitoring systems that query uptime frequently, implementing caching mechanisms prevents unnecessary repeated queries and reduces network traffic. This approach stores previous query results and only updates systems that have potentially changed state:
$cacheFile = "C:\Scripts\uptime_cache.xml"
$cacheTimeout = (Get-Date).AddMinutes(-15)
# Load existing cache
if (Test-Path $cacheFile) {
$cache = Import-Clixml $cacheFile
} else {
$cache = @{}
}
$computers = Get-Content "C:\Scripts\servers.txt"
$results = foreach ($computer in $computers) {
# Check if cached data is still valid
if ($cache.ContainsKey($computer) -and $cache[$computer].QueryTime -gt $cacheTimeout) {
$cache[$computer]
} else {
# Query fresh data
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
$uptime = (Get-Date) - $os.LastBootUpTime
$result = [PSCustomObject]@{
ComputerName = $computer
LastBootTime = $os.LastBootUpTime
UptimeDays = [math]::Round($uptime.TotalDays, 2)
QueryTime = Get-Date
}
$cache[$computer] = $result
$result
}
catch {
# Return cached data if available, otherwise mark as offline
if ($cache.ContainsKey($computer)) {
$cache[$computer]
} else {
[PSCustomObject]@{
ComputerName = $computer
LastBootTime = $null
UptimeDays = $null
QueryTime = Get-Date
}
}
}
}
}
# Save updated cache
$cache | Export-Clixml $cacheFile
$results | Format-Table -AutoSizeThis caching strategy dramatically reduces query load while ensuring that monitoring data remains reasonably current. The cache timeout can be adjusted based on your specific monitoring requirements and acceptable data freshness thresholds.
Credential Management for Remote Queries
Production environments typically require authenticated access to remote systems, necessitating secure credential management within PowerShell scripts. Never hardcode credentials in scripts—instead, use PowerShell's credential management features or integrate with enterprise secret management solutions.
For scheduled tasks running under service accounts, Windows Credential Manager provides secure credential storage:
# Store credentials (run once interactively)
$credential = Get-Credential
$credential | Export-Clixml "C:\Scripts\Secure\monitoring_creds.xml"
# Use stored credentials in automated scripts
$credential = Import-Clixml "C:\Scripts\Secure\monitoring_creds.xml"
$computers = Get-Content "C:\Scripts\servers.txt"
$results = foreach ($computer in $computers) {
try {
$cimSession = New-CimSession -ComputerName $computer -Credential $credential -ErrorAction Stop
$os = Get-CimInstance Win32_OperatingSystem -CimSession $cimSession
$uptime = (Get-Date) - $os.LastBootUpTime
Remove-CimSession $cimSession
[PSCustomObject]@{
ComputerName = $computer
UptimeDays = [math]::Round($uptime.TotalDays, 2)
}
}
catch {
Write-Warning "Failed to query $computer: $_"
}
}For environments with Azure Key Vault or other enterprise secret management systems, PowerShell modules like Az.KeyVault enable secure credential retrieval without storing credentials on the file system.
Practical Use Cases and Real-World Scenarios
Understanding the technical mechanisms for retrieving uptime data is only half the equation—knowing when and how to apply these techniques in real-world situations determines their actual value to your organization. Different scenarios require different approaches, reporting formats, and integration strategies.
🔧 Patch Compliance Verification
Security teams need to verify that systems have been rebooted after critical patch installations. Many security updates require a restart to become effective, and systems with extended uptimes may indicate incomplete patching processes. An automated uptime check following patch deployment windows can identify systems that failed to reboot:
$patchWindow = Get-Date "2024-01-15 02:00:00"
$computers = Get-Content "C:\Scripts\patched_servers.txt"
$nonCompliant = foreach ($computer in $computers) {
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
if ($os.LastBootUpTime -lt $patchWindow) {
[PSCustomObject]@{
ComputerName = $computer
LastBootTime = $os.LastBootUpTime
DaysSinceBoot = [math]::Round(((Get-Date) - $os.LastBootUpTime).TotalDays, 1)
ComplianceStatus = "Non-Compliant"
}
}
}
catch {
[PSCustomObject]@{
ComputerName = $computer
LastBootTime = "Query Failed"
DaysSinceBoot = $null
ComplianceStatus = "Unknown"
}
}
}
if ($nonCompliant) {
$nonCompliant | Export-Csv "C:\Reports\Patch_NonCompliance.csv" -NoTypeInformation
# Trigger alert to operations team
}🔧 Capacity Planning and Performance Analysis
Extended uptime correlates with memory consumption patterns, temporary file accumulation, and performance degradation in certain applications. Tracking uptime alongside performance metrics helps identify systems that benefit from regular restart schedules:
$computers = Get-Content "C:\Scripts\production_servers.txt"
$performanceData = foreach ($computer in $computers) {
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
$uptime = (Get-Date) - $os.LastBootUpTime
# Gather additional performance metrics
$memory = Get-CimInstance Win32_OperatingSystem -ComputerName $computer
$memoryUsagePercent = [math]::Round((($memory.TotalVisibleMemorySize - $memory.FreePhysicalMemory) / $memory.TotalVisibleMemorySize) * 100, 2)
[PSCustomObject]@{
ComputerName = $computer
UptimeDays = [math]::Round($uptime.TotalDays, 1)
MemoryUsagePercent = $memoryUsagePercent
TotalMemoryGB = [math]::Round($memory.TotalVisibleMemorySize / 1MB, 2)
FreeMemoryGB = [math]::Round($memory.FreePhysicalMemory / 1MB, 2)
}
}
catch {
Write-Warning "Failed to query $computer"
}
}
# Identify systems with high uptime and memory pressure
$concerningSystems = $performanceData | Where-Object {$_.UptimeDays -gt 90 -and $_.MemoryUsagePercent -gt 85}
$concerningSystems | Format-Table -AutoSize🔧 Disaster Recovery Testing
Disaster recovery procedures often require documentation proving that systems were successfully restarted and returned to operational status within defined recovery time objectives. Uptime tracking before and after DR tests provides objective evidence of recovery success:
# Pre-DR test baseline
$preDRBaseline = foreach ($computer in $drSystems) {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer
[PSCustomObject]@{
ComputerName = $computer
PreDRBootTime = $os.LastBootUpTime
PreDRUptime = ((Get-Date) - $os.LastBootUpTime).TotalDays
}
}
$preDRBaseline | Export-Csv "C:\DR_Tests\Baseline_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
# Post-DR test verification (run after DR exercise)
$postDRVerification = foreach ($computer in $drSystems) {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer
$baseline = $preDRBaseline | Where-Object {$_.ComputerName -eq $computer}
[PSCustomObject]@{
ComputerName = $computer
PostDRBootTime = $os.LastBootUpTime
BootTimeChanged = ($os.LastBootUpTime -gt $baseline.PreDRBootTime)
RecoveryVerified = ($os.LastBootUpTime -gt $baseline.PreDRBootTime)
}
}
$postDRVerification | Export-Csv "C:\DR_Tests\Verification_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation🔧 Service Level Agreement Monitoring
Organizations with uptime SLAs need continuous monitoring to detect and document any violations. Combining uptime queries with historical event log analysis provides comprehensive availability reporting:
$reportingPeriod = (Get-Date).AddDays(-30)
$computers = Get-Content "C:\Scripts\sla_systems.txt"
$slaReport = foreach ($computer in $computers) {
# Get all boot events in reporting period
$bootEvents = Get-WinEvent -ComputerName $computer -FilterHashtable @{
LogName = 'System'
ID = 6005
StartTime = $reportingPeriod
} -ErrorAction SilentlyContinue
# Get unexpected shutdown events
$crashEvents = Get-WinEvent -ComputerName $computer -FilterHashtable @{
LogName = 'System'
ID = 6008
StartTime = $reportingPeriod
} -ErrorAction SilentlyContinue
# Calculate availability percentage
$totalMinutes = ((Get-Date) - $reportingPeriod).TotalMinutes
$downtimeMinutes = ($crashEvents | Measure-Object).Count * 5 # Estimate 5 minutes per crash
$availabilityPercent = [math]::Round((($totalMinutes - $downtimeMinutes) / $totalMinutes) * 100, 3)
[PSCustomObject]@{
ComputerName = $computer
ReportingPeriod = "$($reportingPeriod.ToString('yyyy-MM-dd')) to $((Get-Date).ToString('yyyy-MM-dd'))"
BootCount = ($bootEvents | Measure-Object).Count
UnexpectedShutdowns = ($crashEvents | Measure-Object).Count
AvailabilityPercent = $availabilityPercent
SLATarget = 99.9
SLAMet = ($availabilityPercent -ge 99.9)
}
}
$slaReport | Export-Csv "C:\Reports\SLA_Report_$(Get-Date -Format 'yyyyMM').csv" -NoTypeInformation🔧 Automated Maintenance Scheduling
Systems with extended uptimes can be automatically flagged for scheduled maintenance windows, ensuring that necessary reboots occur during planned downtime rather than as emergency interventions:
$maintenanceThreshold = 60 # Days
$computers = Get-Content "C:\Scripts\all_servers.txt"
$maintenanceQueue = foreach ($computer in $computers) {
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
$uptime = (Get-Date) - $os.LastBootUpTime
if ($uptime.TotalDays -ge $maintenanceThreshold) {
[PSCustomObject]@{
ComputerName = $computer
UptimeDays = [math]::Round($uptime.TotalDays, 1)
Priority = switch ($uptime.TotalDays) {
{$_ -ge 180} { "Critical" }
{$_ -ge 120} { "High" }
{$_ -ge 90} { "Medium" }
default { "Low" }
}
RecommendedAction = "Schedule Maintenance Reboot"
}
}
}
catch {
Write-Warning "Could not query $computer"
}
}
$maintenanceQueue | Sort-Object Priority, UptimeDays -Descending |
Export-Csv "C:\Reports\Maintenance_Queue.csv" -NoTypeInformationTroubleshooting Common Issues
Even well-designed uptime monitoring scripts encounter various challenges in production environments. Understanding common failure modes and their resolutions ensures that your monitoring remains reliable and accurate.
Access Denied Errors: These typically indicate insufficient permissions on the target system. WMI and CIM queries require administrative privileges on remote systems. Verify that the account running the script has appropriate permissions, and consider using Group Policy to configure WMI namespace security if necessary.
RPC Server Unavailable: This error suggests firewall rules blocking WMI communication or the Windows Management Instrumentation service not running on the target. Windows Firewall must allow "Windows Management Instrumentation (WMI-In)" rules, and the WinMgmt service must be running.
Timeout Errors: Slow network connections or overloaded target systems can cause query timeouts. Implement timeout parameters and retry logic:
$timeout = 30 # seconds
$maxRetries = 3
function Get-SystemUptimeWithRetry {
param($ComputerName)
$attempt = 0
while ($attempt -lt $maxRetries) {
try {
$option = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName $ComputerName -SessionOption $option -OperationTimeoutSec $timeout -ErrorAction Stop
$os = Get-CimInstance Win32_OperatingSystem -CimSession $session
$uptime = (Get-Date) - $os.LastBootUpTime
Remove-CimSession $session
return $uptime
}
catch {
$attempt++
if ($attempt -lt $maxRetries) {
Start-Sleep -Seconds 5
}
}
}
throw "Failed to retrieve uptime after $maxRetries attempts"
}
$uptime = Get-SystemUptimeWithRetry -ComputerName "Server01""Robust error handling isn't optional in production scripts—it's the difference between a monitoring solution that provides reliable data and one that generates false alerts and wastes administrator time investigating phantom issues."
Incorrect Uptime Values: Occasionally, WMI or CIM queries return unexpected uptime values, particularly on systems that have experienced sleep or hibernation states. Cross-referencing with event log data provides validation:
function Get-ValidatedUptime {
param($ComputerName)
# Get uptime from CIM
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $ComputerName
$cimUptime = (Get-Date) - $os.LastBootUpTime
# Validate against event log
$bootEvent = Get-WinEvent -ComputerName $ComputerName -FilterHashtable @{
LogName = 'System'
ID = 6005
} -MaxEvents 1
$eventLogUptime = (Get-Date) - $bootEvent[0].TimeCreated
# If values differ significantly, use event log value
if ([math]::Abs(($cimUptime.TotalMinutes - $eventLogUptime.TotalMinutes)) -gt 5) {
Write-Warning "Uptime discrepancy detected for $ComputerName. Using event log value."
return $eventLogUptime
}
return $cimUptime
}
$validatedUptime = Get-ValidatedUptime -ComputerName "Server01"Exporting and Reporting Uptime Data
Collecting uptime data serves little purpose without effective reporting mechanisms that transform raw data into actionable insights. Different audiences require different presentation formats—technical teams need detailed CSV exports for analysis, management prefers executive summaries with visual indicators, and compliance auditors require formatted reports with attestation capabilities.
PowerShell supports multiple export formats, each suited to specific use cases. CSV exports provide maximum flexibility for further analysis in Excel or database systems. HTML reports offer visually appealing presentations suitable for email distribution or web dashboards. JSON exports enable integration with modern monitoring platforms and APIs.
Here's a comprehensive reporting example that generates multiple output formats:
$computers = Get-Content "C:\Scripts\servers.txt"
$reportDate = Get-Date -Format "yyyy-MM-dd"
# Collect uptime data
$uptimeData = foreach ($computer in $computers) {
try {
$os = Get-CimInstance Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop
$uptime = (Get-Date) - $os.LastBootUpTime
[PSCustomObject]@{
ComputerName = $computer
Status = "Online"
LastBootTime = $os.LastBootUpTime.ToString("yyyy-MM-dd HH:mm:ss")
UptimeDays = [math]::Round($uptime.TotalDays, 2)
UptimeHours = [math]::Round($uptime.TotalHours, 1)
OSVersion = $os.Caption
ServicePackMajorVersion = $os.ServicePackMajorVersion
}
}
catch {
[PSCustomObject]@{
ComputerName = $computer
Status = "Offline"
LastBootTime = "N/A"
UptimeDays = $null
UptimeHours = $null
OSVersion = "Unknown"
ServicePackMajorVersion = $null
}
}
}
# CSV Export for detailed analysis
$uptimeData | Export-Csv "C:\Reports\Uptime_Detail_$reportDate.csv" -NoTypeInformation
# HTML Report for email distribution
$htmlHead = @"
body { font-family: Arial, sans-serif; }
table { border-collapse: collapse; width: 100%; }
th { background-color: #4CAF50; color: white; padding: 10px; text-align: left; }
td { border: 1px solid #ddd; padding: 8px; }
tr:nth-child(even) { background-color: #f2f2f2; }
.offline { background-color: #ffcccc; }
.warning { background-color: #fff3cd; }
"@
$htmlBody = $uptimeData | ConvertTo-Html -Head $htmlHead -PreContent "System Uptime Report - $reportDate" | Out-String
$htmlBody | Out-File "C:\Reports\Uptime_Report_$reportDate.html"
# JSON Export for API integration
$uptimeData | ConvertTo-Json -Depth 3 | Out-File "C:\Reports\Uptime_Data_$reportDate.json"
# Executive Summary
$summary = [PSCustomObject]@{
ReportDate = $reportDate
TotalSystems = $uptimeData.Count
OnlineSystems = ($uptimeData | Where-Object {$_.Status -eq "Online"}).Count
OfflineSystems = ($uptimeData | Where-Object {$_.Status -eq "Offline"}).Count
AverageUptimeDays = [math]::Round(($uptimeData | Where-Object {$_.UptimeDays} | Measure-Object UptimeDays -Average).Average, 2)
SystemsOver90Days = ($uptimeData | Where-Object {$_.UptimeDays -gt 90}).Count
}
$summary | Export-Csv "C:\Reports\Uptime_Summary_$reportDate.csv" -NoTypeInformationThis multi-format approach ensures that every stakeholder receives uptime information in their preferred format, maximizing the value of your monitoring efforts.
Scheduling Automated Reports
Windows Task Scheduler enables automated execution of PowerShell uptime monitoring scripts on regular intervals. Creating scheduled tasks programmatically ensures consistency across monitoring servers and simplifies deployment:
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Get-UptimeReport.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 6:00AM
$principal = New-ScheduledTaskPrincipal -UserId "DOMAIN\MonitoringAccount" -LogonType Password
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Hours 2) -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 5)
Register-ScheduledTask -TaskName "Daily Uptime Report" -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description "Generates daily system uptime reports"For environments with configuration management systems like SCCM or Intune, deploying monitoring scripts and scheduled tasks through these platforms provides centralized management and ensures consistent configuration across all monitoring infrastructure.
How accurate is PowerShell uptime reporting compared to other monitoring tools?
PowerShell queries the same underlying Windows APIs and WMI classes that commercial monitoring tools use, providing identical accuracy. The LastBootUpTime property from Win32_OperatingSystem represents the authoritative boot time recorded by the Windows kernel. Any differences between PowerShell results and other tools typically stem from how the data is formatted or presented rather than actual accuracy discrepancies. Event log validation provides an additional verification layer when absolute certainty is required.
Can I retrieve uptime information from systems that are currently offline or unreachable?
Uptime data requires querying the live system, so offline systems cannot provide current uptime information. However, you can retrieve historical boot and shutdown events from backed-up event logs or centralized log collection systems like Windows Event Forwarding or SIEM platforms. These historical records allow reconstruction of uptime patterns even when systems are unavailable. For comprehensive monitoring, implement both real-time queries and historical event log analysis.
What is the performance impact of running uptime queries against hundreds of systems simultaneously?
Individual WMI or CIM queries consume minimal resources on target systems—typically less than 1% CPU for under one second. The primary performance consideration is network bandwidth and the monitoring server's capacity to manage concurrent connections. Using PowerShell 7's parallel processing with appropriate throttle limits (typically 10-50 concurrent connections depending on infrastructure) allows efficient querying of large environments without overwhelming network or system resources. CIM sessions with connection reuse further optimize performance for repeated queries.
How should I handle systems in different time zones when calculating uptime?
PowerShell automatically handles time zone conversions when retrieving LastBootUpTime from remote systems. The returned DateTime object is in the local time zone of the queried system, and PowerShell's date arithmetic correctly calculates uptime regardless of time zone differences between the monitoring server and target systems. For reporting purposes where you need consistent time zone representation, convert all timestamps to UTC using the ToUniversalTime() method before performing calculations or generating reports.
Is there a way to track uptime for systems that use sleep or hibernation modes?
Traditional uptime queries report time since the last full boot, which resets when systems wake from hibernation but not from sleep mode. Windows distinguishes between these states through different event IDs: Event ID 1 (Power-Troubleshooter) indicates sleep/wake cycles, while Event ID 42 (Kernel-Power) indicates hibernation. For accurate availability tracking on systems using power management, combine LastBootUpTime queries with power event analysis to distinguish between planned power-saving modes and unexpected shutdowns or reboots.
Can PowerShell retrieve uptime from Linux servers in mixed environments?
PowerShell 7's cross-platform capabilities include the Get-Uptime cmdlet, which functions identically on Linux and Windows systems. For remote Linux queries, establish SSH-based PowerShell sessions using New-PSSession with the -HostName parameter, then invoke Get-Uptime through Invoke-Command. This approach enables unified monitoring scripts that handle both Windows and Linux infrastructure without platform-specific code branches, significantly simplifying management of heterogeneous environments.