PowerShell Remoting(WinRM)入门 ── 批量管理多台 Windows

· · PowerShell, Windows, WinRM, 远程管理, 自动化, 运维改善, 安全, 脚本

“Windows Update 之后,帮我看看 20 台服务器是不是都正常启动了”“想请你查一下所有据点的 PC 上那个设置值现在是什么状态”── 面对这类请求,你是不是还在用远程桌面一台台登录确认?哪怕每台只要 3 分钟,20 台也要花 1 小时。人一旦重复劳动就会疲于应付,也容易漏检。

PowerShell Remoting 正是把“对多台机器执行同一操作”这件事,变成一条命令就能完成的机制。Windows 标准内置了名为 WinRM 的远程管理基础设施,在 Windows Server 上默认已经启用。也就是说,在很多现场,今天就可以直接使用,不需要安装任何额外软件。尽管如此,“总觉得有点可怕”“在工作组环境里连不上就放弃了”这样的声音却很常见。

本文面向中小企业的信息系统部门与运维人员,梳理 Remoting 的原理(哪些内容在哪个端口上运行、谁可以连接)、通过 Invoke-Command 实现批量执行、域环境与工作组环境的差异、second hop 问题这类著名的陷阱、“连不上”时的排查方法,以及“从只读操作开始”的安全运维方式。

前提环境

  • 被连接的一方:在 Windows Server 2012 及以后的 Windows Server 上,PowerShell Remoting 默认已启用1 客户端版 Windows 的 WinRM 服务默认为禁用状态,需要通过 Enable-PSRemoting 启用。1
  • 发起连接的一方:本文的命令示例只使用 Windows PowerShell 5.1 与 PowerShell 7 两者都具备的 cmdlet。不过 WSMan: 驱动器与 Test-WSMan 等 WS-Management 相关 cmdlet 仅在 Windows 上的 PowerShell 中可用(Linux、macOS 上的 PowerShell 请使用第 6 章介绍的基于 SSH 的 Remoting)。2
  • 权限:无论是修改远程一侧的设置(Enable-PSRemoting、TrustedHosts、监听器),都需要以管理员身份启动的 PowerShell1
  • 网络:本文同时涉及域环境与工作组环境,两者的差异汇总在第 3 章。

1. 先说结论

  • PowerShell Remoting 运行在 WinRM(WS-Management 实现)之上,默认使用 HTTP 5985 / HTTPS 5986 端口。即使走 HTTP,通信内容也会由认证协议施加消息级加密。3
  • 默认情况下只有远程一侧 Administrators 组的成员才能连接,会话在所连接用户的上下文中运行。并不是“启用 Remoting 后谁都能进来”。3
  • Enable-PSRemoting 所做的事情有明确定义。包括启动 WinRM 服务并设为自动启动、创建监听器、开启防火墙例外、启用会话配置。Windows Server 上默认启用,客户端 Windows 需要手动启用。43
  • 域环境下 Kerberos 可以直接工作,工作组则需要 NTLM + TrustedHosts + 显式凭据。TrustedHosts 并非“信任对方”的设置,而是“放弃验证对方身份的列表”,因此应控制在最小范围。53
  • 批量执行用 Invoke-Command,交互式操作用 Enter-PSSession,需要保持状态并反复使用则用 New-PSSession。Invoke-Command 默认最多并行处理 32 台。67
  • 从远程返回的是反序列化后的对象,不带方法。操作应在 ScriptBlock 内部(远程一侧)完结,本地只做结果的汇总。87
  • 无法从连接目标进一步访问另一台服务器(second hop 问题)。这不是缺陷,而是“不把凭据发送到远程”这一安全设计的必然结果。应对方法有多种,需按需求选择。9
  • PowerShell 7 还可以使用基于 SSH 的 Remoting。在需要与 Linux 互相管理,或者不想开放 WinRM 的环境中,可以作为一个选项。2

2. 原理 ── 在 WinRM 之上,谁、如何进入

PowerShell Remoting 的基础是 WinRM(Windows Remote Management)。WinRM 是标准协议 WS-Management 的微软实现,PowerShell Remoting 正是通过这个 WinRM,把命令发送到远程计算机上的 PowerShell。3 通信默认使用 HTTP 5985 端口、HTTPS 5986 端口。35

有人会担心“HTTP 是不是明文传输”,但初次认证完成后 WinRM 会对通信进行加密。HTTPS 下用的是 TLS,HTTP 下则是由认证协议协商出的消息级加密(若是 Kerberos,在现代环境中为 AES-256)。3 另外,默认情况下只有远程一侧 Administrators 组的成员才能连接,会话在所连接用户的上下文中执行,因此文件与注册表的访问控制会照常适用。3

