55、PowerShell远程会话操作全解析

PowerShell远程会话操作全解析

1. 远程会话清理与验证

在进行远程会话操作时,清理和验证是重要的环节。首先,使用以下代码进行清理操作:

$command = [ScriptBlock]::Create($script)
Invoke-Command $computername {
    schtasks /DELETE /TN 'Enable CredSSP' /F } -Cred $credential

上述代码中, Invoke-Command 用于在远程计算机上执行命令,这里是删除名为 Enable CredSSP 的计划任务。

接下来进行输出验证:

Invoke-Command $computername {
    Get-WmiObject Win32_ComputerSystem } -Auth CredSSP -Cred $credential

此代码通过 Get-WmiObject 获取远程计算机的系统信息,验证远程会话是否正常。完成这些配置步骤后,远程会话将拥有不受限制的网络访问权限。

2. 向远程会话传递变量

2.1 问题描述

当需要在远程计算机上调用命令,并将本地的一些动态信息作为参数传递时,就会遇到如何传递变量的问题。

2.2 PowerShell 3.0 及以上版本解决方案

在 PowerShell 3.0 及以上版本中,可以使用 $USING: 前缀来传递本地变量。示例如下:

PS > $s = New-PSSession
PS > $myNumber = 10
PS > Invoke-Command $s { 2 * $myNumber }
0
PS > Invoke-Command $s { 2 * $USING:myNumber }
20

在上述代码中,当直接使用 $myNumber 时,它引用的是远程会话中的变量;而使用 $USING:myNumber 时,PowerShell 会从本地会话中获取该变量的值。

2.3 PowerShell 2.0 解决方案

在 PowerShell 2.0 中,传递变量的方法相对复杂。可以使用 Invoke-Command -ArgumentList 参数,示例如下:

PS > $cred = Get-Credential
PS > $command = {
    param($cred)
    Invoke-Command leeholmes1c23 {
        "Hello from $($env:Computername)" } -Credential $cred
}
PS > Invoke-Command RemoteComputer $command -ArgumentList $cred -Credential $cred
Hello from LEEHOLMES1C23

在这个示例中,通过 -ArgumentList 参数将本地的 $cred 变量传递给远程脚本。需要注意的是,传递的参数会经过序列化过程,序列化后的对象虽然属性与原对象相似,但不再具有方法。

3. 配置高级远程会话配额和选项

3.1 问题描述

在远程会话中,可能需要配置压缩、配置文件、代理认证、证书验证或文化信息等高级选项。

3.2 客户端配置

客户端配置可以使用 New-PSSessionOption cmdlet,示例如下:

PS > $options = New-PSSessionOption -Culture "fr-CA"
PS > $sess = New-PSSession Lee-Desk -Cred Lee -SessionOption $options
PS > Invoke-Command $sess { Get-Date | Out-String }
20 février 2010 17:40:16

在上述代码中,通过 -Culture 参数设置了远程会话的文化信息为法语(加拿大)。

3.3 服务器端配置

服务器端配置可以查看 WSMan:\localhost\Shell WSMan:\localhost\Service 下的选项。例如,设置每个用户的最大 shell 数量:

Set-Item WSMan:\localhost\shell\MaxShellsPerUser 10

以下是 WSMan:\localhost\Shell 下的常见配置选项表格:
| Type | Name | SourceOfValue | Value |
| ---- | ---- | ------------- | ----- |
| System.String | AllowRemoteShellAccess | | true |
| System.String | IdleTimeout | | 7200000 |
| System.String | MaxConcurrentUsers | | 10 |
| System.String | MaxShellRunTime | | 2147483647 |
| System.String | MaxProcessesPerShell | | 25 |
| System.String | MaxMemoryPerShellMB | | 1024 |
| System.String | MaxShellsPerUser | | 30 |

除了服务器全局设置,还可以通过配置单个端点来进一步限制设置。例如:

PS WSMan:\localhost\Plugin\Restrictive\Quotas> Set-Item MaxShellsPerUser 1

需要注意的是,更新后的配置只有在小于或等于全局配额时才有效,并且配置更改需要重启 WinRM 服务才能生效。

