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-Command over 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 sudo model, 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 with gsudo.

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

  1. 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
    
  2. Configure PowerShell Subsystem: Edit $env:ProgramData\ssh\sshd_config and ensure this line is present and uncommented:
    Subsystem powershell C:\Program Files\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile
    
  3. Restart the service: Restart-Service sshd

On the Linux Server

  1. Install SSH and PowerShell:
    sudo apt update
    sudo apt install openssh-server -y
    # Follow official guides to install PowerShell
    
  2. Configure PowerShell Subsystem: Edit /etc/ssh/sshd_config and add this line:
    Subsystem powershell /usr/bin/pwsh -sshs -NoLogo -NoProfile
    
  3. 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" -SSHTransport

A Note on Posh-SSH and Invoke-SSHCommand

You may see older guides that recommend the Posh-SSH module and its Invoke-SSHCommand cmdlet. 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 -SSHTransport switch provides a much richer experience, including the object serialization that Invoke-SSHCommand lacks. 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:

  1. Authentication: The remote server authenticates the admin user.
  2. Headless Session: It creates a non-interactive (headless) session for that user.
  3. 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 -Force
For non-domain machines, you will also need to configure TrustedHosts 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 Level

Pros 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.

By mastering both the modern SSH and traditional WinRM methods, you are equipped to handle any remote administration challenge PowerShell can solve.