概要

每次使用Invoke-CommandEnter-PSSession命令连接远程计算机时,至少需要指定计算机名称 。根据具体环境的不同,可能还需要指定备用凭据,这意味着需要提示你输入密码。或许还需要指定备用端口或身份验证机制,这取决于当前组织如何配置远程控制。

有一种更好的办法,来避免进行重复性操作,那就是可重用会话

创建并使用可重用会话

会话是一个在你的PowerShell副本与远程PowerShell副本之间的持久化连接。当一个会话处于活动状态时,你的计算机与远程计算机都会划分出一小部分用于维护连接的内存和处理器时间,还有非常少一部分与连接相关的网络流量。PowerShell维护一个所有已打开的会话列表,你可以使用这些会话调用命令或进入远程Shell。
通过New-PSSesion这个Cmdlet创建一个新的会话,可以指定一个或多个计算机名称。如果需要,还可以指定备用用户名称、端口以及身份验证机制等。结果是一个存在PowerShell内存中的会话对象。
image.png
通过Get-PSSession获取创建好的会话:
image.png
image.png
image.png
实际使用中,更倾向于创建Session后立刻将其存入变量。
例如,有3个基于IIS的Web服务器,它需要定期通过Invode-Command命令配置这些服务器。为了让过程变得简单,可将这些会话存入特定变量。
image.png

移除会话 - Remove-PSSession

使用Remove-PSSession这个Cmdlet关闭会话。比如说,只关闭连接到IIS的会话,可以使用下面的命令。
image.png
如果希望关闭所有处于开启状态的会话,使用下面的命令:
image.png
如果变量中包含多个会话,关闭其中一个,则不影响访问该变量中的其他会话(也可以使用断开会话的方式)。
image.png
image.png

补充说明

有一个语法允许使用一个命令创建多个会话,并将每个会话赋值给对应的变量。

  1. $server1,$server2 = New-PSSession -ComputerName printer, vpn-server

该语法将连接到printer服务器的会话存入变量$server1,将连接到vpn-server服务器的会话存入$server2。
但是请小心使用:我们曾见过会话的顺序和计算机名称的顺序不完全一致,导致$server1最终包含连接到vpn-server而不是printer的会话。你可以将变量内容显示出来,从而查看会话连接到哪一台计算机。
image.png

使用会话 - Enter-PSSession

Enter-PSSession命令是用于进入远程计算机一对一的交互式Shell所用的命令。该命令的参数可以是一个会话对象,而不是具体的计算机名称。
image.png
由于$session变量中包含两个会话对象,必须通过索引指定使用其中哪一个会话对象。
image.png
可以看到命令提示符已经改变,表示已经在控制远程计算机。Exit-PSSession命令用于帮助我们返回到本地提示符,但远程会话并不会中断,以便于后续使用。
image.png

使用会话对象属性

$sessions对象通过管道传递给Gm命令时,可以得到如下输出结果。
image.png
可以看到会话对象包含一个名为ComputerName的属性。这意味着,可以通过其筛选出指定计算机名称的会话。

  1. PS C:\> Enter-PSSession -Session ($sessions | where {$_.computername -eq 'printer'})
  2. [printer]: PS C:\Users\rick\Documents>

获取指定会话 - Get-PSSession

将会话对象存于变量中,这些会话依然被存于PowerShell的一个打开会话的主列表中。这意味着可以通过Get-PSSession访问这些会话。
image.png
但请注意,如果该会话存在多个,则会报错。
image.png
也可以使用Enter-PSSession的-Session参数,且不会受多个会话的影响。
image.png

提示:为了方便,将会话存入一个变量是可以的。但请记住,PowerShell已经保存了所有已打开会话的列表;将这些会话存入变量,只有在需要一次性引用多个会话时才会变得有用。

利用Invoke-Command使用会话

将会话存入一个$sessions变量,此时使用Invoke-Command命令才能展示Session对象的价值。
image.png
注意,我们将一个Get-WmiObject命令发送到远程计算机。此处本可以选择使用Get-WmiObject命令自带的-computername参数,但是由于下面4个原因,我们没有这么做。

  • 远程控制通过一个预定义的端口进行传输,WMI却不是。远程控制因此针对在防火墙后的计算机更加容易使用,这是由于更容易开启必要的防火墙例外。
    • Windows防火墙包含必要的状态检测,为WMI随机端口选择提供特定的例外,从而使得WMI随机端口选择(也就是端点匹配)可以正常工作,但对于其他第三方防火墙产品来说却难以管理。通过远程控制就容易很多,因为只需要将一个端口设为例外。
  • 将所有的进程传输到本地费时费力。使用Invoke-Command这个Cmdlet,可以让每一台计算机完成各自的工作,并将结果返回。
  • 远程控制并行执行,默认可以连接最多32台计算机。WMI顺序执行,一次只能在一台计算机上执行。
  • 无法通过Get-WmiObject使用我们预定义的会话对象,但可以通过Invoke-Command使用。

Tips:
在PowerShell v3中,新的CIM Cmdlet(比如Get-CimInstance)并不像Get-WmiObject那样有一个-computerName参数。新Cmdlet被设计的本意就是,若希望在远程计算机上执行,请使用Invoke-Command命令。