接受连接一方的准备工作是 Enable-PSRemoting。这个 cmdlet 会做什么,官方文档中有明确列举。它在内部执行 Set-WSManQuickConfig,完成 (1) 启动 WinRM 服务、(2) 将启动类型改为自动、(3) 创建可在任意 IP 地址上接收请求的监听器、(4) 启用 WS-Management 通信所需的防火墙例外、(5) 创建并启用会话配置(终结点)及远程访问权限之后,重新启动 WinRM 服务。4

# 在被连接的一方,用管理员权限的 PowerShell 只需执行一次
# (Windows Server 默认已启用,通常不需要执行;客户端 Windows 上需要)
Enable-PSRemoting

# 从发起连接的一方进行连通性确认 ── 这个通了,说明 Remoting 的基础已经就绪
Test-WSMan -ComputerName sv-app01

有两个不了解就容易踩坑的地方。第一是防火墙。在服务器版 Windows 上,Enable-PSRemoting 会针对公用网络也创建“仅允许来自同一子网”的规则;而在客户端版上,如果网络配置文件是公用网络,启用操作本身就会报错(这是把测试机接入公司 Wi-Fi 时的经典问题)。这种情况下可以加上 -SkipNetworkProfileCheck,或者把网络配置文件改为专用网络。4

第二是 PowerShell 版本与终结点的关系。Enable-PSRemoting 会为“执行它所使用的那个 PowerShell 版本”配置对应的终结点。即使在 PowerShell 7 中执行,也不会影响 Windows PowerShell 5.1 的终结点,反过来也一样。4 而且即便从 PowerShell 7 发起连接,默认情况下也会使用 Windows PowerShell 5.1 已有的终结点(Microsoft.PowerShell)10,所以“本地明明是 7,远程实际运行的却是 5.1”是很常见的情况。请养成在远程一侧显示 $PSVersionTable 确认版本的习惯。

3. 域与工作组 ── 认证的分水岭在这里

Remoting 入门时最容易卡住的地方,不是命令的语法,而是认证。所需的准备工作会因环境而异。

项目 域环境 工作组环境
认证协议 Kerberos(具备相互认证)5 NTLM(不验证服务器身份)5
额外设置 原则上不需要,直接用计算机名连接即可 需要在发起连接的一方登记 TrustedHosts5
凭据 直接使用当前登录用户 通常需要用 -Credential 显式指定
按 IP 地址连接 需要登记 TrustedHosts 或使用 HTTPS11 同左

如果是域环境,Kerberos 的相互认证(客户端与服务器互相验证对方身份)会生效,Invoke-Command -ComputerName sv-app01 { ... } 可以直接连通。工作组环境无法使用 Kerberos5,因此需要在发起连接的一方把目标主机登记到 TrustedHosts。

# 在发起连接的一方,用以管理员身份启动的 PowerShell 执行(仅工作组环境需要)
# 修改 TrustedHosts 同样需要管理员权限。已有的值会被覆盖,所以先确认当前值
Get-Item WSMan:\localhost\Client\TrustedHosts

# 只追加必需的最少主机名。不加 -Concatenate 会清空已有登记。应避免用 '*' 全部放行
Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'sv-app01,sv-app02' -Concatenate

# 显式指定凭据进行连接测试
$cred = Get-Credential sv-app01\Administrator
Invoke-Command -ComputerName sv-app01 -Credential $cred -ScriptBlock { hostname }

这里 TrustedHosts 的含义很关键。官方文档明确写道:“登记在 TrustedHosts 中的计算机不会被身份验证,客户端有可能因此把凭据发送出去”。5 也就是说,与其说是“可信对象的列表”,不如说是“放弃验证服务器身份这一警告的抑制列表”3 如果 DNS 或 ARP 被篡改,就存在把管理员凭据发送给伪造服务器的风险。因此应避免通配符登记,只登记固定的主机名或 IP,并且数量要控制到最少。对于要长期使用的工作组环境或 DMZ,准备证书并构建 HTTPS(5986) 监听器,才是更稳妥的实务判断。3

另外需要留意,如果你开始把 Get-Credential 取得的凭据以明文形式写进脚本,那就是一个危险信号了。关于凭据的保存与传递定式,已整理在同期发布的《安全地在 PowerShell 中处理凭据》中。

在工作组环境里,还有一点容易被忽略,那就是密码是否为空。如果目标账户没有设置密码(空密码),远程命令将无法执行。1

3.1. 构建 HTTPS(5986) 监听器

