跳到主要内容

远程管理

PowerShell Remoting 是 PowerShell 的核心功能之一,允许管理员在远程计算机上执行命令和脚本。通过 WinRM(Windows Remote Management)或 SSH,可以实现跨服务器的批量管理和自动化运维。

远程管理概述

支持的远程协议

PowerShell 支持多种远程连接协议:

WinRM:Windows 原生远程管理协议,基于 WS-Management 标准。Windows 环境下的首选方式。

SSH:跨平台远程协议,PowerShell 6+ 支持,适用于 Linux/macOS 和混合环境。

远程管理模式

1:1 远程会话:使用 Enter-PSSession 进入交互式远程会话。

1: 多远程执行:使用 Invoke-Command 在多台计算机上执行命令。

启用 WinRM 远程管理

检查 WinRM 状态

Get-Service WinRM
Test-WSMan

启用远程管理

在目标计算机上运行:

Enable-PSRemoting -Force

此命令会:

  • 启动 WinRM 服务
  • 设置服务自动启动
  • 创建 WinRM 监听器
  • 配置防火墙规则

配置可信主机

在工作组环境中,需要配置可信主机:

Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.1.100" -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force

查看当前配置:

Get-Item WSMan:\localhost\Client\TrustedHosts

防火墙配置

确保防火墙允许 WinRM 连接:

Enable-NetFirewallRule -Name "WINRM-HTTP-In-TCP"
Enable-NetFirewallRule -Name "WINRM-HTTPS-In-TCP"

交互式远程会话

Enter-PSSession

进入远程交互式会话:

Enter-PSSession -ComputerName "Server01"
Enter-PSSession -ComputerName "192.168.1.100" -Credential (Get-Credential)

会话中执行命令:

[Server01]: PS C:\Users\admin\Documents> Get-Process
[Server01]: PS C:\Users\admin\Documents> Get-Service

退出会话:

[Server01]: PS C:\Users\admin\Documents> Exit-PSSession

使用会话对象

创建并复用会话:

$session = New-PSSession -ComputerName "Server01" -Credential (Get-Credential)

Enter-PSSession -Session $session
Exit-PSSession

Invoke-Command -Session $session -ScriptBlock { Get-Process }

Remove-PSSession -Session $session

远程命令执行

Invoke-Command

在远程计算机上执行命令:

Invoke-Command -ComputerName "Server01" -ScriptBlock { Get-Process }

传递凭据:

$cred = Get-Credential
Invoke-Command -ComputerName "Server01" -Credential $cred -ScriptBlock {
Get-Service
}

批量执行

在多台计算机上执行:

$servers = "Server01", "Server02", "Server03"
Invoke-Command -ComputerName $servers -ScriptBlock {
Get-Service -Name "WinRM"
}

传递本地变量

使用 -ArgumentList 传递参数:

$path = "C:\Logs"
Invoke-Command -ComputerName "Server01" -ScriptBlock {
param($p)
Get-ChildItem $p
} -ArgumentList $path

使用 $using: 作用域(PowerShell 3.0+):

$path = "C:\Logs"
Invoke-Command -ComputerName "Server01" -ScriptBlock {
Get-ChildItem $using:path
}

执行本地脚本

在远程计算机上执行本地脚本文件:

Invoke-Command -ComputerName "Server01" -FilePath "C:\Scripts\Check-System.ps1"

会话管理

New-PSSession

创建持久会话:

$session = New-PSSession -ComputerName "Server01"

创建多个会话:

$sessions = New-PSSession -ComputerName "Server01", "Server02", "Server03"

Get-PSSession

查看当前会话:

Get-PSSession
Get-PSSession -ComputerName "Server01"

Remove-PSSession

断开会话:

Remove-PSSession -Id 1
Remove-PSSession -ComputerName "Server01"
Get-PSSession | Remove-PSSession

Disconnect-PSSession / Connect-PSSession

断开并重新连接会话:

$session = New-PSSession -ComputerName "Server01"
Disconnect-PSSession -Session $session

Connect-PSSession -Session $session

SSH 远程连接

配置 SSH 远程

在 Linux/macOS 或 Windows 上使用 SSH:

Enter-PSSession -HostName "192.168.1.100" -UserName "admin"

使用 SSH 配置文件:

