PowerShell ยท Remoting

PowerShell Remoting and WinRM

A practical guide to PowerShell Remoting: how WinRM carries commands to remote computers, when to reach for Enter-PSSession versus Invoke-Command, and how to avoid the double-hop problem before it breaks a script in production.

Quick idea: PowerShell Remoting lets you run commands on a remote computer, or on hundreds of them at once, over WinRM instead of opening Remote Desktop and typing at each console in turn.
WinRM

The Windows service and WS-Management implementation that actually carries remoting traffic.

PSSession

A persistent, reusable connection to a remote computer’s PowerShell engine.

Double Hop

The classic failure where a remote session can’t use your credentials to reach a third computer.

What Is PowerShell Remoting?

PowerShell Remoting lets you run PowerShell commands on a remote computer as though you were sitting at its console, without ever opening a Remote Desktop session.

Think of it as picking up a direct line into a remote server’s PowerShell engine, rather than walking over to its desk and typing on its keyboard through RDP. You send a command, the remote engine runs it locally on that machine, and the result comes back to your console as structured data, not a screenshot.

Under the hood, this is carried by Windows Remote Management (WinRM), Microsoft’s implementation of the WS-Management protocol. WinRM listens for connections on TCP 5985 for HTTP and TCP 5986 for HTTPS, and PowerShell layers its own remoting protocol on top of those WS-Management messages. Three cmdlets sit on top of that transport: Enter-PSSession for an interactive one-to-one session, Invoke-Command for running a script block on one or many computers, and New-PSSession for creating a persistent connection either of the other two can reuse.

Important: PowerShell Remoting is not the same thing as the -ComputerName parameter on older cmdlets like Get-Service or Get-EventLog. Those typically use RPC/DCOM. PowerShell Remoting specifically means WinRM.

Why Remoting Instead of RDP or Local Consoles?

Clicking through Remote Desktop sessions one server at a time works fine for a handful of machines. It stops working the moment you have twenty, two hundred, or a Server Core box with no desktop to click through in the first place.

Scale

One Invoke-Command -ComputerName call can fan a script block out to hundreds of servers at once.

Objects, Not Screens

Remote output comes back as live PowerShell objects you can filter and export, not text you have to read off a screen.

No Desktop Required

Server Core and headless VMs have no GUI to RDP into. Remoting is often the only management path they have.

This is also why PowerShell Remoting has been enabled by default on Windows Server since Windows Server 2012 R2. Microsoft treats it as the baseline way to manage a server fleet, not an optional add-on.

Why Active Directory and Security Care About Remoting

Remoting is only as safe as the authentication behind it, and that authentication behaves differently depending on how you connect.

When a client connects to a domain-joined server by computer name, WinRM authenticates with Kerberos by default. Kerberos proves both the user’s identity and the server’s identity to each other without ever sending a reusable credential across the wire. When you connect by IP address, or to a workgroup computer with no domain to ask, Kerberos isn’t available and WinRM falls back to NTLM, which proves who you are but cannot prove who the server is.

Key rule: NTLM-based remoting authentication is disabled by default. You have to deliberately enable it, either by putting an SSL certificate on the target server or by adding the target to the client’s WinRM TrustedHosts list.

Adding a host to TrustedHosts is not a statement that the host is trustworthy. It only tells your local WinRM client to stop demanding proof of the server’s identity over NTLM, which is why it should be scoped as tightly as possible rather than set to a wildcard on a workstation that talks to production.

Because a remoting session runs under the connecting user’s own account and inherits their normal access controls, it shows up in the same places any other authenticated logon would: security event logs on the target server, and (where module or script block logging is configured) the PowerShell operational log. Treat a remoting session the same way you would treat an interactive logon when you’re reviewing who touched what.

The Double-Hop Problem

This is the failure every PowerShell remoting user eventually runs into once a script tries to reach past the first remote computer.

The scenario: you are logged in to ServerA. From ServerA you open a remoting session to ServerB. A command running on ServerB then tries to reach a resource, a share, another server’s WMI, an Active Directory query, on ServerC. Access to ServerC is denied, because the credentials you used to authenticate to ServerB were never handed to ServerB to use on your behalf.