如果要在工作组或 DMZ 环境中长期使用,就不能只靠 TrustedHosts 应付,而应搭建 HTTPS 监听器。步骤大致如下。12

  1. 准备用于服务器身份验证的证书。要求包括“是本地计算机的服务器身份验证证书”“CN(或使用者备用名称)与主机名一致”“在有效期内、未被吊销、且不是自签名证书”。如果有企业内部 CA,可以通过 https://<证书颁发机构服务器>/certsrv 的 Web 注册页面申请。
  2. 将证书导入本地计算机的个人存储区。可以通过证书管理单元(在 MMC 中添加“证书”,并在向导中选择“计算机账户”)的 证书(本地计算机) > 个人 > 证书 进行确认。
  3. 创建 HTTPS 监听器。最简单的方式是执行下面这一行命令。

     winrm quickconfig -transport:https
    

    如果有多张证书,想明确指定使用哪一张,也可以用 PowerShell 通过指纹来创建(指纹可以用 Get-ChildItem Cert:\LocalMachine\My 确认)。

     New-Item -Path WSMan:\localhost\Listener -Address * -Transport HTTPS -CertificateThumbPrint '证书指纹' -Force
    
  4. 在防火墙上允许 TCP 5986 的入站连接。Enable-PSRemoting 创建的规则是针对 HTTP(5985) 的,HTTPS 需要另行开放。
  5. 确认监听器是否已创建。

     winrm enumerate winrm/config/listener
    

发起连接的一方只需加上 -UseSSL 即可。

Invoke-Command -ComputerName sv-app01.example.local -UseSSL -Credential $cred -ScriptBlock { hostname }

如果证书不满足条件,创建监听器时会出现“Cannot create a WinRM listener on HTTPS because this machine does not have an appropriate certificate.(错误代码 -2144108267 / 0x80338115)”这样的错误。这时应依次检查证书的有效期、颁发对象是否与主机名一致、增强型密钥用法中是否包含“服务器身份验证”、以及证书路径是否有效。12

4. Invoke-Command ── “对 20 台机器做同一件事”的基本用法

Remoting 的主角是 Invoke-Command。把多台机器传给 -ComputerName,把“要在远程做的事”写进 -ScriptBlock。6 从只读的盘点操作开始,是安全运维的铁律。

$servers = 'sv-app01', 'sv-app02', 'sv-db01'

# 打补丁后的状态确认 ── 只是读取,可以放心执行
$result = Invoke-Command -ComputerName $servers -ScriptBlock {
    # 这个代码块内部是在“远程一侧”执行的
    $os = Get-CimInstance Win32_OperatingSystem
    [PSCustomObject]@{
        LastBoot   = $os.LastBootUpTime                      # 是否已完成重启
        SpoolerRun = (Get-Service -Name Spooler).Status      # 业务所需服务的状态
        FreeGB     = [math]::Round((Get-PSDrive C).Free / 1GB, 1)
    }
}

# 哪个结果来自哪台服务器,可以通过 PSComputerName 分辨
$result | Sort-Object PSComputerName |
    Select-Object PSComputerName, LastBoot, SpoolerRun, FreeGB |
    Export-Csv -Path .\patch-check.csv -NoTypeInformation -Encoding UTF8

对每台服务器的连接是并行处理的,默认的并发数(ThrottleLimit)为 32。7 结果会按到达顺序混杂返回,因此需要像上面的示例那样,用自动附加的 PSComputerName 属性重新排序。8

4.1. 变量的传递方式 ── $using: 与 -ArgumentList

ScriptBlock 是在远程执行的,本地变量并不会直接可见。把值带过去的方法有两种。13

$threshold = (Get-Date).AddDays(-30)

# 方法 1: $using: ── 把调用方变量的“值的副本”嵌入到远程一侧
Invoke-Command -ComputerName $servers -ScriptBlock {
    (Get-ChildItem 'D:\AppLogs' -Filter *.log |
        Where-Object LastWriteTime -lt $using:threshold).Count
}

# 方法 2: -ArgumentList ── 在 ScriptBlock 一侧的 param 中接收(参数较多时更易读)
Invoke-Command -ComputerName $servers -ScriptBlock {
    param($limit)
    (Get-ChildItem 'D:\AppLogs' -Filter *.log |
        Where-Object LastWriteTime -lt $limit).Count
} -ArgumentList $threshold

通过 $using: 传递的值,在远程一侧是独立的副本。即使在远程一侧改写,本地的变量也不会随之改变。13

4.2. 返回的是“快照”── 反序列化后的对象