4. 在多台计算机上调用命令

4.1 问题描述

在传统的网络脚本中,同时管理多台计算机是一个难题,因为远程计算机管理通常受网络限制,大部分时间都在等待网络响应。

4.2 解决方案

PowerShell 提供了 -ThrottleLimit -AsJob 参数来解决这个问题。示例如下:

PS > $sessions = $(
    New-PSSession localhost;
    New-PSSession localhost;
    New-PSSession localhost)
PS > $start = Get-Date
PS > Invoke-Command $sessions { Start-Sleep 2; "Test $pid" }
Test 720
Test 6112
Test 4792
PS > (Get-Date) - $start | Select TotalSeconds | Format-Table -Auto
TotalSeconds
------------
     2.09375
PS > 
PS > $start = Get-Date
PS > Invoke-Command $sessions { Start-Sleep 2; "Test $pid" } -ThrottleLimit 1
Test 6112
Test 4792
Test 720
PS > (Get-Date) - $start | Select TotalSeconds | Format-Table -Auto
TotalSeconds
------------
        6.25

在上述代码中,默认情况下,PowerShell 会同时连接 32 台计算机。通过 -ThrottleLimit 参数可以限制同时连接的计算机数量。当使用 -AsJob 参数时,可以将命令作为 PowerShell 作业在后台运行,这样可以在执行长时间任务时继续使用本地 shell。

5. 在远程计算机上运行本地脚本

5.1 问题描述

当有一个本地脚本,需要在远程计算机上运行时,该如何操作呢?

5.2 解决方案

可以使用 Invoke-Command -FilePath 参数来实现。示例如下:

PS > Get-Content .\Get-ProcessByName.ps1
param($name)
Get-Process -Name $name
PS > Invoke-Command -Computername Lee-Desk `
    -FilePath .\Get-ProcessByname.ps1 -ArgumentList PowerShell `
    -Cred Lee

上述代码中, -FilePath 指定了本地脚本的路径, -ArgumentList 传递了脚本所需的参数。需要注意的是,PowerShell 在运行脚本时不会处理依赖关系,因此要确保远程计算机上有脚本所需的其他脚本、命令和环境依赖。

6. 向远程计算机传输文件

6.1 问题描述

在与远程计算机交互时,如何将本地的工具和环境带到远程计算机是一个常见问题。使用文件共享或 FTP 传输并不总是可行的。

6.2 解决方案

可以使用以下的 Send-File.ps1 脚本通过 PowerShell 远程连接传输文件:

<#
.SYNOPSIS
Sends a file to a remote session.
.EXAMPLE
PS > $session = New-PsSession leeholmes1c23
PS > Send-File c:\temp\test.exe c:\temp\test.exe $session
#>
param(
    ## The path on the local computer
    [Parameter(Mandatory = $true)]
    $Source,
    ## The target path on the remote computer
    [Parameter(Mandatory = $true)]
    $Destination,
    ## The session that represents the remote computer
    [Parameter(Mandatory = $true)]
    [System.Management.Automation.Runspaces.PSSession] $Session
)
Set-StrictMode -Version 3
$remoteScript = {
    param($destination, $bytes)
    ## Convert the destination path to a full filesystem path (to support
    ## relative paths)
    $Destination = $executionContext.SessionState.`
        Path.GetUnresolvedProviderPathFromPSPath($Destination)
    ## Write the content to the new file
    $file = [IO.File]::Open($Destination, "OpenOrCreate")
    $null = $file.Seek(0, "End")
    $null = $file.Write($bytes, 0, $bytes.Length)
    $file.Close()
}
## Get the source file, and then start reading its content
$sourceFile = Get-Item $source
## Delete the previously-existing file if it exists
Invoke-Command -Session $session {
    if(Test-Path $args[0]) { Remove-Item $args[0] }
} -ArgumentList $Destination
## Now break it into chunks to stream
Write-Progress -Activity "Sending $Source" -Status "Preparing file"
$streamSize = 1MB
$position = 0
$rawBytes = New-Object byte[] $streamSize
$file = [IO.File]::OpenRead($sourceFile.FullName)
while(($read = $file.Read($rawBytes, 0, $streamSize)) -gt 0)
{
    Write-Progress -Activity "Writing $Destination" `
        -Status "Sending file" `
        -PercentComplete ($position / $sourceFile.Length * 100)
    ## Ensure that our array is the same size as what we read
    ## from disk
    if($read -ne $rawBytes.Length)
    {
        [Array]::Resize( [ref] $rawBytes, $read)
    }
    ## And send that array to the remote system
    Invoke-Command -Session $session $remoteScript `
        -ArgumentList $destination,$rawBytes
    ## Ensure that our array is the same size as what we read
    ## from disk
    if($rawBytes.Length -ne $streamSize)
    {
        [Array]::Resize( [ref] $rawBytes, $streamSize)
    }

    [GC]::Collect()
    $position += $read
}
$file.Close()
## Show the result
Invoke-Command -Session $session { Get-Item $args[0] } -ArgumentList $Destination

