PowerShell Remoting (WinRM, Invoke-Command, PSSessions)
PowerShell Intermediate/Advanced
Chapter 3 · PowerShell Remoting (WinRM, Invoke-Command, PSSessions)
Everything since Fundamentals 1 has run against the local machine. This chapter is where PowerShell stops being a single-machine tool: Invoke-Command runs a script block on another computer entirely and brings the results back — but "brings the results back" hides a real, important asterisk. What comes back across the network isn't the same kind of live object Fundamentals 1 built this whole course around; understanding exactly what's lost in that trip is this chapter's own central lesson.
What Remoting Actually Is: WinRM
PowerShell Remoting runs over WinRM (Windows Remote Management), Microsoft's implementation of the WS-Management protocol — a separate transport from SSH, though PowerShell 7+ can optionally use SSH instead once it's configured on both ends. One-time setup, run as Administrator on the machine you want to remote into:
This starts the WinRM service, creates a listener for incoming remoting connections, and opens the necessary firewall rule — all three are required before any of the cmdlets below will succeed against that machine.
Invoke-Command: Running Code on a Remote Machine
The script block runs entirely on Server01, not on the local machine — and only the results travel back over the network, not a live connection to keep working with them afterward.
The Deserialization Gotcha: What Comes Back Isn't "Live"
System.Diagnostics.Process object, and its ability to actually terminate a running process, never left Server01 at all — what you're holding locally is a detailed, read-only snapshot, type-prefixed Deserialized. as an explicit signal of exactly that. This is a deliberate architectural boundary, not a limitation to work around: letting a remote object's live methods execute back on your machine (or worse, letting your local methods silently execute back on the remote one) would be a genuine security problem, not a convenience worth having.
Passing Local Data In: the $using: Scope Modifier
A remote script block runs in a completely separate process on a completely separate machine — it has no visibility into local variables by default, even ones defined on the very line above the call:
$using: is the explicit signal "reach across and grab this value from my local session" — without it, the remote script block only ever sees its own, entirely empty, local scope.
Persistent Connections: New-PSSession & State
Invoke-Command -ComputerName opens a fresh connection, runs the script block, and tears the whole thing down again — every single time. A variable set in one -ComputerName call is completely gone by the next one, even against the identical computer, because there was never a persistent session for it to survive in:
New-PSSession the moment state needs to survive between calls — it's the difference between one-shot remote commands and an actual ongoing remote working session.
Enter-PSSession -ComputerName Server01 is the fully interactive version of the same idea — it drops your prompt directly into the remote machine's own PowerShell session, exactly as if you'd logged in locally, until Exit-PSSession (or plain exit) returns you home.
Multiple Computers at Once — Automatic Parallelism
.GetType().Name the way this chapter's own $procs[0] example did — seeing the Deserialized. prefix is an immediate, reliable signal that only data, not behavior, made the trip back.
Hands-On Exercises
Explain why $procs[0].Kill() fails after Invoke-Command -ComputerName Server01 -ScriptBlock { Get-Process }, even though $procs[0].ProcessName works perfectly fine. Use this chapter's own deserialization explanation.
Explain why $serviceName isn't visible inside a remote script block without $using:serviceName, even though it's clearly defined in the local session on the line right before the Invoke-Command call.
Explain why a variable set inside one Invoke-Command -ComputerName call doesn't persist into a second Invoke-Command -ComputerName call against the same computer. What would you change to make it persist?
Chapter 3 Quick Reference
- WinRM / WS-Management — the protocol underneath PowerShell Remoting;
Enable-PSRemoting -Forceis the one-time setup Invoke-Command -ComputerName / -ScriptBlock— runs code remotely, returns only results, no persistent connection- Deserialized objects — remote results keep their data (properties) but lose their behavior (methods); type-prefixed
Deserialized. $using:variableName— explicitly injects a local value into a remote script block's otherwise-empty scopeNew-PSSession/-Session— a persistent connection; variables and state survive between multipleInvoke-Commandcalls-ComputerNamealone is stateless — every call is a brand-new connection; nothing persists between callsEnter-PSSession— a fully interactive remote prompt;Exit-PSSessionto return- Multiple computers run in parallel automatically —
-ThrottleLimitcontrols the maximum concurrent (default 32) - Next chapter: Background & Parallel Execution (Start-Job, ForEach-Object -Parallel, a Runspaces intro)