这是使用 Remoting 时最先让人困惑的地方。由于活的 .NET 对象无法跨越网络,远程的输出会被序列化为 XML(CLIXML) 后发送,并在本地还原为反序列化后的对象。这只是执行那一刻各属性的快照,不带方法。87

例如,在本地接收 Get-Service 的结果后,无法对它调用 .Stop()。如果想停止服务,就应该在 ScriptBlock 内部执行 Stop-Service ── 也就是说,正确的做法是把角色分开:操作在远程一侧完结,带回本地的只是用于报告的数据。属性的筛选、整理、转成 CSV 等本地加工,可以像平时使用 PowerShell 那样进行(这部分的基本操作可以参考《PowerShell 命令基础》)。

5. Enter-PSSession 与 New-PSSession ── 交互与复用

想对单台机器做交互式排查时,用 Enter-PSSession。提示符会变成 [sv-app01]: PS>,输入的命令会在远程执行,用 exit 退出。11 与远程桌面不同,它不会带来画面,但如果只是“看看日志”“确认一下设置”,这种方式反而更快,连接同样要求是 Administrators 组的成员。11

另一方面,每次用 -ComputerName 调用 Invoke-Command,都会重新建立并关闭连接。14 如果要向同一批服务器多次发送命令,用 New-PSSession 创建持久会话并反复使用会更快,而且远程一侧的变量和状态会在多条命令之间保持。1415

# 只建立一次会话,通过变量 $s 反复使用
$s = New-PSSession -ComputerName $servers
try {
    # 第 1 次: 在远程一侧创建变量 $hotfix
    Invoke-Command -Session $s { $hotfix = Get-HotFix | Sort-Object InstalledOn -Descending }

    # 第 2 次: 前一条命令创建的变量可以直接使用(会话保持了状态)
    Invoke-Command -Session $s { $hotfix | Select-Object -First 5 }
}
finally {
    # 即使中途出错或被中断,也一定要释放(回收远程一侧的资源)
    Remove-PSSession $s
}

6. 陷阱 ── second hop,以及 SSH 这一选项

6.1. second hop(双跳)问题

这是现场最有名的一道墙。从本地 PC(A) 通过 Remoting 进入服务器 B,再从 B 内部尝试读取 \\fs01\share(服务器 C)时会被拒绝访问。默认的 Kerberos/NTLM 认证是一种不把凭据本身发送给 B 的安全方式,因此 B 无法代表你去向 C 认证。39

应对方法有多种,官方文档按推荐顺序做了整理。9 只列出主要几种如下。

  • 在 ScriptBlock 内显式传递凭据 ── 不需要修改服务器一侧的配置,最为方便。通过 $using:cred 把凭据传给内层的 Invoke-Command。9 但由于凭据对象会传到中继服务器(B)的会话中,一旦 B 被攻破,传过去的凭据也可能被利用,因此前提是只有在完全信任 B 的情况下才能使用,而且传递的账户也应是在 C 一侧仅拥有最小必要权限的专用账户
  • 基于资源的 Kerberos 约束委派 ── 不保存凭据,而是在被访问方(C)一侧配置“接受来自 B 的委派”。无需域管理员权限即可配置,是安全性与配置复杂度之间平衡较好的一个选项。9
  • JEA(Just Enough Administration) ── 一种 PowerShell 机制,通过准备一个只允许“该业务所需命令”的专用终结点来收窄权限。如果配置为使用虚拟账户,使用者就能以非管理员凭据连接,同时只能执行被允许的管理命令。16 在 second hop 的场景下,这可以理解为:不给中继服务器(B)分发管理员凭据,而是开一个只能执行包含访问 C 在内的定型作业的窗口。9
  • CredSSP ── 凭据会被缓存到远程服务器上,一旦该服务器被攻破,凭据也会随之被窃取。默认为禁用状态,启用应仅限于最值得信赖的环境。9
# 最简便的规避方式: 通过 $using:cred 把凭据传给内层的 Invoke-Command
# 外层(sv-app01)用自己的凭据连接,传给内层的则是在 fs01 一侧
# 只拥有最小必要权限的专用账户(不分发管理员凭据)
#
# 【前提】内层调用是"从 sv-app01 到 fs01 的一次新的 Remoting 连接",因此需要:
#   1. sv-app01 能够到达 fs01 的 WinRM(5985/5986)
#   2. 若是工作组环境或按 IP 地址指定,需要 sv-app01 一侧的 TrustedHosts 中已登记 fs01
#   3. $cred 是在 fs01 上有效的账户(域账户,或 fs01 的本地账户)
# 不确认这些就直接照搬使用,很容易出现"本地能跑,但从中继服务器执行就失败"的情况
$cred = Get-Credential CONTOSO\svc-fileread
Invoke-Command -ComputerName sv-app01 -ScriptBlock {
    Invoke-Command -ComputerName fs01 -Credential $using:cred -ScriptBlock { hostname }
}