该脚本的工作流程如下:
1. 读取本地文件内容到字节数组。
2. 将字节数组分割成 1MB 的块。
3. 将每个块流式传输到远程系统。
4. 远程系统将块重新组合成目标文件。

通过这种方式,可以优化网络效率,并避免配额问题。

7. 判断脚本是否在远程计算机上运行

7.1 问题描述

有时需要知道脚本是在本地计算机还是远程计算机上运行。

7.2 解决方案

可以通过查看 $host.Name 属性的值来判断。如果值为 ServerRemoteHost ,则脚本在远程计算机上运行;否则,在本地计算机上运行。示例如下:

PS > $host.Name
ConsoleHost
PS > Invoke-Command leeholmes1c23 { $host.Name }
ServerRemoteHost

需要注意的是,虽然控制台主机的 $host.Name 值为 ConsoleHost ,但不能仅依赖此值来判断脚本是否在本地运行,因为还有其他 PowerShell 主机,它们的 $host.Name 值各不相同,但都不是 ServerRemoteHost

8. 创建特定任务的远程端点

8.1 问题描述

在某些情况下,需要创建一个 PowerShell 端点,限制可以运行的命令。

8.2 解决方案

可以使用 New-PSSessionConfigurationFile 命令创建会话配置文件,然后使用 Register-PSSessionConfiguration cmdlet 创建基于该配置的端点。示例如下:

PS > $s = New-PSSessionConfigurationFile -Path c:\temp\restricted.pssc `
    -VisibleCmdlets Get-Random -SessionType RestrictedRemoteServer
PS > Register-PSSessionConfiguration -Name Random `
    -Path c:\temp\restricted.pssc -Force
PS > $s = New-PSSession -ConfigurationName Random
PS > Invoke-Command $s { Get-Process }
The term 'Get-Process' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
    + CategoryInfo          : ObjectNotFound: (Get-Process:String) []
    + FullyQualifiedErrorId : CommandNotFoundException
    + PSComputerName        : localhost
PS > Invoke-Command $s { 1+1 }
The syntax is not supported by this runspace. This might be because it is in
no-language mode.
    + CategoryInfo          : ParserError: ( 1+1 :String) [], ParseException
    + FullyQualifiedErrorId : ScriptsNotAllowed
    + PSComputerName        : localhost
PS > Invoke-Command $s { Get-Random }
1967230856

上述代码中,创建了一个名为 Random 的受限端点,只允许运行 Get-Random 命令。

8.3 受限运行空间原理

PowerShell 提供了受限运行空间机制,通过会话配置文件可以限制许多方面,如支持的 cmdlet、语言模式、默认变量等。当会话配置文件无法满足复杂需求时,可以使用启动脚本来补充。许多受限运行空间实现了可信子系统,命令可以在具有更高权限的用户账户下运行。

以下是一个创建只暴露 Get-Inventory 命令的端点的启动脚本示例:

