文章目录
介绍
PowerShell
作为Windows
系统中的默认脚本,其实在开发中,我们用的很少。接下来的几篇文章都将PowerShell
的相关知识,在上一章中我们介绍了PowerShell
更多的知识,本章我们将深入了解其远程的相关知识。
远程处理:一对一及一对多
PowerShell远程处理的原理
在一定程度上,PowerShell
的远程处理类似与Telnet
或者其他一些老旧的远程处理计算。当键入该命令时,他会在远程计算机上运行。只有该命令的运行结果会返回本地计算机。与Telnet
和Secure Shell
(SSH)不一样的是,PowerShell
采用新的通信协议,我们称之为针对管理的Web
服务(Web Services for Management
, WS-MAN)。该协议完全基于HTTP
或者HTTPS
进行工作,微软对其的实现主要基于一个后台服务:Windows
远程管理组件(WinRM)。另外,通过远程返回的结果只是想要获取的对象的快照,并不是实时的对象信息。(其主要通过XML
方式进行序列化和反序列化来完成传输)
WinRM概述
在使用远程处理之前,我们必须配置该WinRm服务
。记住,你只需要在接收远程命令的计算机上配置WinRM
以及PowerShell
远程处理即可。
WinRM
类似一个调度器:当有新的浏览进来后,WinRM
会决定由哪种程序来处理这部分流量。同时WinRM
还有侦听器
,一个侦听器
会为WinRM
等待网络流量的进入——有点像Web
服务器侦听传入请求。PowerShell
默认使用Enable-PSRemoting
创建一个默认的侦听本地所有IP
地址的某个端口,但是必须是以管理员权限运行。有时候你可能会看到另外一个相关的命令Set-WSManQuickConfig
,无需手动处理,因为Enable-PSRemoting
命令会自动调用该命令。
Enable-PSRemoting命令只有在网络状态为“工作网络”或“家庭网络”时才可以,否则会报错
WinRM
默认使用TCP
端口5985
侦听HTTP
,使用5986
端口侦听HTTPS
。可以使用以下方式进行端口的修改,示例种端口改为1234
,如果要修改HTTPS
的端口则将命令中的HTTP
改为HTTPS
即可。
winRM Set WinRM/Config/Listener?Address=*+Transport=HTTP @{PORT="1234"}
一对一场景的Enter-PSSession和Exit-PSSession
当使用一对一远程处理时,实际上是在单台远程计算机上调用了一个Shell
命令窗口。输入的任何命令都会直接在该计算机上运行,然后在远程处理窗口种返回输出结果。该机制非常类似与远程桌面(Remote Desktop Connection
),只是PowerShell
采用的是命令行环境。相对与远程桌面,该计算占用资源更少,所以服务器开销更小。
Enter-PSSession -ComputerName Server-R2
你需要使用正确的计算机名称来替代
Server-R2
如果执行成功,则显示如下。
[Server-R2] PS C:\>
退出一对一的远程需要使用Exit-PSSession
命令,当然如果你忘记了也没关系,在你关闭PowerShell
窗口的时候,其会自动关闭远程会话。
一对多场景的Invoke-Command
下面讲的是Windows PowerShell
最酷的功能之一,也就是将一个命令同时传递给多台远程计算机。也可称之为全面的分布式计算
。每台计算机都独立运行发送到命令,然后将结果集返回给你。PowerShell
利用Invoke-Command
命令来实现该功能,称之为一对多或者1:n远程处理。
Invoke-Command -ComputerName S12,S13,S14 -Command { Get-EventLog Security -Newest 200 | Where { $_.EventID -eq 1212 }}
简单说明如下。
- S12,S13,S14需要使用真正的计算机名称。如果网络良好,计算机足够强劲,也可使用
-ThrottleLimit
参数来指定更多数量的计算机 - -Command在帮助中是找不到的,实际上它是
-ScriptBlock
参数的别名或者昵称
针对多个计算机每次执行远程命令都需要写一遍,我们可以将其放入要给文本文件中,并通过下面的命令进行调用。
Invoke-Command -Command {dir} -ComputerName (Get-Content ct.txt)
而如果需要将本地的变量传递到远程使用,可以考虑使用ArgumentList参数或者$using语句进行传递,直接赋值是无法传递到远程的。
$serverFilePath = 'C:\File.txt'
# 此命令无法传递本地变量
Invoke-Command -ComputerName (Get-Content ct.txt) -Command { Write-Host "The value of foo is $serverFilePath"}
# 使用ArgumentList参数
Invoke-Command -ComputerName (Get-Content ct.txt) -Command { Write-Host "The value of foo is $($args[0])} -ArgumentList $serverFilePath"
# 使用using语句
Invoke-Command -ComputerName (Get-Content ct.txt) -Command { Write-Host "The value of foo is $using:serverFilePath"}
远程命令和本地命令直接的差异
使用以下命令来进行说明。
Invoke-Command -ComputerName S12,S13,S14 -Command { Get-EventLog Security -Newest 200 | Where { $_.EventID -eq 1212 }}
Invoke-Command和-ComputerName对比
下面是实现相同目的的另一种写法。
Get-EventLog Security -Newest 200 -ComputerName S12,S13,S14 | Where { $_.EventID -eq 1212 }
该命令的运行方式上存在很大的不同。
- 提及的计算机会按顺序被串行访问,而不会采用并行方式,也就意味着命令执行时间更长
- 该命令输出的结果中不会包含
PSComputerName
属性,也就意味着我们很难判别某个结果是从哪台计算机得来的 - 该连接并不会使用
WinRM
实现,而使用.Net Framework
决定的底层协议 - 我们会从
3
台计算机上返回200
条记录,然后在根据EventID
过滤。这意味着返回很多我们不需要的结果 - 我们得到的是功能全面的事件对象
如果我们使用之前的Invoke-Command
命令,就好是如下这样。
- 计算机会被并行访问,即效率更高
- 命令的结果包含
PSComputerName
属性,即我们知道哪个结果来自哪台计算机 - 通过
WinRM
来创建连接,会使用一个预定义的端口,使得命令更轻易地穿过任何防火墙 - 每天计算机都会查
200
条记录并本地筛选,传回的结果都是我们想要的 - 传递结果前,每天计算机都会将输出结果序列化为
XML
。本地收到XML
后在反序列化为一些类似的对象结果。
本地处理和远程处理对比
再次引用之前的例子。
Invoke-Command -ComputerName S12,S13,S14 -Command { Get-EventLog Security -Newest 200 | Where { $_.EventID -eq 1212 }}
然后和下面的例子对比一下,唯一的不同是移动了最后的大括号位置。
Invoke-Command -ComputerName S12,S13,S14 -Command { Get-EventLog Security -Newest 200 } | Where { $_.EventID -eq 1212 }
在第二个命令中,只有Get-EventLog
命令被远程调用。Get-EventLog
命令产生的所有结果都被序列化,之后发送到本地计算机反序列化为对象,在进行筛选。相对来说,第二种方式效率更低。
反序列化对象
远程处理的另一个需要注意的的事项是返回来本地计算机的对象可能缺失部分功能。可以通过下面的命令进行对比。
Get-Service | Get-Member
Invoke-Command -ScriptBlock { Get-Service } -ComputerName S1 | Get-Member
从上面命令结果中可以看到,除了每个对象都拥有的普通ToString()
方法外,其他的方法都不在了。
轻松实现远程控制
介绍
会话是一个在你的PowerShell
副本与远程PowerShell
副本之间的持久化连接。当一个会话处于活动状态时,你的计算机与远程计算机都会划分出一小部分用于维护连接的内存和处理器时间,还有非常少一部分与连接相关的网络流量。PowerShell
维护了一个所有已经打开的会话列表,你可以使用这些会话调用命令或进入远程Shell
。
你可以通过New-PSSessin
这个Cmdlet
创建一个新的会话,指定一个或多个计算机名称。如果需要,还可以指定备用用户名称、端口号以及身份验证机制等。结果是一个存在PowerShell
内存中的对象。
New-PSSession -ComputerName Serverr2,server17,dc5
Get-PSSession # 获取会话
我们更倾向于把会话放到一个变量中。
$sessions = New-PSSession -ComputerName Server-R2,server17,dc5 -Credential WebAdmin
请永远不要忘记这些会话会消耗资源。建议不用的时候手动关闭,可以使用Remove-PSSession
命令来实现。
$sessions | Remove-PSSession
Get-PSSession | Remove-PSSession #关闭所有处于开启状态的会话
请记住,我们已经在这些计算机上启用了远程控制,并且这些计算机处于同一个域
利用Enter-PSSession命令使用会话
Enter-PSSession -Session $sessions[0]
执行上面的命令,可以看到命令提示符已经改变,表示我们已经在控制远程计算机,Exit-PSSession
命令用于帮助我们返回本地提示符,但是远程会话并不会中断,以便后续使用。
或许你很难通过索引号记起是哪个计算机,这种情况可以考虑使用属性来进行区分。例如,将$sessions
通过管道传递给GM
命令时,我们可以得到下面输出。
具体可以参考下面的示例来查看怎么使用。
Enter-PSSession -Session ($sessions | Where-Object { $_.ComputerName -EQ 'Server-R2' })
Enter-PSSession -Session (Get-PSSession -ComputerName Server-R2)
Get-PSSession -ComputerName Server-R2 | Enter-PSSession
通过-Full
参数查帮助信息,我们可以看到-Session
参数可以从管道接收一个PSSession
对象。
利用Invoke-Command命令使用会话
Invoke-Command
命令展示了Session
对象的价值,你习惯于用该命令将一个命令(或一个完整脚本)并行在多个远程计算机上执行。
Invoke-Command -Command { Get-WmiObject -Class Win32_Process } -Session $sessions
注意,我们将一个Get-WmiObject
命令发送到远程计算机。我们本可以选择使用Get-WmiObject
命令自带的-ComputerName
参数,但是由于下面4
个原因,我们没有这么做。
- 远程控制通过一个预定义的端口进行传输,
WMI
却不是。远程控制因此针对在防火墙后的计算机更加容易使用,这是由于更容易开启必要的防火墙例外 - 将所有的进程传输到本地费时费力。使用
Invoke-Command
命令,可以让每台计算机完成各自的工作,并返回结果 - 远程控制并行执行,默认可以连接最多
32
台计算机。WMI
顺序执行,一次只能一台计算机执行 - 我们无法通过
Get-WmiObject
使用我们预定义的会话对象,但可以通过Invoke-Command
使用
Invoke-Command
无法通过管道接收会话对象,但是可以使用下面示例的形式完成。
Invoke-Command -Command { Get-WmiObject -Class Win32_Process } -Session (Get-PSSession -ComputerName Ioc*)
隐式远程控制:导入一个会话
隐式远程控制是对我们来说最酷、最有用的功能之一——可能是在任何操作系统的命令行界面中迄今为止最酷、最有用的功能。但是不幸的是,该功能并未记入PowerShell
文档。
比如,有些功能只在某些系统上安装了,由于各种各样的原因,你无法将这些模块安装到本地计算机上。这时候如果想使用,就可以使用隐式远程控制。
让我们通过一个例子来说明。
# 建立连接
$session = New-PSSession -ComputerName Server-r2
# 载入远程控制模块
Invoke-Command -Command { Import-Module ActiveDirectory } -Session $session
# 导入远程控制命令
Import-PSSession -Session $session -Module ActiveDirectory -Prefix rem
# 查看临时本地模块
ModuleType Name ExportedCommands
---------- ---- ----------------
Script tmp_2b92353dc-23523-34s42... {Set-ADOrganizationalUnit,Get-ADD...
下面是本例的解释。
- 首先,通过与一台装有活动目录模块的远程计算机建立一个会话。我们需要该计算机上装有
PowerShell V2
或更新的版本,同时启用了远程控制 - 我们告诉远程计算机导入其本地的活动目录模块。由于会话处于打开状态,该模块将一直在远程计算机上处于被载入状态
- 我们接下来告诉我们的计算机从远程会话中导入命令。我们只需要活动目录模块中的命令,并在每个命令的名词部分加入“
rem
”前缀。这样更容易区分远程命令,也防止与本地命令发生冲突 PowerShell
在本地计算机创建一个临时模块,用于代表远程命令。这些命令并不是被复制过来;Powershell
为其创建了指向远程计算机的快捷方式
当我们运行这些命令时,它们并不是在我们本地的计算机上执行,而是隐式的在远程计算机上执行,之后将结果发送给本地计算机。所以这个结果不会包含对象的方法。
使用断开会话
PowerShell v3
对远程控制引入两项提升。
首先,会话不在脆弱,意思是网络闪断或其他传输中断的情况下,会话不会断开。即使没有显示使用会话对象时,你也可以用到这项提升。因此,你获得了更稳定的的连接。
在第3
版本中,你必须显示使用一项功能:断开会话。比如你正在以用Admin1
(是Domain Admins
组成员)的身份连接到名称为Computer1
的计算机上,并创建一个连接到名称为COMPUTER2
的连接。然后你断开连接。该操作仍然是在Computer1
上进行的。当你完成该操作后,它会断开两台计算机之间的连接,但是会在Computer2
上保留一份PowerShell
的副本。在PowerShell
早期版本中,断开连接的Session
会被丢弃,所以无需清理工作。在第3
版中,未被回收的会话可能导致一些问题,这意味着你必须负责起回收工作。
但最酷的地方在于,我们可以登录另一台计算机,也就是COMPUTER3
上,用同样的域账号Admin1
,并获取运行在COMPUTER2
上的会话列表,并重新连接。
在PowerShell
的WSMAN:Drive
,你可以发现大量帮助你管理已断开会话的设置。你还可以通过组策略对多数配置进行中心化管理。需要虚招的关键设置如下。
在WSMan:\localhost\Shell
下:
- -IdleTimeout指定当远程
Shell
中没有用户活动时,远程Shell
将保持打开状态的最长时间。在指定时间过后,远程Shell
将被自动删除。默认值是2000
小时,或84
天 - -MaxConcurrentUsers指定可以在同一计算机上通过远程
Shell
同时执行远程操作的最大用户数 - -MaxShellRunTime指定会话可以打开的最初时间。默认值无限。请记住,
IdleTimeout
参数可以覆盖此参数 - -MaxShellsPerUser指定任何用户可以在同一系统上远程打开的并发
Shell
的最大数目。该值与MaxConcurrentUsers
相乘,可以得到计算机上所有用户做大会话数据量的值
在WSMan:\localhost\Service
下:
- -MaxConnections设置连接到整个远程控制架构下的连接数上限。即使你设置了每个用户可以运行的
Shell
数量或上限值的用户,该参数也会限制传入连接
高级远程控制配置
使用其他端点
在PowerShell
中,端点也被称为会话配置(session configurations
)。举例来说,在64
位机器上启用远程控制会同时为32
位PowerShell
和64
位PowerShell
各启用一个端点,其中64
位PowerShell
是默认端点。如果你有管理员权限,可以在任意计算机上运行下述命令,获得可用的会话配置列表。
你可能注意到,我们的64
位系统中有一个运行32
位PowerShell
的备用端点:“microsoft.powershell32
”用于兼容的目的。如果你希望连接到备用端点,只需要在命令中使用-ConfigurationName
参数指定端点名称。
Enter-PSSession -ComputerName Server-R2 -ConfigurationName 'microsoft.powershell32'
创建自定义端点
创建自定义端点可用分为以下两步。
1、通过New-PSSessionConfigurationFile
命令创建一个新的会话配置文件,该文件的扩展名为.PSSC
。该文件用于定义端点的特点,特征主要指的是该端点允许运行的命令和功能。
2、通过Register-PSSessionConfiguration
命令载入.PSSC文
件,并在WinRm
服务中创建新的端点。在注册过程中,可用设置多个可选参数,比如谁可连接到端点。也可在必要时通过命令Set-PSSessionConfiguration
改变设置。
我们可以创建一个只有域中HelpDesk
组的成员可以访问的端点。在端点内,我们启用与网络适配器相关的命令,并且只允许这些命令。我们不打算给HelpDesk
组运行命令的权限,仅仅是让他们可以查看命令。我们还配置端点从而可以在我们提供的备用凭据下运行命令,因此可以使HelpDesk
组可以在自身无需拥有执行命令的权限时执行命令。
New-PSSessionConfigurationFile -Path C:\HelpDeskEndpoint.pssc -ModulesToImport NetAdapter -SessionType RestrictedRemoteServer -CompanyName "Our Company" -Author "Don Jones" -Description "Net adapter commands for use by help desk" -PowerShellVersion '5.1'
参数的简单说明。
- -Path参数是必须的,并且你提供的文件名称必须以
.PSSC
结尾 - -ModulesToImoport列出组件(
NetAdapter
),我们只希望对本端点只有该组件可用 - -SessionType RestrictedRemoteServer除了一些必要的命令,移除所有
PowerShell
核心命令。该列表会很小,仅包含Select-Object
、Measure-Object
、Get-Command
、Get-Help
、Exit-PSSession
等 - -PowerShellVersion默认是
5.1
创建完会话配置文件之后,可用同过下述命令使配置文件生效。
Register-PSSessionConfiguration -Path .\HelpDeskEndpoint.pssc -RunAsCredential COMPANY\HelpDeskProxyAdmin -ShowSecurityDescriptorUI -Name HelpDesk
这就创建好了一个HelpDesk
的新端点。其会提示我们输入COMPANY\HelpDeskProxyAdmin
的账号和密码;该端点运行的所有命令都通过该账号的身份运行,所有要确保账号所有的权限。中间会出现几次“是否继续运行”的提示,请仔细阅读提示。因为-ShowSecurityDescriptorUI
参数,该命令还提供了图形化对话框指定哪个用户可以连接到端点。我们可用以下方式使用此新端点。
Enter-PSSession -ComputerName SS1 -ConfiguraionName HelpDesk
深入远程控制身份验证
PowerShell
远程控制采用了双向身份验证,这意味着远程控制计算机必须向你证明它的身份。换句话说,当你执行Enter-PSSession -ComputerName DC01
,名称为DC01
的计算机必须在连接建立完成之前证明它就是DC01
。
微软期望更多是在域环境下使用PowerShell
。因此可以通过活动目录列出的实际计算机名称连接到计算机,域会为你处理双向身份验证。该技巧需要你的计算机名称满足以下两点。
- 名称可以被解析为
IP
地址 - 名称必须与活动目录中的计算机名称匹配
如果不满足这些条件,则无法实现双向身份验证,也就无法远程。这样你只能选择:SSL
或者“受信任的主机”。
要想通过SSL
实现双向身份验证,你必须获得目标计算机的SSL
数字证书。而通过受信任的主机实现双向身份验证相对简单,但是更危险,这是由于该技术主要是对于选定的主机关闭双向身份验证。在开始之前,你需要足够自信的声明“不会有任何人冒充这几台计算机中的任何一台,或者入侵DNS
记录”。这里有一个通过本地安全策略的指南来实现的步骤。
在任意GPO
或本地计算机处理编辑器中,步骤如下。
- 展开计算机配置
- 展开管理模块
- 展开
Windows
组 - 展开
Windows
远程控制管理 - 展开
WinRM
客户端 - 双击受信任的主机
- 启用策略并添加信任的主机列表,多个可通过逗号分隔,如“
*.company.com
,*.sales.company.com.
”
总结
以上是对PowerShell
的远程进行了详细的介绍,在接下来的章节中我们将介绍PowerShell
的函数和脚本。