In PowerShell 7+, PowerShell Remoting supports the Secure Shell (SSH) protocol as an alternative transport layer to WinRM. SSH-based remoting enables true cross-platform management between Windows, Linux, and macOS endpoints over standard port 22, replacing complex WinRM and Kerberos workgroup configurations with industry-standard SSH key-based authentication.
SSH vs. WinRM Transport Architecture
While traditional PowerShell Remoting relies on Windows Remote Management (WinRM) over ports 5985/5986, SSH remoting tunnels the PowerShell remoting protocol (MS-PSRP) directly through a standard OpenSSH encrypted connection.
| Feature | WinRM Remoting | SSH Remoting |
|---|---|---|
| PowerShell Version | Windows PowerShell 5.1 & PS 7+ | PowerShell 7+ (pwsh) only |
| Supported Operating Systems | Primarily Windows (limited Linux via OMI) | Windows, Linux, macOS |
| Network Port | 5985 (HTTP), 5986 (HTTPS) | 22 (SSH) |
| Target Parameter | -ComputerName |
-HostName |
| Authentication | Kerberos, NTLM, Negotiate, Basic | SSH Key Pairs, Passwords |
| Firewall Setup | Requires custom WS-Management ports | Uses existing enterprise SSH infrastructure |
| Cross-OS remoting | ❌ No | ✅ Yes |
Prerequisites
Both the local and remote machines need:
- PowerShell 7+ installed.
- An SSH server running (OpenSSH Server on Windows, or the built-in SSH daemon on Linux/macOS).
- The remote machine's SSH server configured with a subsystem entry pointing to PowerShell.
Installing OpenSSH Server on Windows
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType Automaticwhere:
Add-WindowsCapabilityinstalls the OpenSSH Server optional feature.Start-Service sshdstarts the SSH daemon immediately.Set-Service -StartupType Automaticensures SSH starts automatically on boot.
Configuring the SSH Subsystem for PowerShell
For SSH remoting to launch PowerShell (instead of a normal shell), the SSH server's configuration file must register PowerShell as a subsystem.
Target Configuration on Windows
On a Windows host, OpenSSH Server can be installed via Windows Optional Features. Once installed, edit C:\ProgramData\ssh\sshd_config to register the pwsh.exe binary as a subsystem.
Subsystem powershell C:/Program Files/PowerShell/7/pwsh.exe -sshs -NoLogowhere:
Subsystem powershelldefines a named subsystem calledpowershell.-sshsa required internal parameter flag instructingpwsh.exeto run in SSH Server Mode.-NoLogosuppresses the startup banner upon connection initialization.
$sshdConfig = "$env:ProgramData\ssh\sshd_config"
$subsystemLine = 'Subsystem powershell "C:/Program Files/PowerShell/7/pwsh.exe" -sshs -nologo'
if ((Get-Content $sshdConfig) -notcontains $subsystemLine) {
Add-Content -Path $sshdConfig -Value "`n$subsystemLine"
Restart-Service -Name "sshd" -Force
Write-Output "PowerShell SSH Subsystem registered and sshd restarted."
}where:
$env:ProgramData\ssh\sshd_configis the default configuration file path for OpenSSH on Windows.Restart-Service -Name "sshd"restarts the OpenSSH service to apply configuration changes.
./configure-windows-sshd.ps1PowerShell SSH Subsystem registered and sshd restarted.Target Configuration on Linux
On a Linux distribution (such as Ubuntu or RHEL) with PowerShell 7 installed, edit /etc/ssh/sshd_config:
Subsystem powershell /usr/bin/pwsh -sshs -nologoAfter modifying the file, restart the SSH daemon:
sudo systemctl restart sshdConnecting with New-PSSession over SSH
Once configured, New-PSSession and Invoke-Command accept SSH-specific parameters instead of -ComputerName alone.
$session = New-PSSession -HostName "linuxserver01" -UserName "admin" -SSHTransportwhere:
-HostNamespecifies the target machine's hostname or IP address.-UserNamespecifies the SSH username to authenticate with.-SSHTransporttells PowerShell to use SSH instead of WinRM as the connection transport.
Password:Connecting with an SSH Key
$session = New-PSSession -HostName "linuxserver01" -UserName "admin" -KeyFilePath "$HOME/.ssh/id_rsa"where -KeyFilePath is the path to a private SSH key used for passwordless authentication.
Interactive Session Example
Enter-PSSession -HostName "192.168.1.50" `
-UserName "ubuntu" `
-KeyFilePath "~/.ssh/id_ed25519"where:
Enter-PSSessionstarts an interactive remote session.-HostNamedirects the connection over SSH to the target IP address or hostname.-UserNamethe remote user account to authenticate as.-KeyFilePathpath to the local OpenSSH private authentication key file (bypasses interactive password prompts).
./interactive-ssh-session.ps1[ubuntu@192.168.1.50]: PS /home/ubuntu> $PSVersionTable.OS
Linux 5.15.0-101-generic #111-Ubuntu SMP
[ubuntu@192.168.1.50]: PS /home/ubuntu> Exit-PSSession
PS C:\Users\Admin>Interactive Sessions with Enter-PSSession over SSH
Enter-PSSession -HostName "linuxserver01" -UserName "admin"[linuxserver01]: PS /home/admin>Running Commands over SSH with Invoke-Command
The Invoke-Command cmdlet executes script blocks across one or more SSH endpoints concurrently. Even when executing on a remote Linux server, PowerShell serializes command output into .NET objects and transmits them back to the caller.
Invoke-Command -HostName "linuxserver01" -UserName "admin" -ScriptBlock { Get-Process }ProcessName Id PSComputerName
----------- -- --------------
bash 1204 linuxserver01
pwsh 1215 linuxserver01One-to-Many Commands over SSH
$linuxTargets = @("srv-linux01.corp.internal", "srv-linux02.corp.internal")
Invoke-Command -HostName $linuxTargets -UserName "admin_ops" -KeyFilePath "~/.ssh/id_rsa" -ScriptBlock {
[PSCustomObject]@{
HostName = $env:HOSTNAME
Kernel = (uname -r)
UptimeDays = [math]::Round(((Get-Content /proc/uptime).Split(' ')[0] / 86400), 1)
ProcessCount= (Get-Process).Count
}
} | Select-Object -Property PSComputerName, HostName, Kernel, UptimeDays, ProcessCountwhere:
Invoke-Commandruns code across remote hosts in parallel.-HostNamelist of remote nodes to connect to via SSH.uname -rexecutes native Linux CLI utilities directly inside the remote script block.Get-Content /proc/uptimereads Linux virtual filesystem counters as standard PowerShell objects.PSComputerNameis an automatically attached property containing the destination hostname.
./invoke-ssh-command.ps1PSComputerName HostName Kernel UptimeDays ProcessCount
-------------- -------- ------ ---------- ------------
srv-linux01.corp.internal srv-linux01 5.15.0-91-generic 12.4 148
srv-linux02.corp.internal srv-linux02 5.15.0-91-generic 44.1 162Connecting Windows ↔ Linux in Both Directions
SSH remoting functions symmetrically: a Windows machine can connect to Linux, and a Linux machine running PowerShell 7 can connect to Windows. This works as long as both systems have PowerShell and SSH correctly configured.
Enter-PSSession -HostName "192.168.1.50" -UserName "ubuntu"Enter-PSSession -HostName "192.168.1.10" -UserName "Administrator"Practical Usage
Cross-Platform Server Inventory Script
A common use case for SSH remoting is gathering system information from a mixed fleet of Windows and Linux servers in one script.
$targets = @(
@{ HostName = "winserver01"; UserName = "Administrator" },
@{ HostName = "linuxserver01"; UserName = "admin" }
)
foreach ($target in $targets) {
$session = New-PSSession -HostName $target.HostName -UserName $target.UserName -KeyFilePath "$HOME/.ssh/id_rsa"
Invoke-Command -Session $session -ScriptBlock {
[PSCustomObject]@{
ComputerName = $env:COMPUTERNAME ?? (hostname)
OS = $PSVersionTable.OS
PSVersion = $PSVersionTable.PSVersion.ToString()
}
}
Remove-PSSession -Session $session
}where:
$targetsis an array of hashtables defining each remote host and its SSH username.$env:COMPUTERNAME ?? (hostname)uses the null-coalescing operator to fall back to the Linuxhostnamecommand if the Windows-only environment variable is empty.$PSVersionTable.OSreports the operating system of each remote target.
./cross-platform-inventory.ps1ComputerName OS PSVersion
------------ -- ---------
WINSERVER01 Microsoft Windows 10.0.20348 7.4.2
linuxserver01 Linux 5.15.0-91-generic 7.4.2Managing Hybrid Infrastructure (Windows & Linux)
In modern enterprise networks, administrators often manage both Windows and Linux nodes within a single script. SSH Remoting standardizes connection parameters across operating systems without requiring WinRM listeners on Linux.
$sessionWin = New-PSSession -HostName "win-app01.corp" -UserName "winadmin" -KeyFilePath "~/.ssh/id_ed25519"
$sessionLinux = New-PSSession -HostName "linux-db01.corp" -UserName "dbadmin" -KeyFilePath "~/.ssh/id_ed25519"
$hybridSessions = @($sessionWin, $sessionLinux)
$fleetReport = Invoke-Command -Session $hybridSessions -ScriptBlock {
[PSCustomObject]@{
OperatingSystem = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
Architecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
PSVersion = $PSVersionTable.PSVersion.ToString()
MemoryAvailable = if ($IsWindows) {
[math]::Round((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB, 2)
} else {
[math]::Round(([int](Get-Content /proc/meminfo | Select-String "MemAvailable" | ForEach-Object { $_.Line.Split()[1] }) / 1MB), 2)
}
}
}
$hybridSessions | Remove-PSSession
$fleetReport | Select-Object -Property PSComputerName, OperatingSystem, Architecture, PSVersion, MemoryAvailable |
Format-Table -AutoSizewhere:
New-PSSession -HostNameinstantiates persistent SSH-backed runspaces.[System.Runtime.InteropServices.RuntimeInformation]cross-platform .NET class that returns operating system and architecture information reliably on Windows, macOS, and Linux.$IsWindowsis a PowerShell automatic boolean variable that returns$trueon Windows and$falseon Linux/macOS, used to execute platform-specific inspection logic.Remove-PSSessioncloses the SSH connections and frees remote resources.
./manage-hybrid-fleet.ps1PSComputerName OperatingSystem Architecture PSVersion MemoryAvailable
-------------- --------------- ------------ --------- ---------------
win-app01.corp Microsoft Windows 10.0.20348 X64 7.4.1 6.42
linux-db01.corp Linux 5.15.0-101-generic #111-Ubuntu X64 7.4.1 11.85