<#
.SYNOPSIS
Serves as the configuration script for a custom remoting endpoint that
exposes only the Get-Inventory custom command.
.EXAMPLE
PS >Register-PsSessionConfiguration Inventory `
    -StartupScript 'C:\Program Files\Endpoints\Inventory.ps1'
PS >Enter-PsSession leeholmes1c23 -ConfigurationName Inventory
[leeholmes1c23]: [Inventory] > Get-Command
CommandType     Name                          Definition
-----------     ----                          ----------
Function        Exit-PSSession                [CmdletBinding()]...
Function        Get-Command                   [CmdletBinding()]...
Function        Get-FormatData                [CmdletBinding()]...
Function        Get-Help                      [CmdletBinding()]...
Function        Get-Inventory                 ...
Function        Measure-Object                [CmdletBinding()]...
Function        Out-Default                   [CmdletBinding()]...
Function        prompt                        ...
Function        Select-Object                 [CmdletBinding()]...
[leeholmes1c23]: [Inventory] > Get-Inventory
SystemDirectory : C:\Windows\system32
Organization    :
BuildNumber     : 6002
RegisteredUser  : Lee Holmes
SerialNumber    : 89580-433-1295803-71477
Version         : 6.0.6002
[leeholmes1c23]: [Inventory] > 1+1
The syntax is not supported by this runspace. This might be because it is
in no-language mode.
    + CategoryInfo          :
    + FullyQualifiedErrorId : ScriptsNotAllowed
[leeholmes1c23]: [Inventory] > Exit-PsSession
PS >
#>
Set-StrictMode -Off
## Create a new function to get inventory
function Get-Inventory
{
    Get-WmiObject Win32_OperatingSystem
}
## Customize the prompt
function Prompt
{
    "[Inventory] > "
}
## Remember which functions we want to expose to the user
$exportedCommands = "Get-Inventory","Prompt"
## The System.Management.Automation.Runspaces.InitialSessionState class
## has a CreateRestricted() method that creates a default locked-down
## secure configuration for a remote session. This configuration only
## supports the bare minimum required for interactive remoting.
$issType = [System.Management.Automation.Runspaces.InitialSessionState]
$iss = $issType::CreateRestricted("RemoteServer")
## Add the commands to a hashtable so that we can access them easily
$issHashtable = @{}
foreach($command in $iss.Commands)
{
    $issHashtable[$command.Name + "-" + $command.CommandType] = $command
}
## Go through all of functions built into the restricted runspace and add
## them to this session. These are proxy functions to limit the functionality
## of commands that we need (such as Get-Command, Select-Object, etc.)
foreach($function in $iss.Commands |
    Where-Object { $_.CommandType -eq "Function" })
{
    Set-Content "function:\$($function.Name)" -Value $function.Definition
}
## Go through all of the commands in this session
foreach($command in Get-Command)
{
    ## If it was one of our exported commands, keep it Public
    if($exportedCommands -contains $command.Name) { continue }
    ## If the current command is defined as Private in the initial session
    ## state, mark it as private here as well.
    $issCommand = $issHashtable[$command.Name + "-" + $command.CommandType]
    if((-not $issCommand) -or ($issCommand.Visibility -ne "Public"))
    {
        $command.Visibility = "Private"
    }
}
## Finally, prevent all access to the PowerShell language
$executionContext.SessionState.Scripts.Clear()
$executionContext.SessionState.Applications.Clear()
$executionContext.SessionState.LanguageMode = "NoLanguage"

该脚本创建了一个名为 Inventory 的端点,只允许运行 Get-Inventory 命令,并自定义了提示符。同时,通过 CreateRestricted() 方法创建了一个安全的远程会话配置,限制了命令的功能和 PowerShell 语言的使用。

9. 相关操作总结与对比

9.1 变量传递方式对比

PowerShell 版本 变量传递方式 示例代码 特点
3.0 及以上 使用 $USING: 前缀 powershell<br>PS > $s = New-PSSession<br>PS > $myNumber = 10<br>PS > Invoke-Command $s { 2 * $USING:myNumber }<br>20<br> 简单直观,直接引用本地变量值
2.0 使用 -ArgumentList 参数 powershell<br>PS > $cred = Get-Credential<br>PS > $command = {<br> param($cred)<br> Invoke-Command leeholmes1c23 {<br> "Hello from $($env:Computername)" } -Credential $cred<br>}<br>PS > Invoke-Command RemoteComputer $command -ArgumentList $cred -Credential $cred<br>Hello from LEEHOLMES1C23<br> 稍复杂,参数需序列化,序列化后对象无方法

9.2 远程执行命令方式对比

执行场景 执行方式 示例代码 适用情况
简单命令 使用 -ScriptBlock 参数 powershell<br>PS > Invoke-Command Lee-Desk { Get-Process -n PowerShell } -Cred Lee<br> 快速执行简单命令
本地脚本 使用 -FilePath 参数 powershell<br>PS > Get-Content .\Get-ProcessByName.ps1<br>param($name)<br>Get-Process -Name $name<br>PS > Invoke-Command -Computername Lee-Desk `<br> -FilePath .\Get-ProcessByname.ps1 -ArgumentList PowerShell `<br> -Cred Lee<br> 执行本地复杂脚本,需确保远程有依赖

9.3 会话配置方式对比

配置端 配置方式 示例代码 配置内容
客户端 使用 New-PSSessionOption cmdlet powershell<br>PS > $options = New-PSSessionOption -Culture "fr-CA"<br>PS > $sess = New-PSSession Lee-Desk -Cred Lee -SessionOption $options<br>PS > Invoke-Command $sess { Get-Date | Out-String }<br>20 février 2010 17:40:16<br> 配置压缩、文化等客户端选项
服务器端 查看 WSMan 路径选项 powershell<br>Set-Item WSMan:\localhost\shell\MaxShellsPerUser 10<br> 配置服务器全局及端点配额等选项

10. 操作流程梳理

10.1 远程文件传输流程

graph LR
    A[读取本地文件] --> B[分割成 1MB 块]
    B --> C[流式传输到远程]
    C --> D[远程组合成文件]

10.2 创建受限端点流程

graph LR
    A[创建会话配置文件] --> B[注册会话配置]
    B --> C[创建基于配置的端点]
    C --> D[测试端点命令]

11. 常见问题及解决办法

11.1 命令未找到问题

当在远程会话中执行命令出现 CommandNotFoundException 时,可能是因为端点配置限制了命令。例如:

PS > $s = New-PSSession -ConfigurationName Random
PS > Invoke-Command $s { Get-Process }
The term 'Get-Process' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.

解决办法:检查端点配置文件,确保所需命令在 VisibleCmdlets 列表中。

11.2 脚本语法不支持问题

当执行命令出现 ParserError 提示脚本语法不支持时,可能是因为运行空间处于 NoLanguage 模式。例如:

PS > Invoke-Command $s { 1+1 }
The syntax is not supported by this runspace. This might be because it is in
no-language mode.

解决办法:检查会话配置文件或启动脚本,调整语言模式相关设置。

11.3 网络超时问题

在执行远程命令时,可能会遇到网络超时问题。可以通过设置 New-PSSessionOption 的超时参数来解决,例如:

PS > $options = New-PSSessionOption -OperationTimeout 00:05:00
PS > $sess = New-PSSession Lee-Desk -Cred Lee -SessionOption $options

12. 总结与应用建议

12.1 总结

本文详细介绍了 PowerShell 远程会话的多种操作,包括清理验证、变量传递、会话配置、多机命令调用、脚本运行、文件传输、脚本运行位置判断以及受限端点创建等。不同的操作有不同的适用场景和实现方式,需要根据具体需求选择合适的方法。

12.2 应用建议

  • 在进行远程操作前,先进行清理和验证操作,确保远程环境的干净和可用。
  • 对于变量传递,根据 PowerShell 版本选择合适的方式,3.0 及以上优先使用 $USING: 前缀。
  • 配置会话时,根据实际需求调整客户端和服务器端的选项,如文化信息、配额限制等。
  • 在管理多台计算机时,合理使用 -ThrottleLimit -AsJob 参数,提高效率。
  • 执行本地脚本时,确保远程计算机有脚本所需的依赖。
  • 创建受限端点时,根据安全需求限制可运行的命令,保障系统安全。

通过合理运用这些操作,可以更高效、安全地进行 PowerShell 远程会话操作。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值