This is deliberate, not a bug. Kerberos and NTLM both authenticate you to the remote machine without giving that machine a copy of your credentials, which is exactly what stops a compromised intermediate server from impersonating you everywhere else on the network. It just means the second hop needs one of a small number of documented workarounds.

Method Trade-off
CredSSPSimplest to enable on both sides; caches your credentials on ServerB, which becomes a target if ServerB is compromised.
Resource-based Kerberos constrained delegationNo credentials stored; configured on ServerC itself; needs Windows Server 2012+ and rights to edit the ServerC computer object, not Domain Admin.
Kerberos constrained delegation (legacy)No credentials stored, but needs Domain Admin rights and is limited to a single domain.
Unconstrained delegationWorks, but gives ServerB a ticket it can use anywhere on your behalf. Not recommended.
Just Enough Administration (JEA)Strong security ceiling, but needs its own configuration on every intermediate server.
Pass credentials inside the script blockNo server configuration needed at all; just awkward to write and means handling a credential object explicitly.

For a domain admin managing domain controllers, CredSSP is the pragmatic default Microsoft itself points to, precisely because the environment is already highly trusted. For anything less trusted, resource-based Kerberos constrained delegation is the modern recommended path: it is configured entirely with PowerShell against Active Directory, and it doesn’t require anyone to hold Domain Admin rights.

Configuring WinRM

On Windows Server this is usually already done for you. On workstations, or a server where remoting was previously disabled, you enable it explicitly.

# Enable PowerShell Remoting: starts WinRM, sets it to auto-start,
# creates a default HTTP listener, and opens the firewall exception
Enable-PSRemoting -Force

# Equivalent low-level command using the WinRM tool directly
winrm quickconfig

# Create an HTTPS listener as well (requires a suitable certificate
# and TCP 5986 open separately)
winrm quickconfig -transport:https

# Add a specific server to this client's trusted hosts (needed for
# NTLM auth by IP address, or workgroup machines with no Kerberos)
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "server02.contoso.com" -Concatenate -Force

# Confirm what is currently in TrustedHosts
Get-Item WSMan:\localhost\Client\TrustedHosts
Setting Purpose
Enable-PSRemoting Runs Set-WSManQuickConfig, starts and auto-starts WinRM, creates the listener, and opens the firewall exception. Needs an elevated session.
winrm quickconfig / winrm qc The lower-level equivalent, driven straight through the winrm command-line tool rather than a PowerShell cmdlet.
Port 5985 Default WinRM listener port for HTTP.
Port 5986 Default WinRM listener port for HTTPS.
TrustedHosts Client-side list of hosts to skip NTLM server-identity verification for. Does not authenticate the host, only suppresses the error.
Practical note: On a public network profile, client Windows versions restrict remoting to the local subnet by default. Use Enable-PSRemoting -SkipNetworkProfileCheck -Force only if you understand the exposure, and prefer HTTPS listeners over widening the public firewall rule.

Scripts / Commands

The commands below cover connecting, running remote work at scale, and setting up the double-hop workarounds from the section above.

# Quick connectivity check before anything else
Test-WSMan -ComputerName Server01

# Start an interactive session on one remote computer
Enter-PSSession -ComputerName Server01 -Credential Domain01\User01

# Leave the interactive session (or just type exit)
Exit-PSSession

# Run a single command against several computers at once
Invoke-Command -ComputerName Server01, Server02, Server03 -ScriptBlock {
    Get-Service -Name W32Time
}

# Cap concurrent connections for a large fan-out (32 is the default anyway)
Invoke-Command -ComputerName (Get-Content .\servers.txt) -ThrottleLimit 20 -ScriptBlock {
    Get-CimInstance Win32_OperatingSystem | Select-Object Caption, LastBootUpTime
}

# Create persistent sessions once, then reuse them for multiple calls
$sessions = New-PSSession -ComputerName Server01, Server02
Invoke-Command -Session $sessions -ScriptBlock { Get-Process powershell }
Invoke-Command -Session $sessions -ScriptBlock { Get-Process pwsh }

# List sessions you currently have open, and tear them down when done
Get-PSSession
Remove-PSSession -Session $sessions

# Run a remote command as a background job on many servers
Invoke-Command -ComputerName Server01, Server02 -AsJob -JobName FleetCheck -ScriptBlock {
    Get-EventLog -LogName System -Newest 5
}
Get-Job -Name FleetCheck | Receive-Job

