自动化实践(7.25):把 PsTools 接入 PowerShell / 批处理 / Ansible
自动化实践(7.25):把 PsTools 接入 PowerShell / 批处理 / Ansible
- 自动化实践(7.25):把 PsTools 接入 PowerShell / 批处理 / Ansible
- 1)前置与安全基线
- 2)PowerShell 集成:模块化、并发与结构化日志
- 2.1 目录与凭据约定
- 2.2 并发执行(PowerShell 7 推荐)
- 2.3 封装常用动作(示例库)
- 3)批处理(.bat/.cmd):轻依赖、可落地
- 3.1 基础模板(带日志与退出码)
- 3.2 并发小技巧(慎用)
- 4)Ansible 集成:把 PsTools 纳入编排
- 4.1 示例清单
- 5)流水线与计划任务:把脚本放进 CI/CD
- 5.1 Windows 计划任务(每日清点)
- 5.2 Jenkins/GitLab CI(示意)
- 6)幂等与回滚策略
- 7)故障快修清单(执行层面)
- 8)推荐仓库结构(可直接落地)
- 结语
自动化实践(7.25):把 PsTools 接入 PowerShell / 批处理 / Ansible
目标:把 PsExec / PsInfo / PsService / PsLogList 等常用 PsTools,系统化封装进 PowerShell 脚本、批处理(.bat/.cmd)与 Ansible 流水线,实现可复用、可审计、可回滚的批量运维与应急流程。
1)前置与安全基线
- 网络:目标机放行
445/TCP(SMB)、135/TCP(RPC)与 动态 RPC 端口。 - 权限:使用域受控账号或最小权限服务账号;工作组注意 UAC 远程限制。
- 共享:
ADMIN$ / C$未禁用。 - 签名/EDR:将 PsTools 目录与
PSEXESVC行为加入白名单。 - 时间/DNS:NTP 同步、DNS 正常解析(Kerberos/NTLM 影响大)。
- 脚本规范:全流程 日志留痕 + 幂等检查 + 失败回滚。
2)PowerShell 集成:模块化、并发与结构化日志
2.1 目录与凭据约定
C:\Ops\PsTools\ # 放置 PsTools
C:\Ops\Scripts\ # 自建脚本目录
C:\Ops\Logs\ # 结构化日志输出
C:\Ops\hosts.txt # 主机清单(逐行一个主机)
凭据建议:用
Get-Credential交互获取,或安全地从 Windows 凭据管理器/密管(Vault)读取。
# C:\Ops\Scripts\New-PsExecSession.ps1
param([Parameter(Mandatory)] [string]$Computer,[Parameter()] [pscredential]$Credential,[Parameter()] [string]$Command = 'hostname',[switch]$System, [switch]$High, [int]$Timeout = 20,[string]$WorkDir, [string[]]$CpuAffinity
)$psi = @{FilePath = 'psexec.exe'ArgumentList = @("\\$Computer")NoNewWindow = $trueWait = $true
}
if ($Credential) { $psi.ArgumentList += @('-u', $Credential.UserName, '-p', $Credential.GetNetworkCredential().Password) }
if ($System) { $psi.ArgumentList += '-s' }
if ($High) { $psi.ArgumentList += '-h' }
if ($Timeout) { $psi.ArgumentList += @('-n', $Timeout) }
if ($WorkDir) { $psi.ArgumentList += @('-w', $WorkDir) }
if ($CpuAffinity){ $psi.ArgumentList += @('-a', ($CpuAffinity -join ',')) }
$psi.ArgumentList += @('-accepteula','-nobanner')
$psi.ArgumentList += $Commandtry{$sw = [Diagnostics.Stopwatch]::StartNew()$p = Start-Process @psi -PassThru -RedirectStandardOutput 'stdout.tmp' -RedirectStandardError 'stderr.tmp'$code = $p.ExitCode$out = Get-Content .\stdout.tmp -Raw$err = Get-Content .\stderr.tmp -RawRemove-Item .\stdout.tmp, .\stderr.tmp -ErrorAction SilentlyContinue[pscustomobject]@{Computer = $Computer; Command = $Command; ExitCode = $codeDurationMs = $sw.ElapsedMilliseconds; StdOut = $out; StdErr = $err; Time = (Get-Date)}
}
catch{[pscustomobject]@{Computer = $Computer; Command = $Command; ExitCode = -1DurationMs = 0; StdOut = ''; StdErr = $_ | Out-String; Time = (Get-Date)}
}
2.2 并发执行(PowerShell 7 推荐)
# C:\Ops\Scripts\Invoke-PsToolsBatch.ps1
$hosts = Get-Content 'C:\Ops\hosts.txt' | ? {$_ -and $_ -notmatch '^\s*#'}
$cred = Get-Credential -Message '输入用于 PsExec 的域凭据(最小权限)'$results = $hosts | ForEach-Object -Parallel {Import-Module -Name 'C:\Ops\Scripts\New-PsExecSession.ps1' -Force# 示例:查询磁盘信息(可以换成任意命令,如:ipconfig /all). 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $_ -Credential $using:cred -High `-Command 'cmd /c wmic logicaldisk get caption,freespace,size /format:table'
} -ThrottleLimit 20# 结构化日志(JsonLines)
$log = 'C:\Ops\Logs\psexec-' + (Get-Date -UFormat %Y%m%d-%H%M%S) + '.jsonl'
$results | ForEach-Object { $_ | ConvertTo-Json -Compress | Out-File -FilePath $log -Append -Encoding utf8 }# 失败告警粗筛
$fail = $results | ? { $_.ExitCode -ne 0 }
if($fail){ Write-Warning "失败 ${($fail.Count)} 台,详情:$log" } else { Write-Host '全部成功' -ForegroundColor Green }
2.3 封装常用动作(示例库)
- 服务健康三连(查询→依赖→重启)
function Invoke-ServiceHealth {param([string]$Computer, [string]$Service, [pscredential]$Credential). 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $Computer -Credential $Credential -High -Command "psservice query $Service". 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $Computer -Credential $Credential -High -Command "psservice depend $Service". 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $Computer -Credential $Credential -High -Command "psservice restart $Service"
}
- 冻结→抓转储→恢复(针对 CPU 飙高的 w3wp)
function Invoke-FreezeDumpResume {param([string]$Computer,[pscredential]$Credential,[string]$Proc='w3wp',[string]$Dump='C:\Dumps\w3wp.dmp'). 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $Computer -Credential $Credential -High -Command "pssuspend $Proc". 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $Computer -Credential $Credential -High -Command "procdump -accepteula -ma $Proc $Dump". 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $Computer -Credential $Credential -High -Command "pssuspend -r $Proc"
}
- 拉事件日志(近 2 天关键错误)
. 'C:\Ops\Scripts\New-PsExecSession.ps1' -Computer $Computer -Credential $cred -Command `'psloglist -s -d 2 -i 41,1001 System'
幂等性建议:
- 起服务前先
query判断状态;- 改配置前先
get对比;- 大规模批量先试点(1~3 台)再并发放量。
3)批处理(.bat/.cmd):轻依赖、可落地
3.1 基础模板(带日志与退出码)
@echo off
setlocal enabledelayedexpansion
set P= C:\Ops\PsTools
set H= C:\Ops\hosts.txt
set LOG=C:\Ops\Logs\batch-%date:~0,10%_%time:~0,8%.logfor /f "usebackq delims=" %%h in ("%H%") do (if not "%%h"=="" (echo ===== %%h ===== >> "%LOG%""%P%\psexec.exe" \\%%h -accepteula -nobanner ipconfig /all >> "%LOG%" 2>&1set EC=!errorlevel!echo ExitCode=!EC! >> "%LOG%"if not "!EC!"=="0" echo [WARN] %%h 失败(!EC!))
)
echo 完成:%LOG%
endlocal
3.2 并发小技巧(慎用)
:: 同时对 3 台发起
start "" cmd /c "%P%\psexec.exe \\PC-001 hostname & echo EC=!errorlevel! >> %LOG%"
start "" cmd /c "%P%\psexec.exe \\PC-002 hostname & echo EC=!errorlevel! >> %LOG%"
start "" cmd /c "%P%\psexec.exe \\PC-003 hostname & echo EC=!errorlevel! >> %LOG%"
注意:批处理缺少原生结构化日志与健壮错误处理,建议用 PowerShell 主导,批处理当“引导器”。
4)Ansible 集成:把 PsTools 纳入编排
思路:Ansible 控制端为 Windows(或 WSL/代理到 Windows),PsTools 安装在控制端。Playbook 通过
delegate_to: localhost在控制端循环调用 PsTools 远程连接各 Windows 主机。
4.1 示例清单
inventory.ini
[win]
PC-001
PC-002
SRV-APP
group_vars/win.yml
psexec_path: 'C:\\Ops\\PsTools\\psexec.exe'
ops_user: 'CORP\\opsuser'
ops_pass: '{{ vault_ops_pass }}' # 建议用 Ansible Vault
playbook:批量查询磁盘信息
---
- name: PsTools orchestration from Windows control nodehosts: wingather_facts: notasks:- name: Run PsExec disk queryansible.windows.win_command: >"{{ psexec_path }}""\\{{ inventory_hostname }}"-u {{ ops_user }} -p {{ ops_pass }}-accepteula -nobannercmd /c wmic logicaldisk get caption,freespace,size /format:tableregister: outdelegate_to: localhost- name: Save raw output per hostansible.builtin.copy:dest: "C:/Ops/Logs/{{ inventory_hostname }}-disk.txt"content: "{{ out.stdout }}"delegate_to: localhost
更推荐:Ansible 访问 Windows 目标,首选 WinRM +
ansible.windows.*原生模块(如win_service、win_command、win_eventlog、win_package)。
在必须跨越网络策略/EDR 限制时,再用 PsTools 作为“后备通道”。
5)流水线与计划任务:把脚本放进 CI/CD
5.1 Windows 计划任务(每日清点)
$act = New-ScheduledTaskAction -Execute 'pwsh.exe' -Argument '-File C:\Ops\Scripts\Invoke-PsToolsBatch.ps1'
$trg = New-ScheduledTaskTrigger -Daily -At 03:00
Register-ScheduledTask -Action $act -Trigger $trg -TaskName 'Nightly-PsTools-Inventory' -User 'CORP\svc_ops' -Password (Read-Host -AsSecureString)
5.2 Jenkins/GitLab CI(示意)
- Jenkins:Windows Node 执行
pwsh -File C:\Ops\Scripts\Invoke-PsToolsBatch.ps1,归档C:\Ops\Logs\*.jsonl。 - GitLab CI:Windows Runner 执行同上,产物落盘归档。
6)幂等与回滚策略
- 服务/配置:变更前
psservice query快照 → 变更 → 失败时按快照回退。 - 关机/重启:
psloglist抽取关键事件留痕;计划窗口+沟通提示。 - 杀进程:优先
pssuspend(冻结)+ 取证(procdump)+ 再pskill。 - 大批量:分批灰度(10% → 30% → 100%),提高成功率与可控性。
7)故障快修清单(执行层面)
| 现象 | 快速定位 | 处置 |
|---|---|---|
Access is denied | 权限/UAC/ADMIN$ | 管理员凭据;启用 ADMIN$;必要时工作组放宽 UAC 远程限制 |
| RPC/SMB 不通 | Test-NetConnection -ComputerName HOST -Port 445/135 | 放行端口、防火墙策略核对 |
Logon failure | 凭据错误/锁定 | 校验域名 + 口令;解锁策略 |
PSEXESVC 启动失败 | EDR/白名单 | 协同安全放行 PsExec 行为 |
| 回显阻塞 | 交互/超时 | 加 -d 后台、或 -i 指定会话、-n 超时 |
8)推荐仓库结构(可直接落地)
Ops-Automation/
├─ PsTools/ # 官方二进制
├─ Scripts/
│ ├─ New-PsExecSession.ps1 # 核心封装
│ ├─ Invoke-PsToolsBatch.ps1 # 并发批量执行
│ ├─ Recipes/ # 业务动作库(服务健康、转储、日志收集…)
├─ Logs/ # JsonLines/CSV/原始输出
├─ hosts.txt # 资产清单
└─ README.md # 使用说明、安全提示、回滚流程
结语
把 PsTools 接入 PowerShell/批处理/Ansible 后,你就拥有了一个 轻依赖、跨版本、可编排 的 Windows 批量运维“工具台”。建议从 PowerShell 封装 + 并发执行 + 结构化日志 起步,逐步沉淀“动作库”(服务治理、事件采集、取证流程),再上 CI/计划任务,实现 标准化、自动化与可审计 的闭环。
下一篇(7.26):安全与合规——最小权限、日志审计与取证要点,保证“能用更要安全可查”。
