PowerShell Remoting会话超时配置与故障主机处理方案咨询
PowerShell 批量服务器WinRM连接管理脚本优化方案
原有脚本核心缺陷
- 整批传入服务器列表创建PSSession时,连接失败的主机会被直接静默丢弃,无任何失败记录
Wait-Job设置的超时是针对整批任务的粗粒度控制,超时后未完成的单主机任务直接终止,无法追溯具体哪台主机超时- 无并行度控制,服务器规模达到数百台以上时容易触发本机端口耗尽、目标端WinRM连接限流问题
- 无实时进度反馈,大规模执行时无法判断当前运行状态
优化后实现脚本
# 基础配置参数 $serverList = @('server1','server2','server3') $connectionTimeout = 30 # 单台主机WinRM连接超时,单位秒 $operationTimeout = 30 # 单台主机远程命令执行超时,单位秒 $maxParallel = 50 # 最大并行执行数,建议根据网络环境调整为30~100区间 $cred = Get-Credential $successList = [System.Collections.Concurrent.ConcurrentBag[psobject]]::new() $failedList = [System.Collections.Concurrent.ConcurrentBag[psobject]]::new() # 配置WinRM会话超时规则 $sessionOption = New-PSSessionOption -OpenTimeout $($connectionTimeout*1000) ` -OperationTimeout $($operationTimeout*1000) ` -CancelTimeout $($connectionTimeout*1000) ` -NoMachineProfile # 并行遍历处理所有主机 $serverList | ForEach-Object -Parallel { $targetServer = $_ $success = $using:successList $failed = $using:failedList $opt = $using:sessionOption $credential = $using:cred Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 开始处理主机: $targetServer" try { # 单台主机单独建立会话,捕获连接阶段异常 $session = New-PSSession -ComputerName $targetServer -Credential $credential -SessionOption $opt -ErrorAction Stop try { # 执行远程命令,捕获执行阶段异常 $result = Invoke-Command -Session $session -ScriptBlock {[System.Net.Dns]::GetHostName()} -ErrorAction Stop $success.Add(@{ ServerName = $targetServer ReturnValue = $result Status = "Success" }) Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 主机 $targetServer 执行成功" } catch { $failed.Add(@{ ServerName = $targetServer ErrorMsg = $_.Exception.Message Status = "CommandExecuteFailed" }) Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 主机 $targetServer 命令执行失败: $($_.Exception.Message)" } finally { # 主动释放会话资源,避免残留占用 if ($session) { Remove-PSSession $session -ErrorAction SilentlyContinue } } } catch { $failed.Add(@{ ServerName = $targetServer ErrorMsg = $_.Exception.Message Status = "ConnectionFailed" }) Write-Host "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 主机 $targetServer 连接失败: $($_.Exception.Message)" } } -ThrottleLimit $maxParallel # 成功结果写入数据库 foreach ($item in $successList) { $serverName = $item.ServerName Invoke-Sqlcmd -ServerInstance "servername" -Query "insert into mytable select '$serverName'" } # 输出执行统计 Write-Host "`n执行完成,统计信息:" Write-Host "总主机数: $($serverList.Count)" Write-Host "执行成功: $($successList.Count)" Write-Host "执行失败: $($failedList.Count)" # 失败列表导出为CSV供排查使用,也可按需写入单独的异常日志表 $failedList | Export-Csv -Path ".\WinRM_Execute_Failed_$(Get-Date -Format 'yyyyMMddHHmmss').csv" -NoTypeInformation
关键配置说明
- 并行度
$maxParallel不建议设置超过100,过高并发会导致本机临时端口耗尽,反而降低整体执行效率,50左右的并行度处理数千台服务器总耗时基本可控制在10分钟以内 - 会话选项单独配置了连接超时、执行超时、取消超时三个阈值,所有超时都是单主机粒度,不会出现整批任务超时导致的结果丢失问题
- 采用线程安全的
ConcurrentBag存储成功、失败结果,避免多线程并行下的集合写入冲突 - 异常分类覆盖连接失败(主机离线、WinRM未启动、权限不足、网络不通)、命令执行失败两类场景,失败记录自带错误原因,无需事后做差集比对
- 每台主机处理开始、成功、失败都有带时间戳的日志输出,可实时观测执行进度
- 脚本默认使用PowerShell 7+内置的
ForEach-Object -Parallel实现并行,如果需要兼容PowerShell 5.1,可将并行块替换为Start-ThreadJob实现,核心的异常捕获、超时配置逻辑无需修改
内容的提问来源于stack exchange,提问作者learner
相关产品推荐
相关产品推荐