获取指定会话

Invoke-Command的-Session参数,也可以通过括号命令提供。

  1. PS C:\> Invoke-Command -Command {gwmi -class win32_bios} -Session (Get-PSSession -comp printer)
  2. SMBIOSBIOSVersion : P3.80
  3. Manufacturer : American Megatrends Inc.
  4. Name : P3.80
  5. SerialNumber : To Be Filled By O.E.M.
  6. Version : ALASKA - 1072009
  7. PSComputerName : printer

你或许会期望Invoke-Command可以从管道中接收会话对象,就像Enter-PSSession命令那样。但通过查看Invoke-Command的完整帮助,会发现它并不支持这种使用管道的技巧。

隐式远程控制

隐式远程控制功能并未记入PowerShell文档。

有这么一个场景,需要使用AD模块,但当前客户端系统没有安装或无法安装远程服务器管理工具。此时,就可以使用隐式远程控制。

下面来查看一个示例完整的过程:
image.png
说明:

  • ① 首先,通过与一台装有活动目录模块的远程计算机建立一个会话(必须启用该计算机的远程控制)。
  • ② 告诉远程计算机导入其本地的活动目录模块。
    • 此处可以选择载入任意模块,甚至是在需要时添加一个PSSnapin。
    • 由于会话处于打开状态,该模块将一直在远程计算机上处于被载入状态。
  • ③ 接下来告诉计算机从远程会话中导入命令。
    • 此处只需要活动目录模块中的命令,并在每个命令的名词部分加入“rem”前缀。
    • 这样可以更容易跟踪远程命令。并且从远程会话导入的命令,不会与已经在本地Shell中的命令冲突。
  • ④ PowerShell会在本地计算机创建一个临时模块,用于代表远程命令。
    • 这些命令并不是被复制过来的,而是PowerShell为其创建了指向远程计算机的快捷方式。

现在就可以运行活动目录模块的命令了,甚至是使用帮助命令。我们使用New-remADUser来代替New-ADUser,这是由于我们在命令的名词部分添加了前缀“rem”。该命令在关闭Shell或关闭与远程连接的会话之前一直存在。当打开一个新的Shell时,必须重复上述过程来重新获得访问远程命令的权限

当运行这些命令时,它们并不是在本地计算机上执行,而是隐式地在远程计算机上执行。在远程计算机上执行完成后,将结果发送给本地计算机。

范例:导入RemoteAccess模块
image.png
image.png
Tips:
通过隐式远程连接获取到本地计算机的结果是反序列化的结果,这意味着对象的属性将会复制到一个XML文件中,以便通过网络进行传输,用这种方式收到的对象不会包含任何方法。在大多数情况下,这并不是一个问题。但你希望以编程的方式使用模块或插件时,这些模块或插件对隐式远程控制的支持就不会那么好了。

断开会话

PowerShell v3及以后版本,在网络闪断或其他传输中断的情况下,会话不会断开。即使是在没有显式使用会话对象。即使在使用类似Enter-PSSession和它的-ComputerName参数时,从技术角度,也是在底层使用了会话。

在第3版中,必须显式使用的一项功能:断开会话。比如你正在以用户Admin1(是DomainAdmins组成员)的身份连接到名称为Computer1的计算机上,并创建一个连接到名称为COMPUTER2的连接。
image.png
然后就可以关闭连接,当完成该操作后,它会将两台计算机之间的连接断开,但会在Computer2上保留一份PowerShell的副本。可以通过指定Session的ID号完成该操作,该ID号会在第一次创建Session时显示。
image.png
从上面可知,在COMPUTER2上会保留一份PowerShell的副本处于运行状态。因此为其分配一个适用的超时时间,就变得很重要。

我们还可以用同样的域账号Admin1,登录到另一台计算机(如COMPUTER3上),获取运行在COMPUTER2上的会话列表,并能进行连接。
image.png
image.png
如果以其他用户的身份登录,就无法看到这些会话,即使该身份为管理员。

在PowerShell的WSMAN:Drive,可以发现大量管控已断开会话的设置。你还可以通过组策略对大多数配置进行中心化管理,需要寻找的关键设置如下:
1)在WSMan:\localhost\Shell下:

  • -IdleTimeout:指定当远程Shell中没有用户活动时,远程Shell将保持打开状态的最长时间。
    • 在指定的时间过后,远程Shell将被自动删除。
    • 默认值是2000小时,或84天。
  • -MaxConcurrentUsers:指定可以在同一计算机上,通过远程Shell同时执行远程操作的最大用户数。
  • -MaxShellRunTime:指定会话可以打开的最长时间。默认值为无限。
    • 请记住,IdleTimeout参数可以覆盖该参数
  • -MaxShellsPerUser:指定任何用户可以在同一系统上,远程打开的并发Shell的最大数目。
    • 将该值与MaxConcurrentUsers相乘,可以得到计算机上所有用户最大会话数量的值。

2)在WSMan:\localhost\Service下:

  • -MaxConnections:设置连接到整个远程控制架构下的连接数上限。
    • 即使设置了每个用户可运行的Shell数量或上限值的用户,MaxConnections也会限制传入连接。