与其“经由 B 去读 C 上的文件”,不如先考虑能否改为从本地直接对 C 执行 Invoke-Command ── 这才是成本最低的解决方案。

6.2. PowerShell 7 还可以选择基于 SSH 的 Remoting

从 PowerShell 6.0 开始,可以用 SSH 代替 WinRM 进行 Remoting 连接。Invoke-Command / Enter-PSSession / New-PSSession 都新增了 -HostName、-UserName、-KeyFilePath 这一组 SSH 专用参数集,可以实现跨 Windows 和 Linux 的管理。2 连接目标需要有 SSH 服务器以及 PowerShell 的 SSH 子系统配置,目前尚不支持 WinRM 方式所具备的终结点配置或 JEA 这类功能。2 在混有 Linux 服务器的环境,或者想统一采用密钥认证运维的场合,可以把它记在心里作为一个选项。Windows PowerShell 5.1 没有这项功能,如果需要用到,也可以成为迁移的一个动机(关于两者差异的全貌,可参考同期发布的《Windows PowerShell 5.1 与 PowerShell 7 的差异》)。

顺带一提,Remoting 的会话不会像 RDP 那样创建桌面会话。“在远程运行”这句话在两者之间的含义有何不同,已经在《如何理解 Windows 的会话隔离》中整理过。

7. 连不上时的排查方法

初学者放弃 Remoting 的原因,大多是“连不上”。而且失败的原因,可能出在服务、监听器、防火墙、认证、权限中的任何一个环节。按从上游到下游的顺序逐一排除,就不会迷失方向。

7.1. 排查顺序

  1. 先看连通性。从发起连接的一方执行 Test-WSMan -ComputerName sv-app01。如果这一步能通,说明 WinRM 服务、监听器、防火墙都还活着(认证与权限的问题仍可能存在)。如果不通,进入下一步。
  2. 看远程一侧的 WinRM 服务是否在运行。服务器版的启动类型是自动,但客户端版 Windows 的 WinRM 服务默认是禁用的1Get-Service WinRM 确认,如有需要就执行 Enable-PSRemoting(一并完成服务启动、自动启动化、监听器、防火墙例外、会话配置)。4
  3. 看监听器是否在监听。在远程一侧执行下面的命令,确认 ListeningOn 是否为空。如果为空,常见原因是通过组策略下发监听器时配置有误。1

     Get-WSManInstance winrm/config/listener -Enumerate
    
  4. 看网络配置文件。在客户端版上,如果网络是公用网络,Enable-PSRemoting 本身就会因“Unable to check the status of the firewall”而失败。可以把配置文件改为专用网络,或加上 -SkipNetworkProfileCheck1
  5. 看防火墙规则。即使是服务器版,在公用网络配置文件下也会是“仅允许来自同一子网”的规则。如果要从其他子网连接,就需要调整规则(请先用 Get-NetFirewallRule 确认规则名称再修改,规则名称会因 Windows 版本而不同)。1
  6. 看是否满足认证方式的条件。在工作组、非域成员、以及按 IP 地址指定的情况下,无法使用 Kerberos 而只能用 NTLM,因此登记 TrustedHosts(或使用 HTTPS)以及显式指定 -Credential 都是必需的。目标账户不能使用空密码。1
  7. 看是否有连接所需的权限。能连接到默认终结点的,只有远程一侧 Administrators 的成员。来自其他域的用户或本地账户,默认会得到标准用户级别的令牌,无法以管理员身份操作(如有需要可以用 LocalAccountTokenFilterPolicy,但这会对所有用户禁用 UAC 的远程限制,请在理解其影响后再使用)。1
  8. 确认终结点的 PowerShell 版本。如果已经连上了,却提示“找不到这个 cmdlet”,很可能像第 2 章说的那样,进入的是 Windows PowerShell 5.1 一侧的终结点。可以用 Invoke-Command -ComputerName sv-app01 { $PSVersionTable.PSVersion } 确认,如有需要用 -ConfigurationName 显式指定。10

7.2. 常见错误信息的解读方式

错误文本几乎都是固定套路,与其说要背下来,不如养成“看到这句话就去查这里”的对照习惯,更实用。1

