PowerShell Quick Guide: Remote Management Basics

PowerShell remoting is the difference between fixing one machine and fixing two hundred in the same amount of time. The cmdlets themselves are short — Invoke-Command, Enter-PSSession, New-PSSession — but the surrounding plumbing (WinRM listeners, TrustedHosts, double-hop auth, HTTPS certificates) is where most setups quietly go sideways. This guide walks through the working baseline we deploy on domain-joined endpoints, including the security knobs that are worth tightening before this becomes someone else's lateral-movement vector.

Key Takeaways

  • PowerShell remoting is built on WinRM, which speaks WS-Management over HTTP/5985 or HTTPS/5986.
  • Invoke-Command is one-shot, Enter-PSSession is interactive, and New-PSSession is the right choice when you want to reuse a session.
  • Outside a domain, TrustedHosts must be configured explicitly; inside one, Kerberos handles auth automatically.
  • HTTP/5985 is acceptable inside a tunnel or VPN; for anything traversing untrusted networks, configure HTTPS/5986 with a real certificate.
  • Remoting is also an attacker tool. Restrict the WinRM firewall rule to management subnets, log session usage, and avoid leaving Enable-PSRemoting on standalone machines that do not need it.

Environment

  • Windows 10/11 and Windows Server 2019/2022 endpoints, all domain-joined to an Active Directory forest.
  • Windows PowerShell 5.1 and PowerShell 7.4 — both speak the same WinRM protocol, but PowerShell 7 needs its own listener configuration on the target.
  • Microsoft Entra ID joined devices supported via the SSH-based PowerShell remoting path; classic WinRM remoting requires line-of-sight to a domain controller or a configured Kerberos trust.

The Problem

The cmdlet documentation makes remoting look like a one-liner: Enable-PSRemoting on the target, Enter-PSSession from the client, done. In practice the failures show up later — workgroup machines refusing the connection, second-hop authentication errors when the remote script tries to touch a file share, HTTPS certificates not trusted by the client, double-encrypted sessions through a VPN that thinks WinRM traffic is suspicious. The pattern below avoids most of those.

The Solution

Step 1 — Confirm WinRM is reachable

Before anything else, prove the target is listening. Test-WSMan tells you whether the remote WinRM service answers; Test-NetConnection tells you whether the port is even open:

$target = 'fileserver01.corp.example.com'

Test-WSMan -ComputerName $target

Test-NetConnection -ComputerName $target -Port 5985  # HTTP
Test-NetConnection -ComputerName $target -Port 5986  # HTTPS

If Test-NetConnection reports TcpTestSucceeded : False, the problem is firewall or routing, not PowerShell. If the port is open but Test-WSMan fails, the WinRM service is either not running or has no listener bound to the network interface you reached it on.

Step 2 — Run a one-shot command with Invoke-Command

Invoke-Command is the right tool when you want a result back and do not need to stay connected. It happily accepts an array of computer names and fans the call out in parallel:

$servers = 'web01','web02','web03'

Invoke-Command -ComputerName $servers -ScriptBlock {
    Get-Service W3SVC |
        Select-Object @{Name='Host';Expression={$env:COMPUTERNAME}}, Status, StartType
}

Output is deserialised on the way back, which means downstream Where-Object and Sort-Object work but methods on the returned objects do not. If you need to call a method, do it inside the script block.

Step 3 — Start a persistent session with New-PSSession

When you need to issue several commands and avoid the per-call connection overhead, hold the session in a variable and reuse it. Always remove the session when finished, even if a script errors:

$session = New-PSSession -ComputerName 'web01'

try {
    Invoke-Command -Session $session -ScriptBlock { Get-Service }
    Invoke-Command -Session $session -ScriptBlock { Get-EventLog System -Newest 50 }
}
finally {
    Remove-PSSession -Session $session
}

An idle session times out by default after a few minutes of inactivity. For long-running orchestration, configure -IdleTimeout when creating the session.

Step 4 — Handle credentials cleanly

Inside the same domain Kerberos handles authentication automatically — your current ticket is reused. When you need a different identity (another forest, an emergency local account, a delegated admin), prompt once and reuse:

$cred = Get-Credential -Message 'Credentials for remote management'

Invoke-Command -ComputerName 'server01' -Credential $cred -ScriptBlock {
    Get-WinEvent -LogName Security -MaxEvents 10
}

