适用于 PowerShell 7.0 及以上版本(跨平台)
在传统的 Windows 环境中,PowerShell 远程管理主要依赖 WinRM(Windows Remote Management)协议和 Enter-PSSession、Invoke-Command 等 cmdlet。然而,WinRM 是 Windows 专属协议,无法在 Linux 或 macOS 上运行,这给跨平台运维带来了不小的障碍。
从 PowerShell 7.0 开始,PowerShell 正式支持基于 SSH 的远程连接。通过 SSH 传输层,你可以使用熟悉的 Enter-PSSession 和 Invoke-Command 连接到任何安装了 SSH 服务的远程主机,无论对方运行的是 Windows、Linux 还是 macOS。这让 PowerShell 真正成为了一门跨平台的运维语言。
本文将介绍如何配置 SSH 远程管理环境,以及如何使用 PowerShell 通过 SSH 执行远程命令、管理多台服务器。
环境准备 在使用 SSH 远程管理之前,需要确保本地和远程主机都已正确配置。首先,远程主机上需要安装并运行 SSH 服务(sshd),同时需要安装 PowerShell 7。在 Windows 上,可以通过安装 OpenSSH 可选功能来实现;在 Linux 上则通常使用系统自带的 openssh-server 包。
以下脚本检查本地 SSH 客户端是否可用,并测试到远程主机的连通性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $sshClient = Get-Command ssh -ErrorAction SilentlyContinueif ($sshClient ) { Write-Host "SSH 客户端版本:" ssh -V 2 >&1 } else { Write-Host "未找到 SSH 客户端,请先安装 OpenSSH" -ForegroundColor Red } $remoteHost = "192.168.1.100" $sshTest = Test-Connection -ComputerName $remoteHost -Count 2 -Quiet if ($sshTest ) { Write-Host "主机 $remoteHost 网络可达" } else { Write-Host "主机 $remoteHost 无法访问" -ForegroundColor Yellow }
1 2 3 SSH 客户端版本: OpenSSH_for_Windows_9.5, LibreSSL 3.8.2 主机 192.168.1.100 网络可达
建立 SSH 远程会话 配置好环境后,就可以使用 Enter-PSSession 通过 SSH 连接到远程主机了。与传统的 WinRM 会话不同,SSH 会话需要在 -HostName 参数中指定远程主机地址。如果 SSH 使用非默认端口,可以通过 -Port 参数指定。
以下代码演示如何建立交互式 SSH 远程会话,以及如何通过 PSSessionOption 自定义连接行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $remoteHost = "admin@192.168.1.100" $sshPort = 22 $sessionOption = New-PSSessionOption -OpenTimeout 30000 Enter-PSSession -HostName $remoteHost -Port $sshPort -Options $sessionOption Exit-PSSession
1 2 3 4 5 6 7 8 9 10 11 12 13 [admin@192.168.1.100]: PS /home/admin> $PSVersionTable Name Value ---- ----- PSVersion 7.4.6 PSEdition Core GitCommitId 7.4.6 OS Ubuntu 24.04.1 LTS Platform Unix PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0
使用 Invoke-Command 执行远程命令 在实际运维场景中,我们经常需要同时在多台远程主机上执行命令。通过 Invoke-Command 配合 SSH 传输,可以向多台 Linux 或 Windows 主机批量推送脚本。Invoke-Command 的 -HostName 参数接受一个字符串数组,因此可以一次连接多台主机。
以下示例展示如何批量查询多台远程服务器的系统信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $servers = @ ( "admin@web-server-01" , "admin@web-server-02" , "admin@db-server-01" ) $results = Invoke-Command -HostName $servers -ScriptBlock { $osInfo = Get-Content /etc/os-release | Where-Object { $_ -match '^PRETTY_NAME=' } | ForEach-Object { ($_ -split '=' )[1 ].Trim('"' ) } $cpuUsage = (Get-Process | Measure-Object -Property CPU -Sum ).Sum $memInfo = Get-Content /proc/meminfo | Where-Object { $_ -match '^MemAvailable:' } [PSCustomObject ]@ { HostName = $env:COMPUTERNAME ?? hostname OS = $osInfo CPU_Total = [math ]::Round($cpuUsage , 2 ) Mem_Info = $memInfo Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' } } $results | Format-Table -AutoSize
1 2 3 4 5 HostName OS CPU_Total Mem_Info Timestamp -------- -- --------- -------- --------- web-server-01 Ubuntu 24.04.1 LTS 3.42 MemAvailable: 3842 kB 2025-09-29 10:15:32 web-server-02 Ubuntu 24.04.1 LTS 1.87 MemAvailable: 6128 kB 2025-09-29 10:15:33 db-server-01 CentOS Stream 9 8.15 MemAvailable: 12244 kB 2025-09-29 10:15:33
使用 SSH 密钥认证 在生产环境中,每次连接都输入密码既不安全也不方便。配置 SSH 密钥认证可以让 PowerShell 远程会话更加流畅。下面展示如何在 PowerShell 中生成 SSH 密钥并将公钥分发到远程主机:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $sshDir = Join-Path $env:USERPROFILE ".ssh" $keyPath = Join-Path $sshDir "id_ed25519" if (-not (Test-Path $keyPath )) { Write-Host "正在生成 SSH 密钥..." -ForegroundColor Cyan ssh-keygen -t ed25519 -f $keyPath -C "powershell-remoting" -N '""' Write-Host "密钥已生成:$keyPath " -ForegroundColor Green } else { Write-Host "已有 SSH 密钥:$keyPath " } $publicKey = Get-Content "$keyPath .pub" -Raw Write-Host "公钥内容(前 80 字符):" Write-Host $publicKey .Substring(0 , [Math ]::Min(80 , $publicKey .Length))
1 2 3 4 5 6 7 正在生成 SSH 密钥... Generating public/private ed25519 key pair. Your identification has been saved in C:\Users\admin\.ssh\id_ed25519 Your public key has been saved in C:\Users\admin\.ssh\id_ed25519.pub 密钥已生成:C:\Users\admin\.ssh\id_ed25519 公钥内容(前 80 字符): ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINxR9fG3hK7YpBzM2vWqLpC4T5JkR powershell-remoting
配置 SSH Config 文件简化连接 当需要管理大量服务器时,可以在 ~/.ssh/config 文件中预定义主机别名和连接参数。PowerShell 远程管理可以直接使用这些别名,而不必每次都输入完整的用户名和地址。
以下脚本用于生成和维护 SSH Config 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $serverList = @ ( [PSCustomObject ]@ { Alias = "web01" ; Host = "192.168.1.101" ; User = "admin" ; Port = 22 } [PSCustomObject ]@ { Alias = "web02" ; Host = "192.168.1.102" ; User = "admin" ; Port = 22 } [PSCustomObject ]@ { Alias = "db01" ; Host = "192.168.1.201" ; User = "dba" ; Port = 2222 } [PSCustomObject ]@ { Alias = "proxy" ; Host = "10.0.0.50" ; User = "ops" ; Port = 22 } ) $configLines = @ ()$configLines += "# PowerShell SSH Remoting - Auto Generated" $configLines += "" foreach ($server in $serverList ) { $configLines += "Host $ ($server .Alias)" $configLines += " HostName $ ($server .Host)" $configLines += " User $ ($server .User)" $configLines += " Port $ ($server .Port)" $configLines += " IdentityFile ~/.ssh/id_ed25519" $configLines += " StrictHostKeyChecking accept-new" $configLines += "" } $sshConfigPath = Join-Path $env:USERPROFILE ".ssh" "config" $configLines | Set-Content -Path $sshConfigPath -Encoding UTF8Write-Host "SSH Config 已写入:$sshConfigPath " -ForegroundColor GreenWrite-Host "`n配置内容预览:" Get-Content $sshConfigPath
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 SSH Config 已写入:C:\Users\admin\.ssh\config 配置内容预览: # PowerShell SSH Remoting - Auto Generated Host web01 HostName 192.168.1.101 User admin Port 22 IdentityFile ~/.ssh/id_ed25519 StrictHostKeyChecking accept-new Host web02 HostName 192.168.1.102 User admin Port 22 IdentityFile ~/.ssh/id_ed25519 StrictHostKeyChecking accept-new Host db01 HostName 192.168.1.201 User dba Port 2222 IdentityFile ~/.ssh/id_ed25519 StrictHostKeyChecking accept-new Host proxy HostName 10.0.0.50 User ops Port 22 IdentityFile ~/.ssh/id_ed25519 StrictHostKeyChecking accept-new
配置完成后,可以直接使用别名建立远程会话:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Enter-PSSession -HostName web01$webServers = @ ("web01" , "web02" )$diskReport = Invoke-Command -HostName $webServers -ScriptBlock { $df = df -h / | Select-Object -Last 1 $uptime = uptime -p [PSCustomObject ]@ { Server = hostname Disk = $df Uptime = $uptime Collected = Get-Date -Format 'HH:mm:ss' } } $diskReport | Format-List
1 2 3 4 5 6 7 8 9 Server : web-server-01 Disk : /dev/sda1 50G 23G 25G 48% / Uptime : up 42 days, 3 hours, 17 minutes Collected : 10:22:45 Server : web-server-02 Disk : /dev/sda1 50G 18G 29G 39% / Uptime : up 42 days, 3 hours, 22 minutes Collected : 10:22:46
监控远程主机状态 结合 SSH 远程管理,可以编写一个实用的监控脚本,定期检查远程服务器的健康状态。以下示例通过 SSH 同时检查多台服务器的 CPU、内存和磁盘使用情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 $targets = @ ("web01" , "web02" , "db01" )$healthReport = Invoke-Command -HostName $targets -ScriptBlock { $cpuLine = top -bn1 | Select-Object -Index 2 $cpuIdle = if ($cpuLine -match '(\d+\.\d+)\s*id' ) { [math ]::Round(100 - [double ]$Matches [1 ], 1 ) } else { -1 } $memLine = free -m | Select-Object -Index 1 $memParts = $memLine -split '\s+' $memTotal = [int ]$memParts [1 ] $memUsed = [int ]$memParts [2 ] $memPct = [math ]::Round(($memUsed / $memTotal ) * 100 , 1 ) $diskLine = df -h / | Select-Object -Last 1 $diskPct = if ($diskLine -match '(\d+)%' ) { [int ]$Matches [1 ] } else { -1 } $status = "Healthy" if ($cpuIdle -gt 80 -or $memPct -gt 85 -or $diskPct -gt 80 ) { $status = "Warning" } if ($cpuIdle -gt 95 -or $memPct -gt 95 -or $diskPct -gt 90 ) { $status = "Critical" } [PSCustomObject ]@ { Server = hostname CPU_Pct = $cpuIdle Mem_Pct = $memPct Disk_Pct = $diskPct Status = $status } } $healthReport | Sort-Object { switch ($_ .Status) { "Critical" { 0 } "Warning" { 1 } default { 2 } } } | Format-Table -AutoSize $critical = ($healthReport | Where-Object { $_ .Status -eq "Critical" }).Count$warning = ($healthReport | Where-Object { $_ .Status -eq "Warning" }).Count$healthy = ($healthReport | Where-Object { $_ .Status -eq "Healthy" }).CountWrite-Host "`n汇总:健康 $healthy 台 / 警告 $warning 台 / 严重 $critical 台"
1 2 3 4 5 6 7 Server CPU_Pct Mem_Pct Disk_Pct Status ------ ------- ------- -------- ------ db-server-01 12.3 88.2 72 Warning web-server-01 5.1 42.6 48 Healthy web-server-02 3.8 38.1 39 Healthy 汇总:健康 2 台 / 警告 1 台 / 严重 0 台
注意事项
SSH 服务必须运行 :远程主机上的 sshd 服务必须处于运行状态。在 Linux 上可通过 systemctl status sshd 检查,在 Windows 上可通过 Get-Service sshd 查看。
PowerShell 版本要求 :远程主机上必须安装 PowerShell 7 及以上版本。仅安装 SSH 不够,sshd 还需要配置 powershell 作为可用子系统(在 sshd_config 中添加 Subsystem powershell /usr/bin/pwsh -sshs -NoLogo)。
密钥权限 :在 Linux/macOS 上,SSH 私钥文件的权限必须设为 600,否则 SSH 会拒绝使用该密钥。可通过 chmod 600 ~/.ssh/id_ed25519 修复。
防火墙与端口 :确保本地到远程主机的 SSH 端口(默认 22)在防火墙规则中放行。如果使用非默认端口,需要在 SSH Config 中显式指定或通过 -Port 参数传入。
错误排查 :如果连接失败,先用 ssh user@host 命令行工具手动测试,排除认证和网络问题。PowerShell SSH 远程管理底层依赖系统的 SSH 客户端,所以基础连通性必须先保证。
与 WinRM 的区别 :SSH 远程会话不支持 CimCmdlets 和部分 WMI 相关操作(这些是 Windows 专属)。如果需要管理 Windows 专属功能(如注册表、服务控制管理器),建议对 Windows 目标仍然使用 WinRM 传输,对 Linux/macOS 目标使用 SSH 传输。