错误信息(节选) 常见原因 首先查看的地方
Access is denied. You need to run this cmdlet from an elevated process. 本地的 PowerShell 不是以管理员权限运行 用“以管理员身份运行”重新打开
ACCESS IS DENIED 目标一侧 Remoting 未启用 / 连接用户不属于 Administrators / 其他域或本地管理员的令牌受限 在目标一侧执行 Enable-PSRemoting、用 -Credential 指定管理员,如有需要检查会话配置的权限或 LocalAccountTokenFilterPolicy
The connection to the remote host was refused. Verify that the WS-Management service is running on the remote host and configured to listen for requests on the correct port and HTTP URL. WinRM 服务已停止、没有监听器、端口不对 Get-Service WinRM、枚举监听器、检查端口设置
The client cannot connect to the destination specified in the request. Verify that the service on the destination is running and is accepting requests. 监听器的 ListeningOn 为空(策略配置有误)、受代理影响 枚举监听器、检查 New-PSSessionOption 的代理设置
The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting. 工作组环境、非域成员、按 IP 地址指定 登记 TrustedHosts + -Credential,或参照 3.1 构建 HTTPS
Unable to check the status of the firewall(执行 Enable-PSRemoting 时) 客户端版上网络是公用网络 改为专用网络,或加上 -SkipNetworkProfileCheck
The WS-Management service cannot complete the operation within the time specified in OperationTimeout. 处理耗时过长、对方负载过高 New-PSSessionOption -OperationTimeout 延长超时时间
The total data received from the remote client exceeded allowed maximum. 返回的数据量过大 在 ScriptBlock 内先汇总、筛选后再返回,如有需要调整配额

排查的诀窍是,逐一确定“是本地的问题、路径的问题,还是对方的问题”。先用 Test-WSMan 是否能通,来区分出路径和对方服务这一层;通过之后再出现的错误,则集中在认证与权限上。养成这两段式的思路,就不会在原因不明的情况下反复瞎改配置。

8. 实务定式(判断表)

论点 选项 判断标准
交互式排查单台机器 RDP / Enter-PSSession 需要画面就用 RDP,命令能解决就用更轻量的 Enter-PSSession11
对多台机器执行同一处理 逐台手动操作 / Invoke-Command 超过 3 台就用 Invoke-Command。默认最多并行 32 台67
需要多次发送命令 每次用 -ComputerName 连接 / 复用 New-PSSession 需要交互式反复往返的调查场景,应复用会话。用完记得 Remove-PSSession14
工作组的认证 TrustedHosts(HTTP) / HTTPS 监听器 临时使用就用最小限度的 TrustedHosts。要长期使用就构建 HTTPS(步骤见 3.1)5312
second hop 的应对 显式传递凭据 / 基于资源的委派 / CredSSP 先选不需要修改配置的显式传递方式。若是长期使用则选基于资源的委派。CredSSP 是最后手段9
最先执行的命令 变更类 / 只读类 一定先用 Get 系列命令做盘点。变更类命令先用支持 -WhatIf 的命令做预演,再正式执行

最后一行值得特别强调。Invoke-Command 既是“瞬间修好 20 台机器的命令”,同时也是“瞬间弄坏 20 台机器的命令”。所幸,像 Stop-Service、Set-ItemProperty 这类变更类 cmdlet 大多支持 -WhatIf,在 ScriptBlock 内部也可以照常使用。新脚本先在 1 台上试跑,再带 -WhatIf 面向全体机器演练一遍,最后才正式执行——养成这三段式的习惯,事故率会明显下降。

9. 总结

  • Remoting 运行在 WinRM(WS-Management)之上,默认端口是 HTTP 5985 / HTTPS 5986。默认情况下只有远程一侧 Administrators 的成员才能连接,通信在认证完成后会被加密。
  • Enable-PSRemoting 会启动 WinRM 服务、创建监听器、开启防火墙例外、启用会话配置。需要注意的是,配置的终结点是与执行它所用的 PowerShell 版本对应的。
  • 域环境下 Kerberos 可以直接使用,工作组则需要 TrustedHosts + 显式凭据。TrustedHosts 是放弃身份验证的列表,因此要控制在最小范围。
  • 批量执行用 Invoke-Command(默认并行 32 台),交互操作用 Enter-PSSession,需要保持状态就复用 New-PSSession。结果是不带方法的反序列化对象,因此操作应在远程一侧完结。
  • 无法从连接目标进一步访问下一台机器(second hop)。首先考虑显式传递凭据,如果要长期使用,则应研究基于资源的 Kerberos 约束委派。
  • “连不上”时,先用 Test-WSMan 把路径和对方服务这一层排除掉,再把注意力集中在认证(TrustedHosts、凭据)与权限(Administrators)上。常见错误文本及对应查看点已汇总在第 7 章的表格中。
  • 运维应先从只读操作入手。变更类操作按“1 台 → -WhatIf → 正式执行”三个阶段推进。应用到文件服务器盘点的具体示例,请参见同期发布的《用 PowerShell 盘点文件服务器》。