Enter-PSSession -HostName "server01.example.com" -UserName "admin" -KeyFilePath "~/.ssh/id_rsa"

SSH 远程执行

Invoke-Command -HostName "server01", "server02" -UserName "admin" -ScriptBlock {
Get-Process
}

高级远程配置

配置会话配置

查看会话配置:

Get-PSSessionConfiguration

注册自定义会话配置:

Register-PSSessionConfiguration -Name "AdminSession" -MaximumReceivedObjectSizeMB 50

使用自定义配置:

New-PSSession -ComputerName "Server01" -ConfigurationName "AdminSession"

约束委派

使用 CredSSP 进行凭据委派:

Enable-WSManCredSSP -Role Client -DelegateComputer "Server01"
Enable-WSManCredSSP -Role Server

Invoke-Command -ComputerName "Server01" -Authentication CredSSP -Credential $cred -ScriptBlock {

}

Just Enough Administration (JEA)

JEA 限制远程用户可执行的命令:

Register-PSSessionConfiguration -Name "JEA_DNS" -Path "C:\JEA\JEAConfig.pssc"

远程管理最佳实践

使用 SSL 加密

配置 HTTPS 监听器:

$cert = New-SelfSignedCertificate -DnsName "Server01" -CertStoreLocation "Cert:\LocalMachine\My"
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * -CertificateThumbprint $cert.Thumbprint -Force

连接使用 SSL:

New-PSSession -ComputerName "Server01" -UseSSL -SessionOption (New-PSSessionOption -SkipCACheck)

会话限制

限制并发会话数:

Set-Item WSMan:\localhost\Shell\MaxConcurrentUsers 10
Set-Item WSMan:\localhost\Shell\MaxShellsPerUser 5

错误处理

try {
$session = New-PSSession -ComputerName "Server01" -ErrorAction Stop
Invoke-Command -Session $session -ScriptBlock { Get-Process }
}
catch {
Write-Error "远程连接失败: $_"
}
finally {
if ($session) {
Remove-PSSession -Session $session
}
}

实用示例

批量获取服务器状态

$servers = Get-Content -Path "C:\Servers.txt"

$results = Invoke-Command -ComputerName $servers -ScriptBlock {
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
OS = (Get-CimInstance Win32_OperatingSystem).Caption
CPU = (Get-CimInstance Win32_Processor).Name
FreeMemory = [math]::Round((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB, 2)
DiskFree = (Get-Volume -DriveLetter C).SizeRemaining / 1GB
Uptime = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
}
} -ErrorAction SilentlyContinue

$results | Format-Table -AutoSize

远程部署服务

$servers = "Web01", "Web02", "Web03"
$sourcePath = "\\FileServer\Deploy\MyService"
$destPath = "C:\Services\MyService"

Invoke-Command -ComputerName $servers -ScriptBlock {
param($source, $dest)

if (!(Test-Path $dest)) {
New-Item -Path $dest -ItemType Directory -Force
}

Copy-Item -Path "$source\*" -Destination $dest -Recurse -Force

$service = Get-Service -Name "MyService" -ErrorAction SilentlyContinue
if ($service) {
Restart-Service -Name "MyService" -Force
} else {
New-Service -Name "MyService" -BinaryPathName "$dest\MyService.exe" -StartupType Automatic
Start-Service -Name "MyService"
}

Write-Host "部署完成: $env:COMPUTERNAME"
} -ArgumentList $sourcePath, $destPath

远程日志收集

$servers = "Server01", "Server02"
$logPath = "C:\CollectedLogs"

if (!(Test-Path $logPath)) {
New-Item -Path $logPath -ItemType Directory -Force
}

foreach ($server in $servers) {
$session = New-PSSession -ComputerName $server

$logs = Invoke-Command -Session $session -ScriptBlock {
Get-ChildItem -Path "C:\Logs" -Filter "*.log" -Recurse |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) }
}

foreach ($log in $logs) {
$remotePath = $log.FullName
$localPath = Join-Path $logPath "$($server)_$($log.Name)"

Invoke-Command -Session $session -ScriptBlock {
param($path)
Get-Content $path
} -ArgumentList $remotePath | Out-File -FilePath $localPath
}

Remove-PSSession -Session $session
}

下一步

掌握了远程管理后,接下来学习 脚本安全,了解 PowerShell 的安全最佳实践。