使用PowerShell管理Windows服务:远程服务核查与启动流程实现问询
PowerShell 远程Windows服务状态核查与启动管理实现方案
1. 前置准备
- 执行脚本的账户需拥有远程计算机的WMI/CIM访问权限、服务管理权限,以及SMTP发件权限
- 提前整理OS服务白名单,用于区分OS相关服务和应用服务,也可通过服务可执行文件路径判断OS服务
- 提前配置SMTP服务器信息、服务与负责人的映射关系表
- 服务账户凭证建议使用
ConvertTo-SecureString加密存储,禁止明文写在脚本中
2. 完整实现脚本
# 入参配置 param( [Parameter(Mandatory=$true)] [string[]]$RemoteComputers, # OS服务白名单,可替换为路径判断逻辑 [Parameter(Mandatory=$true)] [string[]]$OsServiceWhitelist, # 应用服务和对应通知人映射,示例:@{"Tomcat" = @("appteam@example.com","serveradmin@example.com")} [Parameter(Mandatory=$true)] [hashtable]$ServiceOwnerMapping, [string]$SmtpServer = "你的SMTP服务器地址", [string]$SmtpSender = "alert@example.com", [int]$SmtpPort = 25 ) # 遍历所有远程计算机 foreach ($computer in $RemoteComputers) { # 先测试网络连通性 if (-not (Test-Connection -ComputerName $computer -Count 1 -Quiet -ErrorAction SilentlyContinue)) { Write-Warning "无法连通计算机 $computer,跳过处理" continue } # 获取所有设置为自动启动但当前停止的服务 try { $stoppedServices = Get-CimInstance -ClassName Win32_Service -ComputerName $computer -Filter "State = 'Stopped' AND StartMode = 'Auto'" -ErrorAction Stop } catch { Write-Warning "获取计算机 $computer 服务信息失败:$_" continue } # 逐个处理停止的服务 foreach ($service in $stoppedServices) { $serviceName = $service.Name $displayName = $service.DisplayName $startAccount = $service.StartName # 判断是否为OS相关服务 if ($OsServiceWhitelist -contains $serviceName) { # 核查所有依赖服务运行状态 $dependentServices = Get-CimAssociatedInstance -InputObject $service -ResultClassName Win32_Service -Association Win32_DependentService -Role Dependent -ErrorAction SilentlyContinue $depsAllRunning = $true foreach ($depService in $dependentServices) { if ($depService.State -ne "Running") { $depsAllRunning = $false Write-Host "服务 $serviceName 的依赖服务 $($depService.Name) 未运行,无法启动" break } } if (-not $depsAllRunning) { continue } # 判断启动账户类型 $systemAccounts = @("LocalSystem", "NT AUTHORITY\LocalService", "NT AUTHORITY\NetworkService") if ($systemAccounts -contains $startAccount) { # 本地系统类账户直接启动 try { Start-Service -InputObject (Get-Service -Name $serviceName -ComputerName $computer) -ErrorAction Stop Write-Host "已成功启动计算机 $computer 的OS服务 $serviceName" } catch { Write-Warning "启动计算机 $computer 的OS服务 $serviceName 失败:$_" } } else { # 自定义服务账户启动,需自行实现凭证获取逻辑,禁止明文存储密码 try { # 示例:从加密存储的凭证库获取对应服务账户的凭证 $serviceCred = Get-StoredCredential -ServiceAccount $startAccount $service | Invoke-CimMethod -MethodName StartService -Arguments @{ User = $serviceCred.UserName Password = $serviceCred.GetNetworkCredential().Password } -ErrorAction Stop Write-Host "已使用服务账户 $startAccount 启动计算机 $computer 的服务 $serviceName" } catch { Write-Warning "使用服务账户启动计算机 $computer 的服务 $serviceName 失败:$_" } } } else { # 非OS应用服务,发送告警邮件 if ($ServiceOwnerMapping.ContainsKey($serviceName)) { $recipients = $ServiceOwnerMapping[$serviceName] $mailSubject = "告警:服务器 $computer 应用服务 $displayName 已停止" $mailBody = @" 您好: 服务器 $computer 上的应用服务 $displayName(服务标识:$serviceName)当前处于停止状态,且配置为自动启动,请相关团队尽快核查处理。 "@ try { Send-MailMessage -From $SmtpSender -To $recipients -Subject $mailSubject -Body $mailBody -SmtpServer $SmtpServer -Port $SmtpPort -ErrorAction Stop Write-Host "已向 $($recipients -join ',') 发送服务停止告警邮件" } catch { Write-Warning "发送服务 $serviceName 告警邮件失败:$_" } } else { Write-Warning "未配置应用服务 $serviceName 的对应负责人,跳过邮件通知" } } } }
3. 补充说明
- 若不想维护OS服务白名单,可替换判断逻辑为
$isOsService = $service.PathName -like "C:\Windows\System32\*",通过可执行文件路径判断是否为系统服务,该方式简便但可能存在少量误判 - 若远程计算机开启了防火墙,需要开放WMI对应端口(TCP 5985为HTTP、5986为HTTPS)
- 可根据需求新增日志记录逻辑,将所有操作和异常写入本地日志文件,方便后续排查
内容的提问来源于stack exchange,提问作者abasum
相关产品推荐
相关产品推荐

