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 远程会话操作。
超级会员免费看
80

被折叠的 条评论
为什么被折叠?



