原理
PowerShell的远程处理类似于Telnet或其他一些老旧的远程处理技术。当键入该命令时,它会在远程计算机上运行,只有该命令的运行结果会返回本地计算机。与Telnet和SecureShell(SSH)不一样的是,PowerShell采用一种新的通信协议,称之为针对管理的Web服务(Web Services for Management, WS-MAN
)。
WS-MAN完全基于HTTP或者HTTPS进行工作,这样在需要时,能轻易透过防火墙进行作业(因为每种协议都使用唯一的端口通信)。微软对WS-MAN的实现主要基于一个后台服务:Windows远程管理组件(**WinRM**
)。
Tips:通常来说WinRM服务,在服务器操作系统处于启用状态,在客户端操作系统处于禁用状态。
注:被远程的端点,必须开启winrm远程服务。
基于SSH的远程处理
微软远程处理技术除了WS-MAN之外,还可以基于SSH协议。要了解该远程技术,可以阅读Invoke-Command
与New-PSSession
Option的文档。
序列化和反序列化结果
当运行一个远程命令时,PowerShell会将Cmdlet产生的一些对象作为输出结果,放入到一个特定形式的包中,之后通过网络中的HTTP(或者HTTPS)协议传回本地计算机。XML已经被证明是针对该问题的优秀解决方案,所以PowerShell会将输出对象序列化到XML中。下一步,XML文件会通过网络进行传输。当到达本地计算机之后,该XML会反序列化为PowerShell可以处理的对象。
序列化和反序列化仅仅是一种格式转换的形式:从对象转化为XML称为序列化,从XML转为对象则为反序列化。
这些序列化和反序列化的对象只是各种快照而已,它们并不会随着后续状态的变化而自我更新。例如,如果获得代表远程计算机上运行进程的对象时,这些对象只能反映对象被产生时刻的状态。
要保证远程处理可以正常工作,需要满足下面两个条件:
- 本机计算机和远程计算机,至少需要第2版或者更新版本的PowerShell(XP及以上版本系统)。
- 理论上,两台计算机需要在同一域或者可信域中。如果计算机不在域中,远程处理也可以正常工作,但配置会有些麻烦。请在PowerShell中运行
Help About_Remote_Troubleshooting
进行查看。WinRM概述
在使用远程处理之前,我们必须配置WinRM服务。只需要在接收远程命令的计算机上配置WinRM,以及PowerShell远程处理即可。
并非只有PowerShell能使用WinRM服务。实际上,微软在越来越多的管理程序中开始使用WinRM服务。基于这一思想,微软保证WinRM可以将流量导入至多种管理程序——不仅仅是PowerShell。
WinRM类似一个调度器:当有新的流量进来后,WinRM会决定由哪种程序来处理这部分流量。所有WinRM流量都标记了接收应用程序的名称,同时这些应用程序都必须在WinRM中创建各自的端点,这样WinRM才能侦听这些主体的流量。这也意味着,不止需要启用WinRM服务,也需要在WinRM中将PowerShell注册为一个端点。
调度关系
WinRM、WS-MAN、端点和PowerShell之间的关系:
端点
如图所示,在系统中可以有几十个甚至上百个WinRM端点(PowerShell称它们为会话配置选项)。每一个端点都指向一种应用程序,甚至你可以将多个端点指向同一个应用程序,但是每个端点提供不同的权限以及功能。例如,可以在环境中创建一个PowerShell端点,该端点仅允许特定用户运行一个或者两个命令。
侦听器
WinRM侦听器部分,在图中属于HTTP种类中的一种。一个侦听器会为WinRM等待网络流量的进入——有点像Web服务器侦听传入请求。尽管由Enable-PSRemoting
创建的默认侦听器,会侦听本地所有IP地址的某个端口,但是一个侦听器仅会侦听从特定IP地址的特定端口发出的请求。
可以针对WinRM创建多个侦听器(比如一个针对HTTP流量,一个针对加密的HTTPS流量,或者其他一些针对不同的IP地址)。这些侦听器会将流量导入至计算机上配置的端点。
创建端点 - 开启远程服务
侦听器会连接到已定义的端点。可以采用下面的方法创建一个端点:新开一个以管理员权限运行的PowerShell窗口,之后运行Enable-PSRemoting
命令。
该Cmdlet会启用WinRM服务,配置该服务为自动启动模式,然后在WinRM中为PowerShell注册一个端口,甚至会在Windows防火墙中针对传入的WinRM流量创建例外条件。
有时,你可能会看到另外一个相关的命令
Set-WSManQuickConfig
。但是根本没必要手动运行该命令,Enable-PSRemoting命令会自动调用该命令。 另外,Enable-PSRemoting
命令也会运行一些其他步骤完成开启远程处理服务。
Tips:
- 如果网卡为“公用网络”类型,那么在执行启用远程服务命令时,可能会报错。
- 类型为“公用网络”的网卡中无法设置Windows防火墙例外,所以当运行Enable-PSRemoting命令尝试创建一个防火墙例外时,就会失败。
解决办法是修改该网卡的类型为“工作网络”或者“家庭网络”(服务器版系统,没该限制)。
使用组策略创建端点
可以通过组策略对象(GPO),批量实现远程服务的开启。这些必要的GPO设置选项,已经内置到Windows Server 2008 R2及之后的版本中了。
老版本系统的域控制器计算机,需要去网站http://download.microsoft.com上下载一个ADM(Administrative Templates)模板,之后添加这些GPO选项。
基本操作步骤:
- 打开一个GPO对象,之后查看路径“计算机配置”>“管理模板”>“Windows组件”下的对象。
- 在该列表的中间部分,可以看到“Windows远程Shell”(Windows RemoteShell)和“Windows远程管理”(WinRM)。
Tips:
PowerShell的About_Remote_TroubleShooting
帮助主题中,包含了更多关于如何使用GPO对象的内容。可以查找该帮助信息中的“如何启用企业中的远程处理”和“如何通过使用组策略启用侦听器”部分。
查看Windows远程Shell(Remote Shell)的组策略对象设置选项,有下面几个可更改的选项:
- 一个远程处理进程,在被计算机自动杀掉之前处于打开状态的最长时间;
- 允许并行运行远程处理进程的最大用户数;
- 每个远程Shell可使用的最大内存以及最大进程数;
- 每个用户可打开远程Shell的最大数目等。
WinRM相关命令
查看端口:winrm enumerate winrm/config/listener
有关相关主题的帮助:
winrm help uris:创建资源 URI 的方式。
winrm help aliases:URI 的缩写。
winrm help config:配置 WinRM 客户端和服务设置。
winrm help certmapping:配置客户端证书访问。
winrm help remoting:访问远程计算机的方式。
winrm help auth:提供远程访问的凭据。
winrm help input:提供输入以进行创建、设置和调用。
winrm help switches:其他开关,例如格式化、选项等等。
winrm help proxy :提供代理信息。
范例:
# 侦听计算机上所有 IP 的 HTTP 请求:
winrm create winrm/config/listener?Address=*+Transport=HTTP
# 禁用给定的侦听程序
winrm set winrm/config/listener?Address=IP:1.2.3.4+Transport=HTTP @{Enabled="false"}
# 启用客户端而非服务上的基本身份验证:
winrm set winrm/config/client/auth @{Basic="true"}
# 为所有工作组计算机启用 Negotiate。
winrm set winrm/config/client @{TrustedHosts="<local>"}
修改端口
第2版及后续版本的WinRM服务,默认会使用TCP端口5985侦听HTTP,使用5986端口侦听HTTPS。这样的端口号,保证了不会与本地安装的任意Web服务器使用的端口号(一般使用80~443之间的端口号)冲突。
使用Enable-PSRemoting
创建的远程处理,默认仅对5985端口创建非加密的HTTP侦听器。当然,也可以配置WinRM使用其他端口。如果采用默认值,所有的PowerShell远程处理命令都可以正常运行。假如修改了端口号,在输入远程处理命令时,就必须指定端口号。
查看端口号:
PS C:\Windows\system32> winrm enumerate winrm/config/listener
Listener
Address = *
Transport = HTTP
Port = 5985
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint
ListeningOn = 127.0.0.1, 169.254.25.128, 169.254.182.205, 169.254.223.41, 192.168.1.101, 192.168.56.1, ::1, fe80::25ea:a031:35ae:df29%20, fe80::458c:709b:c3ac:eb0f%11, fe80::a4ec:74a0:4675:b6cd%14, fe80::e85c:472a:2ee:2ac0%25
可以通过下面的命令实现端口号的修改:
# 将其中的HTTP修改为HTTPS,即可修改HTTPS的侦听端口。
PS C:\Windows\system32> winrm set winrm/config/listener?Address=*+Transport=HTTP '@{Port="8666"}'
Listener
Address = *
Transport = HTTP
Port = 8666
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint
ListeningOn = 127.0.0.1, 169.254.25.128, 169.254.182.205, 169.254.223.41, 192.168.1.101, 192.168.56.1, ::1, fe80::25ea:a031:35ae:df29%20, fe80::458c:709b:c3ac:eb0f%11, fe80::a4ec:74a0:4675:b6cd%14, fe80::e85c:472a:2ee:2ac0%25
也可以修改客户端计算机上的WinRM的默认端口。这样当运行命令时,就不需要再指定修改之后的默认端口。
实现远程处理
PowerShell可以通过两种方法实现远程处理:
- 第一种称为一对一,或者1:1远程处理;
- 第二种称为一对多,或者1 : n远程处理。
当使用一对一的远程处理时,不需要担心被序列化和反序列化的对象。其等效于直接在远程计算机的控制台中键入命令。
一对一场景:Enter-PSSession
当使用一对一远程处理时,实际上是在单台远程计算机上调用了一个Shell命令窗口。输入的任何命令都会直接在该计算机上运行,然后在远程处理窗口中返回输出结果。该机制非常类似于远程桌面连接(Remote Desktop Connection),只是Windows PowerShell采用的是命令行环境。
如果需要针对一台远程计算机建立一对一的远程处理进程,请运行下面的命令:
如果Shell命令窗口变为下面的格式,那么也就说明该连接成功建立。
该Shell命令框表示所运行的任何语句,都是在Server-R2上运行。
注意:
1)你需要知道远程计算机的真实名称,WinRM默认不允许使用IP地址或者DNS中的别名去进行远程处理。
2)当使用远程处理连接到另一台计算机时,不要在该命令窗口中再次运行Enter-PSSession
。
该语句会在Server-R2上维护一个到Server-DC4的远程连接,也就是会建立一个“远程处理链”(二连跳)。
在远程计算机上运行的任何命令都依赖于你的凭据,所以你能实现你权限范围之内的任意操作。你运行的PowerShell副本会带有其运行的安全令牌(该过程通过Kerberos实现,所以并不会通过网络传递用户名以及密码到远程计算机)。
两个关键点
- 即使远程计算机上PowerShell存在一个Profile脚本,当使用远程处理时,该脚本也不会自动运行。
- Profile脚本是指当打开Shell时会自动运行的一批命令。
- 人们经常使用Profile脚本来自动载入一些Shell扩展程序以及模块等。
远程计算机的运行策略会限制某些脚本的运行。
- 假如本地计算机的策略设置为RemoteSigned,也就意味着可以运行本地未签名的脚本。
- 如果远程计算机的策略设为默认(严格),当使用远程连接到该计算机时,并不是所有脚本都可以运行。
退出远程处理:Exit-PSSession
当在远程计算机上运行命令结束之后,还要运行什么命令呢?在这个场景中,如果Enter-PSSession
可以对其他计算机运行远程处理,那么可以通过Exit-PSSession
来退出该进程。该命令不需要其他任何参数;运行之后,Shell命令窗口会变回正常,远程连接会被自动关闭。
当然,直接关闭PowerShell的窗口,也可以关闭远程连接。一对多场景:Invoke-Command
该功能,就是将一个命令同时传递给多台远程计算机。也可称之为全面的分布式计算。每台计算机都独立运行发送的命令,然后将结果集返回。PowerShell利用Invoke-Command命令来实现该功能,称之为一对多远程处理。
最外层{}
中包含的全部命令都会传递到3台远程计算机。默认,PowerShell最多一次与32台远程计算机通信。
如果超过32台,那么会将计算机信息存放到一个队列中。
- 如果命令在一台远程计算机上运行完毕,队列中的下一台计算机会立即开始运行。
当然,如果网络足够良好,并且计算机性能足够强劲,那么可以通过Invoke-Command的-ThrottleLimit
参数,来指定更多数量的计算机。
如果有多台计算机,可以放入到文本文档中,每行代表一个计算机名称。
Tips:在Invoke-Command的帮助信息中找不到-Command参数,但是我们确认上面示例中的命令可以正常运行。实际上,-Command是帮助文档中-ScriptBlock参数(可以在帮助文档中找到该参数信息)的一个别名。由于-Command命令更容易记住,所以往往使用它,而不会使用-ScriptCommand
,但它们的作用相同。
指定脚本文件:-FilePath
-FilePath
参数,允许我们指定一个脚本文件,而不是一个命令。该参数可以将本地的完整脚本传递到远程计算机——意味着你可以自动化一些复杂的任务,让每一台计算机完成各自对应的部分。
Invoke-Command -FilePath c:\scripts\test.ps1 -ComputerName Server01
从活动目录获取计算机名称
因为Get-Content命令产生的对象类型,为-Computer参数可接受的简单文本String类型。但是Get-ADComputer
会输出完整的计算机对象,-ComputerName参数不知道应该如何处理这部分数据。
如果要使用Get-ADComputer命令,那么需要找到一个方法去获取这些计算机对象名称属性的值。
远程和本地命令间的差异
使用下面的命令作为演示差异的示例:
使用Invoke-Command命令的特点:
- 计算机会被并发地访问。也就是说,命令运行更有效率。
- 命令的输出结果中包含PSComputerName属性,也就能轻易地看到哪个结果来自于哪台计算机。
- 通过WinRM来建立连接,WinRM会使用一个预定义的端口,使得命令可以更轻易地穿过任何防火墙。
- 每台计算机都会查询200条记录,然后在本地就做筛选。通过网络传递回来的数据是经过筛选之后的结果,也就是说,这些记录都是我们希望得到的有效数据。
- 在传递结果之前,每台计算机都会将输出结果序列化为XML。本地计算机收到该XML之后,会反序列化为一些类似对象的结果。但是它们并不是真正的事件日志对象,这也就限制了本地计算机处理这些对象的方式。
最后一点是使用-ComputerName
参数和Invoke-Command
命令之间,很大的一个差异点。
使用–ComputerName参数的差异
下面是实现相同目的的另外一种方法:
在该示例中,使用Get-EventLog命令的-ComputerName参数,而不是远程调用整个命令。最终会得到类似的结果,但是在该命令运行的方式上存在很大的不同。
- 提及的计算机会按照顺序被串行访问,而不会采用并行方式,也就意味着命令会运行更久的时间。
- 该命令的输出结果中不会包含
PSComputerName
属性,也就是说很难判别某个结果是从哪台计算机得出的。 - 该连接并不会使用WinRM实现,而会使用
.Net FrameWork
决定的底层协议。- 我们不知道到底是哪种底层协议;
- 同时,有可能由于该协议无法在本地和远程计算机之间,顺利通过防火墙而无法建立连接。
- 该命令会从3台计算机上查询200条记录,然后再找到eventid为1212的事件。这样,就有可能会返回一些不需要的结果。
- 通过该命令得到的是功能全面的事件日志对象。
对带有-ComputerName参数的Cmdlet而言都存在这些差异。一般来讲,使用Invoke-Command命令比Cmdlet的-ComputerName参数更有效率,更有用。
本地处理和远程处理对比
下面示例,通过修改{}的位置,来进行差异对比。
该示例,只有Get-EventLog命令被远程调用。Get-EventLog命令产生的所有结果都被序列化,之后发送到本地计算机,最后在本机反序列化为对象,再通过管道传递给Where做筛选。
相对而言,该示例的命令效率更为低下,因为会有大量不必要的数据通过网络传输,然后在本地计算机上筛选来自3台计算机的返回结果,而并不是在3台计算机上筛选好结果之后再发送给本地计算机。
让我们看看其他命令的两个版本,如下:
和上面一样,这里唯一的差异同样是一个大括号的位置不同。但是在本示例中,左边的命令根本无法运行。
这个示例说明了,我们需要在远程计算机上完成尽量多的工作。唯一需要注意的是如何处理Invoke-Command
命令的结果,要么显示,要么将结果存储为一个报表或者一个数据文件等。
此示例右边的脚本正是采用了该思想:发送给远程计算机的命令是Get-Process-Name Notepad | Stop-Process
,所以整个命令(包含获取进程以及停止进程)都是在远程计算机上运行。因为Stop-Process命令不会产生任何运行结果,并没有任何对象需要序列化传递给我们,所以在本地的控制台中无法看到任何信息。
反序列化对象的缺失部分
远程处理另一个需要注意的事项,返回给本地计算机的对象可能会缺失部分功能。在大部分情况中,由于它们不再需要关联到可用软件,它们都会缺少对应的方法(Method)。
比如,在本地计算机上运行下面的命令,你会发现存在关联到Service-Controller对象的多个方法。
现在通过远程处理获取类似的对象:
查看上面返回的结果,你会发现,除了每个对象都拥有的普通ToString()
方法外,其他的方法都不在了。返回的结果只是对象的一些副本,你无法让它完成停止、暂停、恢复等操作。所以如果希望对返回的结果运行一些操作,那么这部分命令都应该包含在发送给远程计算机的脚本中。
远程处理的配置选项
通过阅读帮助文档,可以看到Invoke-Command
和Enter-PSSession
命令都有一个-SessionOption
参数(该参数能处理PSSessionOption类型对象)。
这两个命令在运行时,都会初始化一个新的PowerShell连接或者会话。它们完成对应的工作后,会自动关闭该会话。一个会话选项(Session Option),是指你可以用来改变建立会话方式的一组选项。可以使用New-PSSessionOption
命令来实现该功能。
可以使用New-PSSessionOption
命令实现下面的功能:
- 打开、取消和空闲超时。
- 取消正常数据流的压缩或者加密功能。
- 通过代理服务器传递网络流量时,也可以设置一些代理相关的选项。
- 忽略远程机器的SSL证书、名称以及其他安全特性。
比如,通过下面的命令可以忽略机器名称检查,然后打开一个会话。
可重新查看New-PSSessionOption
命令的帮助信息,确认该命令可实现的功能。
总结
- 默认情况下,只有指定远程计算机的真实名称时,远程处理才能正常工作。不能使用DNS的别名或者IP地址。
- 设计远程处理功能的目的主要是解决域中自动化配置的事情。
- 如果涉及的计算机以及所使用的用户账号都属于同一个域或可信任的域中,那么可以很轻易地实现。
- 如果不是这种情况,那么需要详细查看
About_Remote_TroubleShooting
的帮助文档。 - 一个需要确认的情形是你是否跨域进行远程处理。如果确认如此,那么必须修改一些配置选项使得PowerShell可以正常运行,帮助文档中详细描述了该场景。
- 确保是以管理员身份运行PowerShell,特别是对于开启用户账户控制(UAC)功能的计算机。
- 如果使用的账号在远程计算机上没有管理员权限,那么需要使用Enter-PSSession或者Invoke-Command命令的
-Credential
参数,去指定另外一个拥有管理员权限的账号。
- 如果使用的账号在远程计算机上没有管理员权限,那么需要使用Enter-PSSession或者Invoke-Command命令的
- 如果环境中使用的不是Windows防火墙,而是第三方防火墙产品,
Enable-PSRemoting
不会建立特定的防火墙例外。那么需要手动来完成该项设置。- 如果远程连接,需要穿过一个部署在路由器或者代理服务器上的普通防火墙,那么也需要针对远程流量手动设置一个例外。
- 请不要忘记一点规则,在组策略对象(GPO)中的配置选项会覆盖本地配置的选项。
访问网站http://PowerShell.org的e-book资源,这里Don和MVP Tobias Weltner博士一起写了一本全面探究PowerShell远程处理原理的一本迷你电子书(也是完全免费)。该电子书中会重讲本章中所学的一些基础知识,但是内容主要集中关于如何配置各种远程处理场景的Step-by-step详细说明。
https://runbookautomation.wordpress.com/2013/10/09/powershell-remoting-resources/