# Enable CredSSP for the double-hop problem (client side, then server side)
Enable-WSManCredSSP -Role Client -DelegateComputer "server02.contoso.com"
Enable-WSManCredSSP -Role Server

# Use CredSSP on a connection once both sides allow it
Invoke-Command -ComputerName Server02 -Credential $cred -Authentication Credssp -ScriptBlock {
    Test-Path \\Server03\Share
}

# Resource-based Kerberos constrained delegation instead of CredSSP:
# grant ServerB the right to delegate to ServerC
$ServerB = Get-ADComputer -Identity ServerB
$ServerC = Get-ADComputer -Identity ServerC
Set-ADComputer -Identity $ServerC -PrincipalsAllowedToDelegateToAccount $ServerB
Practical rule: Use Enter-PSSession for a single machine you want to poke around on interactively. Use Invoke-Command the moment you are running the same thing against more than one computer, or the moment you want the result back as an object instead of terminal text.

Enter-PSSession vs Invoke-Command vs New-PSSession

Cmdlet Best For Notes
Enter-PSSession Interactive, exploratory work on one remote computer. Only one interactive session at a time; commands run unparsed on the remote side.
Invoke-Command Running a script block once, against one or many computers, non-interactively. Default -ThrottleLimit is 32 concurrent connections; supports -AsJob for background execution.
New-PSSession Building a persistent connection to reuse across multiple Invoke-Command or Enter-PSSession calls. Avoids repeatedly opening and tearing down the underlying WinRM connection.

Troubleshooting Cheat Sheet

Symptom Likely Cause Fix
“WinRM cannot process the request” / “Access is denied” The connecting account isn’t in the remote computer’s Administrators group, or WinRM isn’t listening. Confirm the account has remote access rights, then run Test-WSMan -ComputerName <target> to confirm the listener responds.
“The client cannot connect to the destination… verify that the service on the destination is running” WinRM service isn’t running, or no listener is configured, on the target. Run Enable-PSRemoting -Force on the target, or check winrm enumerate winrm/config/listener.
Connecting by IP address fails with an identity error Kerberos can’t be used against a bare IP address; NTLM is falling back and can’t verify the server’s identity. Add the target to TrustedHosts with Set-Item WSMan:\localhost\Client\TrustedHosts, or connect by name instead of IP.
A remote command works fine on ServerB but fails to reach ServerC The double-hop problem: credentials weren’t delegated past the first hop. Configure CredSSP or resource-based Kerberos constrained delegation, or pass credentials explicitly with $Using:cred inside the script block.
Firewall blocks the connection entirely TCP 5985 (or 5986 for HTTPS) isn’t open between client and target. Check the WinRM firewall rules with Get-NetFirewallRule -Name 'WINRM*' and confirm the correct network profile is allowed.
Session hangs or times out under heavy fan-out Too many concurrent connections against the default or a custom -ThrottleLimit. Lower -ThrottleLimit on Invoke-Command, or batch the target list into smaller groups.
CredSSP connection refused even after enabling it CredSSP was enabled on only one side (client or server), not both. Run Enable-WSManCredSSP -Role Client -DelegateComputer <target> on the client and Enable-WSManCredSSP -Role Server on the target.

Final Thoughts

PowerShell Remoting is what turns a fleet of servers into something one engineer can actually manage. WinRM handles the transport, Enter-PSSession and Invoke-Command give you the two shapes of remote execution most work needs, and the double-hop problem is the one gotcha worth understanding before it shows up as a confusing access-denied error in someone else’s script.

Once remoting is configured, the same object pipeline you use locally, filtering, sorting, exporting, works exactly the same way on remote output. That consistency is why remoting sits underneath so much of enterprise PowerShell automation, from health checks to Active Directory reporting.

Key takeaway: If Test-WSMan -ComputerName <target> succeeds, remoting is reachable. If a script that works on one hop fails on a second, it’s the double-hop problem, not a permissions bug in your script.
Next in this series

Next, we can cover Just Enough Administration (JEA) in its own right: how constrained endpoints, role capabilities, and virtual accounts let you hand out remoting access without handing out full admin rights.