Never store the password in a plain string. Get-Credential returns a PSCredential with a SecureString backing it. For unattended scripts, use a Group Managed Service Account or an Azure Key Vault-backed secret rather than serialising credentials to disk.

Step 5 — Configure TrustedHosts for non-domain clients

Outside a domain, the client cannot use Kerberos and falls back to NTLM, which requires the target to be on the local TrustedHosts list:

# Add a single host
Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'lab-server' -Concatenate -Force

# Or an entire subnet by wildcard (use sparingly)
Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'lab-*.example.local' -Concatenate -Force

# Always review the resulting list
(Get-Item WSMan:\localhost\Client\TrustedHosts).Value

TrustedHosts is per-client, not per-target. Treat it as an opt-in list for systems you actually plan to talk to, not as a permanent wildcard.

Step 6 — Switch to HTTPS for anything off-LAN

HTTP/5985 traffic is authenticated and message-encrypted by default, but the metadata still leaks. For management traffic that crosses untrusted segments, configure a real HTTPS listener with a certificate issued by your internal CA:

# On the target, with an existing cert thumbprint in LocalMachine\My
$thumb = (Get-ChildItem Cert:\LocalMachine\My |
    Where-Object Subject -like '*CN=server01*').Thumbprint

winrm create winrm/config/Listener?Address=*+Transport=HTTPS `@{Hostname=`"server01.corp.example.com`";CertificateThumbprint=`"$thumb`"}

# Open the firewall
New-NetFirewallRule -DisplayName 'WinRM HTTPS' -Name 'WinRM-HTTPS-In-TCP' `
    -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow `
    -RemoteAddress 10.0.0.0/8

The firewall rule restricts inbound 5986 to a management subnet. Leave 5986 open to the world only if you have a very specific reason and a corresponding network ACL upstream.

Step 7 — Copy files over an existing session

Once you have a session, Copy-Item can move files in either direction without setting up SMB or a separate transfer agent:

$session = New-PSSession -ComputerName 'web01'

Copy-Item -Path 'C:\Build\release.zip' `
          -Destination 'C:\Deploy\' `
          -ToSession $session

Copy-Item -Path 'C:\Logs\app.log' `
          -Destination 'C:\Triage\web01-app.log' `
          -FromSession $session

Remove-PSSession $session

This rides the existing WinRM channel, so any firewall rule that permits remoting also permits file transfer. It is slower than SMB but works in environments where SMB is locked down.

Frequently Asked Questions

What is the difference between Enter-PSSession and Invoke-Command?

Enter-PSSession opens an interactive remote prompt — you type, the remote runs, you see the output as if you were on the box. Invoke-Command sends a script block, runs it remotely, and returns the result to your local pipeline. Use the first for hands-on troubleshooting and the second for automation.

Why does my remote script work locally but fail when it touches a file share?

This is the classic double-hop problem. The first hop authenticates you with Kerberos, but the credential does not delegate to a second remote server by default. The fixes are CredSSP (broad, less safe) or resource-based Kerberos constrained delegation (narrow, recommended). Microsoft documents both in detail.

Do I need PowerShell 7 to use remoting?

No — Windows PowerShell 5.1 ships with remoting enabled by default on Windows Server. PowerShell 7 adds SSH-based remoting as an alternative transport, which is what you want when targeting Linux machines or working across firewalled boundaries where WinRM is blocked.

Is HTTP/5985 actually insecure?

The payload is authenticated and message-level encrypted with the user's session key, so on a trusted LAN it is fine. The case for HTTPS/5986 is defence in depth and protection against downgrade attacks if Kerberos is unavailable. Treat 5985 as acceptable inside a controlled network, 5986 as required for anything else.

Can attackers abuse PowerShell remoting?

Yes — it is a common lateral-movement primitive. The mitigations are the same as for any privileged remote access: restrict the WinRM firewall rule to management subnets, require admin group membership for connection, log session usage via PowerShell module logging and script block logging, and audit Windows Event ID 4104 in the PowerShell operational log.

Conclusion

The point of remoting is not the cmdlets — it is the operational reach they give you when something needs fixing across more than one machine. Get the listener story right, prefer Kerberos inside a domain, scope TrustedHosts tightly outside one, and treat HTTPS as the default for anything that touches an untrusted network. Done that way, remoting is one of the most valuable tools in a Windows admin's kit. Done badly, it is a quiet path for lateral movement.

Related Posts

Microsoft's reference architecture for remoting and the security trade-offs lives at Running Remote Commands on Microsoft Learn.

0 comments:

Post a Comment