PowerShell脚本问题:域内Windows服务器Uptime检测函数与循环不兼容
修复你的域服务器运行时间检查脚本
首先,咱们来梳理下原脚本里的几个核心问题,这些就是导致Get-Uptime函数和ForEach循环配合失效的原因:
Get-Uptime函数未适配远程执行:原函数默认在本地运行,没有接收远程计算机名的参数,没法在循环里针对每台服务器获取运行时间。- 函数返回的是字符串,无法用于数值比较:你现在返回的是
"Uptime: X days"这种文本,没法和$Deadline(日期对象)做>=比较。 - 循环逻辑缺失关键步骤:
ForEach里既没调用Get-Uptime,也没处理$computer对象的Name属性,$uptime变量根本没被赋值。 - 服务器筛选不够精确:原脚本只是匹配包含"server"的系统,没有精准定位Windows Server 2012 R2。
下面是修正后的完整脚本,我会在代码里加注释说明每个修改点:
# Variables for sending the report $sender = "serviceaccount@domain.com" $receiver = "administrator@domain.no" $smtpServer = "mailserver.domain.com" # 精准筛选域内Windows Server 2012 R2的服务器 Get-ADComputer -Filter * -Properties OperatingSystem | Where-Object { $_.OperatingSystem -eq "Windows Server 2012 R2 Standard" -or $_.OperatingSystem -eq "Windows Server 2012 R2 Datacenter" } | Select-Object Name | Export-Csv -Path "C:\script\serverlist.csv" -NoTypeInformation $computers = Import-Csv -Path "C:\script\serverlist.csv" # 设置阈值:运行时间超过30天则告警 $thresholdDays = 30 $deadline = (Get-Date).AddDays(-$thresholdDays) # 计算30天前的日期,用于和最后启动时间比较 # 重构Get-Uptime函数:支持远程计算机,返回可用于比较的对象(最后启动时间、运行天数) function Get-Uptime { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$ComputerName ) try { # 使用Get-CimInstance替代WMI,更高效且支持远程 $os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $ComputerName -ErrorAction Stop $lastBootTime = $os.ConvertToDateTime($os.LastBootUpTime) $uptimeDays = ((Get-Date) - $lastBootTime).Days # 返回包含关键信息的自定义对象,方便后续逻辑处理 return [PSCustomObject]@{ ComputerName = $ComputerName LastBootTime = $lastBootTime UptimeDays = $uptimeDays UptimeDisplay = "Uptime: $uptimeDays days" } } catch { # 处理无法连接服务器的情况 Write-Warning "Failed to get uptime for $ComputerName : $_" return [PSCustomObject]@{ ComputerName = $ComputerName LastBootTime = $null UptimeDays = $null UptimeDisplay = "Failed to retrieve uptime" } } } # 遍历所有服务器,检查运行时间并发送告警邮件 foreach ($computer in $computers) { $computerName = $computer.Name # 从CSV对象中取出服务器名称 $uptimeInfo = Get-Uptime -ComputerName $computerName # 仅当成功获取运行时间,且运行天数超过阈值时发送邮件 if ($uptimeInfo.UptimeDays -ne $null -and $uptimeInfo.UptimeDays -ge $thresholdDays) { $subject = "Server $computerName needs to be rebooted" $body = @" Server: $computerName Last Boot Time: $($uptimeInfo.LastBootTime.ToString("yyyy-MM-dd HH:mm:ss")) Current Uptime: $($uptimeInfo.UptimeDisplay) This server has been running for more than $thresholdDays days and requires a reboot. "@ Send-MailMessage -From $sender -To $receiver -Subject $subject -Body $body -Priority High ` -DeliveryNotificationOption OnSuccess, OnFailure -SmtpServer $smtpServer } }
主要修改说明:
- 精准筛选服务器:明确指定Windows Server 2012 R2的版本(Standard/Datacenter),避免包含其他服务器版本。
- 重构
Get-Uptime函数:- 添加
-ComputerName参数,支持远程获取运行时间 - 使用
Get-CimInstance替代Get-WmiObject,性能更好且兼容性更强 - 返回自定义对象,包含计算机名、最后启动时间、运行天数等可用于逻辑判断的字段
- 增加异常处理,捕获无法连接服务器的情况并输出警告
- 添加
- 修正循环逻辑:
- 从CSV对象中提取服务器名称
$computer.Name - 调用
Get-Uptime并传入服务器名,获取运行时间信息 - 只有当运行天数有效且超过阈值时,才发送邮件
- 从CSV对象中提取服务器名称
- 优化邮件内容:包含服务器名、最后启动时间、具体运行天数,让告警信息更清晰
- 变量命名更清晰:比如把
$Threshold改为$thresholdDays,避免负数带来的混淆
内容的提问来源于stack exchange,提问作者Tomas
相关产品推荐
相关产品推荐