相关文章

相关咨询领域

小村软件有限公司提供通过 PowerShell 构建服务器・客户端批量管理机制、设计与评审内置 Remoting 的运维脚本,以及针对“连不上”“只在特定环境下认证失败”这类 WinRM/认证相关问题的调查服务。

参考链接

  1. Microsoft Learn,about_Remote_Troubleshooting。关于 Windows Server 2012 及以后的 Windows Server 上 Remoting 默认启用、客户端版 Windows 的 WinRM 服务默认禁用、修改 WSMan: 驱动器的设置需要管理员权限、常见错误信息(拒绝访问/连接被拒绝/Unable to check the status of the firewall/与 TrustedHosts 及 HTTPS 相关的 WinRM 客户端错误/超时/超出配额)及其应对方式、用 Get-WSManInstance 枚举监听器以及 ListeningOn 为空的策略配置错误、公用网络下防火墙规则的行为、工作组环境下无法使用空密码账户,以及其他域或本地账户的管理员令牌限制与 LocalAccountTokenFilterPolicy。  2 3 4 5 6 7 8 9 10 11

  2. Microsoft Learn,PowerShell remoting over SSH。关于 PowerShell 6 及以后可以使用基于 SSH 的 Remoting、New-PSSession/Enter-PSSession/Invoke-Command 新增了 -HostName/-UserName/-KeyFilePath 参数集、可跨平台运行,以及终结点配置和 JEA 目前尚不支持。  2 3 4

  3. Microsoft Learn,Security Considerations for PowerShell Remoting using WinRM。关于 Remoting 使用 WinRM(WS-Management 的微软实现)、默认端口为 HTTP 5985/HTTPS 5986、默认只有 Administrators 组成员可以连接且会话在用户上下文中运行、认证完成后通信会被加密、TrustedHosts 只是身份验证错误的抑制列表,以及 second hop 问题的背景。  2 3 4 5 6 7 8 9 10 11 12

  4. Microsoft Learn,Enable-PSRemoting。关于 Enable-PSRemoting 所执行操作的清单(启动 WinRM 服务并设为自动启动、创建监听器、开启防火墙例外、启用会话配置并修改安全描述符)、Windows Server 上默认启用、客户端版在公用网络下的限制与 SkipNetworkProfileCheck,以及会为执行时所用的版本配置对应终结点。  2 3 4 5

  5. Microsoft Learn,Installation and configuration for Windows Remote Management。关于 WinRM 2.0 默认监听端口为 5985/5986、域账户选用 Kerberos・本地账户选用 NTLM、Kerberos 在工作组下无法使用而仅限于域、TrustedHosts 中的计算机不会被身份验证而可能导致凭据被发送出去,因此应控制在最小范围。  2 3 4 5 6 7 8

  6. Microsoft Learn,Invoke-Command。关于 Invoke-Command 可以用单条命令在多台计算机上执行命令、-ComputerName 的临时连接与 -Session 使用 PSSession 的区别,以及 -ArgumentList、-FilePath 等参数。  2 3

  7. Microsoft Learn,PowerShell Remoting FAQ。关于并发连接数默认值为 32、可通过 ThrottleLimit 参数修改、远程命令的输出会被序列化为 CLIXML 并以反序列化对象(不带方法)的形式返回,以及 Invoke-Command 的结果会附加用于区分来源的属性。  2 3 4 5

  8. Microsoft Learn,about_Remote_Output。关于远程命令的输出经过序列化/反序列化后会变成仅含属性的快照,以及结果按到达顺序返回,因此需要用 PSComputerName 重新排序。  2 3

  9. Microsoft Learn,Making the second hop in PowerShell Remoting。关于 second hop 问题的场景、应对方法一览及推荐顺序(CredSSP、基于资源的 Kerberos 约束委派、JEA 等)、CredSSP 会把凭据缓存到远程一侧因而在遭入侵时存在风险且默认禁用,以及在 ScriptBlock 内通过 $Using:cred 传递凭据的示例。  2 3 4 5 6 7 8

  10. Microsoft Learn,Migrating from Windows PowerShell 5.1 to PowerShell 7。关于在启用了 WinRM 的环境中,PowerShell 7 默认会使用 Windows PowerShell 5.1 已有的终结点(Microsoft.PowerShell)进行连接,以及需要执行 Enable-PSRemoting 才能创建 PowerShell 7 自身的终结点。  2

  11. Microsoft Learn,Enter-PSSession。关于 Enter-PSSession 会与单台远程计算机建立交互式会话、用 exit/Exit-PSSession 结束、需要是远程一侧 Administrators 组的成员,以及按 IP 地址指定时需要 HTTPS 配置或 TrustedHosts 登记。  2 3 4

  12. Microsoft Learn,How to configure WINRM for HTTPS(KB 2019527)。关于 HTTPS 监听器所需的证书要求(本地计算机的服务器身份验证证书、CN 与主机名一致、未过期・未吊销・非自签名)、通过证书管理单元确认的步骤、用 winrm quickconfig -transport:https 进行配置、用 winrm enumerate winrm/config/listener 进行确认、Windows 7 及以后的 HTTP 5985/HTTPS 5986 端口,以及在没有合适证书时出现的错误 0x80338115 与应检查的证书属性。  2 3

  13. Microsoft Learn,about_Remote_Variables。关于用于在远程执行的命令中使用本地变量的 $using: 作用域修饰符、在远程会话中值会以独立副本的形式传递,以及序列化会导致方法丢失。  2

  14. Microsoft Learn,New-PSSession。关于 New-PSSession 会创建持久连接(PSSession)、用于执行需要共享数据的多条命令、指定 -ComputerName 时会创建临时连接并在每条命令后关闭,以及同样支持基于 SSH 的连接。  2 3

  15. Microsoft Learn,Running Remote Commands。关于通过 Invoke-Command 执行脚本文件(-FilePath),以及在用 New-PSSession 创建的持久会话中,变量等状态会在多条命令之间保持。 

  16. Microsoft Learn,Overview of Just Enough Administration (JEA)。关于 JEA 是一种可对 PowerShell 管理的对象实现委派管理的安全技术、可以限定能够执行的 cmdlet・函数・外部命令、通过虚拟账户或组托管服务账户减少管理员账户的数量,以及可通过转录和日志了解实际执行了什么。 

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。

