You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    }
}

主要修改说明:

  1. 精准筛选服务器:明确指定Windows Server 2012 R2的版本(Standard/Datacenter),避免包含其他服务器版本。
  2. 重构Get-Uptime函数:
    • 添加-ComputerName参数,支持远程获取运行时间
    • 使用Get-CimInstance替代Get-WmiObject,性能更好且兼容性更强
    • 返回自定义对象,包含计算机名、最后启动时间、运行天数等可用于逻辑判断的字段
    • 增加异常处理,捕获无法连接服务器的情况并输出警告
  3. 修正循环逻辑:
    • 从CSV对象中提取服务器名称$computer.Name
    • 调用Get-Uptime并传入服务器名,获取运行时间信息
    • 只有当运行天数有效且超过阈值时,才发送邮件
  4. 优化邮件内容:包含服务器名、最后启动时间、具体运行天数,让告警信息更清晰
  5. 变量命名更清晰:比如把$Threshold改为$thresholdDays,避免负数带来的混淆

内容的提问来源于stack exchange,提问作者Tomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:54:09