PowerShell Remoting is a cornerstone of Windows administration and automation. For years, it was synonymous with one protocol: WinRM. But with modern PowerShell and the ubiquity of SSH, we now have a powerful, cross-platform alternative.
So, which protocol should you use? WinRM or SSH?
This guide provides the definitive answer. We’ll explore the fundamental differences between the protocols, provide practical, step-by-step instructions for using both, and give clear recommendations for when to choose each one.
Part 1: Understanding the Protocols (The “Why”)
Before diving into the “how,” it’s crucial to understand the “why.” WinRM and SSH are architecturally different, and these differences have major implications for security, data handling, and cross-platform compatibility.
Architecture: Objects vs. Text
The most fundamental difference lies in how they transport data.
-
WinRM (Windows Remote Management): Uses a SOAP-based protocol to transport rich, deserialized .NET objects. When you run
Invoke-Commandover WinRM, the remote server sends back a complete object, which you can interact with natively ($service.Status,$process.CPU). This is powerful but Windows-centric. -
SSH (Secure Shell): Is a text-based protocol. When used with PowerShell, objects are serialized to text (usually XML) on the server, sent over the wire, and deserialized on the client. It’s a universal standard that works everywhere but adds a layer of translation.
The Admin Elevation Challenge
This is often the deciding factor. How do you run a command as an Administrator?
-
WinRM’s Model: Elevate at Connection. You connect with an account that is already an administrator. The entire remote session runs with high integrity from the start. It feels like you’re logged directly into the server.
-
SSH’s Model: Elevate on Demand. Following the Linux
sudomodel, you typically connect as a standard user (or a non-elevated admin) and must explicitly request elevation for a specific command. On Windows, this is often done withgsudo.
| Feature | WinRM | OpenSSH |
|---|---|---|
| Port | 5985 (HTTP) / 5986 (HTTPS) | 22 (TCP) |
| Data | PowerShell Objects | Text (Serialized Objects) |
| Cross-Platform | Windows-centric | Native everywhere |
| Elevation | Automatic (if connected as Admin) | Manual (requires gsudo or similar) |
| JEA Support | Yes (Just Enough Admin) | Limited (via ForceCommand) |
Part 2: The Modern Way: PowerShell with Built-in SSH
For most modern, cross-platform scenarios, using PowerShell’s native SSH transport is the recommended approach. It’s built directly into PowerShell 7+ and doesn’t require extra modules.
Why Use PowerShell’s Native SSH?
- Cross-Platform: The definitive industry standard for managing Linux, macOS, and Windows.
- Firewall-Friendly: Uses the single, well-known TCP port 22.
- Secure and Simple: Relies on proven SSH security and simple key-based authentication.
Step 1: Configure the Servers
On the Windows Server
- Install & Start OpenSSH Server:
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Start-Service sshd Set-Service sshd -StartupType Automatic New-NetFirewallRule -Name "SSH" -DisplayName "SSH" -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 - Configure PowerShell Subsystem: Edit
$env:ProgramData\ssh\sshd_configand ensure this line is present and uncommented:Subsystem powershell C:\Program Files\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile - Restart the service:
Restart-Service sshd
On the Linux Server
- Install SSH and PowerShell:
sudo apt update sudo apt install openssh-server -y # Follow official guides to install PowerShell - Configure PowerShell Subsystem: Edit
/etc/ssh/sshd_configand add this line:Subsystem powershell /usr/bin/pwsh -sshs -NoLogo -NoProfile - Restart the service:
sudo systemctl restart sshd
Step 2: Run Remote Commands
You use the same Invoke-Command and Enter-PSSession cmdlets you already know, but with the crucial -SSHTransport switch.
# Get the kernel version from a Linux server
Invoke-Command -HostName "linux01" -UserName "admin" -SSHTransport -ScriptBlock { uname -a }
# Start an interactive session on a Windows server using a key file
Enter-PSSession -HostName "win01" -UserName "Administrator" -KeyFilePath "C:\Users\Admin\.ssh\id_rsa" -SSHTransportA Note on
Posh-SSHandInvoke-SSHCommandYou may see older guides that recommend the
Posh-SSHmodule and itsInvoke-SSHCommandcmdlet. While this module is still useful for specific edge cases (like interacting with devices that have a basic SSH shell but not a PowerShell host), it is no longer the recommended method for PowerShell-to-PowerShell communication. The built-in-SSHTransportswitch provides a much richer experience, including the object serialization thatInvoke-SSHCommandlacks. For all modern PowerShell remoting, stick with the built-in method.
Part 3: The Traditional Way: PowerShell with WinRM
In a domain-joined, all-Windows environment, WinRM is still a fast and powerful option. It is deeply integrated into the OS and Group Policy, and it excels at handling rich PowerShell objects.
How WinRM Elevation Works: The “Headless” Admin Session
A common point of confusion is how Invoke-Command runs elevated commands without a UAC prompt. It’s not magic, and there is no -RunAsAdministrator flag.
The answer lies in how WinRM sessions work. When you use Invoke-Command with the credentials of a user in the remote machine’s Administrators group, the following happens:
- Authentication: The remote server authenticates the admin user.
- Headless Session: It creates a non-interactive (headless) session for that user.
- Full Admin Token: Because there is no desktop or UI to display a UAC prompt, the session runs with the user’s full administrator token (a high-integrity process) from the start.
This is why silent installers are critical for remote automation—if your script tries to launch anything with a GUI, the process will hang or fail because it has no desktop to show the UAC prompt on.
Step 1: Enable WinRM
On a server, run this in an elevated PowerShell prompt:
Enable-PSRemoting -ForceTrustedHosts on the client.
Step 2: Run Remote Commands
This is the classic syntax. Because WinRM is the default protocol, no special switches are needed. The command is automatically elevated if the credential belongs to an admin.
Invoke-Command -ComputerName "WinSrv01" -Credential (Get-Credential) -ScriptBlock {
# This entire block runs with high integrity
Get-Service -Name "Spooler" | Stop-Service -PassThru
}Step 3: How to Verify Remote Elevation
You can confirm your remote session is elevated by running whoami /groups and checking for the “High Mandatory Level” SID.
Invoke-Command -ComputerName "WinSrv01" -Credential (Get-Credential) -ScriptBlock {
whoami /groups
}
# Look for: Mandatory Label\High Mandatory LevelPros and Cons of WinRM
- Pros: Excellent Active Directory integration (Kerberos), returns true PowerShell objects, built-in to Windows.
- Cons: Can be difficult to configure across firewalls or in non-domain environments, the “double-hop” problem requires complex CredSSP setup, Windows-only.
Part 4: Conclusion: Which Protocol Should I Use?
The choice is simpler than ever. Follow these guidelines.
- You are in a mixed-OS environment (Windows, Linux).
- Use SSH. It’s the universal standard.
- You are managing servers across the internet, WAN, or cloud.
- Use SSH. It’s firewall-friendly and more performant on high-latency links.
- You are in a pure, domain-joined Windows environment.
- Use WinRM. It’s tightly integrated, handles elevation seamlessly, and returns true objects with less overhead.
- You need to run a quick, elevated command on a remote Windows machine.
- SSH is often faster.
Invoke-Command -HostName srv1 -SSHTransport -ScriptBlock { gsudo some-command.exe }can be quicker than establishing a full WinRM session.
- SSH is often faster.
By mastering both the modern SSH and traditional WinRM methods, you are equipped to handle any remote administration challenge PowerShell can solve.