本文与以下服务页面相关联,欢迎从最接近的入口查看。

常见问题

汇总了咨询这一主题时常见的问题。

执行 Enable-PSRemoting 会发生什么?
内部会执行 Set-WSManQuickConfig,从而启动 WinRM 服务并将其启动类型改为自动,创建可在任意 IP 地址上接收请求的监听器,启用 WS-Management 通信所需的防火墙例外,并启用会话配置(终结点)及远程访问权限。Windows Server 上默认已启用,但客户端 Windows 需要自行执行。只有接受连接的一方需要执行,发起连接的一方无需执行。
在工作组环境(无域)中使用 PowerShell Remoting 需要什么?
由于没有域就无法使用 Kerberos 认证,因此会改用 NTLM 认证。基本做法是在发起连接的一方把目标主机加入 WSMan 的 TrustedHosts 列表,并通过 -Credential 显式传入凭据。但由于加入 TrustedHosts 的对象不会被身份验证(即不做冒充检测)就把凭据发送出去,因此不要使用通配符,只登记必需的最少主机名,并尽量考虑构建 HTTPS(5986) 监听器。
为什么 Invoke-Command 返回的结果对象无法调用方法?
因为远程命令的输出需要经网络传送而被序列化为 XML(CLIXML),在本地则以反序列化后的对象形式还原。这只是执行那一刻各属性的快照,而不是活的对象,因此不带方法。像停止服务这样的操作,不应在本地对结果对象调用方法,而必须在 ScriptBlock 内部(即远程一侧)执行。
second hop(双跳)问题是什么?
指从 PC A 通过 Remoting 进入服务器 B,再从 B 试图访问另一台服务器 C(例如文件服务器)时被拒绝的问题。默认的 Kerberos/NTLM 认证不会把凭据本身发送给 B,因此 B 无法以你的身份向 C 进行认证。应对方法包括在 ScriptBlock 内显式传递凭据、基于资源的 Kerberos 约束委派、CredSSP(凭据会传到远程一侧,风险更高)等,需要根据需求选择。
PowerShell Remoting 是不是谁都能连接进来?
不是。默认情况下只有远程计算机 Administrators 组的成员才能连接。而且连接建立后的会话在所连接用户的上下文中运行,操作系统的访问控制会照常生效。尽管如此,对管理员而言这仍是一个强有力的入口,因此应结合防火墙规则审查、HTTPS 监听器、仅限必要管理员使用等多层防御手段一起运维。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表