What Is PowerShell Remoting?
PowerShell Remoting enables you to run PowerShell commands and scripts on remote Windows computers as if you were sitting at the local console. It is built on top of the WS-Management protocol (WinRM), which uses HTTP (port 5985) or HTTPS (port 5986) for transport. PowerShell Remoting is the standard tool for managing Windows Server 2022 at scale — replacing ad-hoc RDP sessions with scripted, repeatable, auditable remote operations.
Understanding remoting is especially important in server environments where interactive logins are discouraged for security reasons. With remoting, you can run commands against dozens of servers from a single management workstation, collect data in parallel, and automate complex multi-server workflows without ever opening a Remote Desktop connection.
Enabling PowerShell Remoting
On Windows Server 2022, PowerShell Remoting is enabled by default on all server editions (it was first enabled by default in Server 2012 R2). You can verify and enable it with a single command run in an elevated PowerShell session:
Enable-PSRemoting -Force
The -Force parameter suppresses confirmation prompts. This command performs several actions: it starts and sets the WinRM service to start automatically, configures a WinRM listener on HTTP port 5985, creates a Windows Firewall rule allowing inbound WinRM traffic, and registers the default session configurations (Microsoft.PowerShell, Microsoft.PowerShell32, Microsoft.PowerShell.Workflow).
If the server is on a public network profile, Enable-PSRemoting will warn you. You can override this:
Enable-PSRemoting -Force -SkipNetworkProfileCheck
To check the current WinRM configuration:
winrm get winrm/config
To list the registered session configurations:
Get-PSSessionConfiguration
To check which WinRM listeners are active:
winrm enumerate winrm/config/listener
If you need to enable remoting on a remote server (where WinRM is already running but PSRemoting sessions are not enabled), you can use Invoke-Command over WMI or psexec, but the most common approach is to enable it via Group Policy using the setting Allow remote server management through WinRM under Computer Configuration > Policies > Administrative Templates > Windows Components > Windows Remote Management (WinRM) > WinRM Service.
Entering an Interactive Remote Session
The simplest form of PowerShell Remoting is an interactive session using Enter-PSSession. This gives you a shell prompt that behaves as if you are at the remote server’s console.
Connect to a remote server by name (uses your current Windows credentials):
Enter-PSSession -ComputerName SRV-WEB01
Your prompt changes to indicate you are in a remote session:
[SRV-WEB01]: PS C:UsersAdministratorDocuments>
Connect using alternate credentials:
$Cred = Get-Credential -UserName "CONTOSOadministrator" -Message "Enter admin credentials"
Enter-PSSession -ComputerName SRV-WEB01 -Credential $Cred
Connect using HTTPS on port 5986 with SSL:
Enter-PSSession -ComputerName SRV-WEB01 -UseSSL -Port 5986 -Credential $Cred
Exit the remote session:
Exit-PSSession
Running Commands Remotely with Invoke-Command
Invoke-Command is the workhorse of PowerShell Remoting. Unlike Enter-PSSession, it runs a script block or script file on one or more remote computers and returns the output to your local session. This is the correct tool for automation and scripted administration.
Run a command on a single remote server:
Invoke-Command -ComputerName SRV-WEB01 -ScriptBlock { Get-Service -Name W3SVC }
Run a command on multiple servers simultaneously (Invoke-Command fans out to all targets in parallel by default):
$Servers = @("SRV-WEB01", "SRV-WEB02", "SRV-DB01", "SRV-APP01")
Invoke-Command -ComputerName $Servers -ScriptBlock {
[PSCustomObject]@{
Server = $env:COMPUTERNAME
CPULoad = (Get-CimInstance Win32_Processor).LoadPercentage
FreeRAM = [math]::Round((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory / 1MB, 2)
Uptime = (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
}
}
Pass local variables into a remote script block using the $Using: scope modifier:
$ServiceName = "Spooler"
Invoke-Command -ComputerName SRV-WEB01 -ScriptBlock {
Get-Service -Name $Using:ServiceName | Stop-Service -Force
Write-Output "Stopped $Using:ServiceName"
}
Run a local script file on a remote computer:
Invoke-Command -ComputerName SRV-WEB01 -FilePath "C:Scriptsconfigure_iis.ps1"
Use alternate credentials:
$Cred = Get-Credential
Invoke-Command -ComputerName SRV-WEB01 -Credential $Cred -ScriptBlock { whoami }
Persistent Sessions with New-PSSession
By default, Invoke-Command creates a new session for each call and tears it down when the command finishes. This is efficient for simple tasks, but if you need to run multiple commands and preserve state between them (for example, module imports, variable values, or changes to the working directory), you need a persistent session created with New-PSSession.
Create a persistent session:
$Session = New-PSSession -ComputerName SRV-WEB01
Run multiple commands that share state in the session:
Invoke-Command -Session $Session -ScriptBlock { Import-Module WebAdministration }
Invoke-Command -Session $Session -ScriptBlock { Get-Website }
Invoke-Command -Session $Session -ScriptBlock { Stop-Website -Name "Default Web Site" }
Enter an interactive session on an existing PSSession object:
Enter-PSSession -Session $Session
List all active sessions:
Get-PSSession
Create sessions to multiple servers at once:
$Sessions = New-PSSession -ComputerName SRV-WEB01, SRV-WEB02, SRV-APP01
Always clean up sessions when you are finished to release resources on both ends:
Remove-PSSession -Session $Session
# Or remove all sessions:
Get-PSSession | Remove-PSSession
Copying Files with PowerShell Remoting
Copy-Item supports the -ToSession and -FromSession parameters to transfer files over a PSSession without requiring SMB access or shared drives. This is useful in restricted network environments.
Copy a local file to a remote server:
$Session = New-PSSession -ComputerName SRV-WEB01
Copy-Item -Path "C:Installersapp_setup.msi" `
-Destination "C:Tempapp_setup.msi" `
-ToSession $Session
Copy a file from a remote server to local:
Copy-Item -Path "C:Logsapp.log" `
-Destination "C:CollectedSRV-WEB01_app.log" `
-FromSession $Session
Copy an entire directory recursively:
Copy-Item -Path "C:AppConfig" `
-Destination "C:AppConfig" `
-ToSession $Session -Recurse
Configuring HTTPS Remoting
By default, WinRM uses HTTP with message-level encryption (the data is encrypted even over HTTP using WS-Management’s built-in SOAP security). However, for cross-domain scenarios, workgroup environments, or compliance requirements, you may need HTTPS transport using a proper TLS certificate.
First, obtain or create a certificate for the server. In a test environment, use a self-signed certificate:
$Cert = New-SelfSignedCertificate -DnsName "SRV-WEB01.contoso.com" `
-CertStoreLocation "cert:LocalMachineMy" `
-FriendlyName "WinRM HTTPS Certificate"
$Thumbprint = $Cert.Thumbprint
Create the HTTPS listener:
winrm create winrm/config/listener?Address=*+Transport=HTTPS `
"@{Hostname=`"SRV-WEB01.contoso.com`"; CertificateThumbprint=`"$Thumbprint`"}"
Open the firewall port for HTTPS (5986):
New-NetFirewallRule -DisplayName "WinRM HTTPS" -Direction Inbound `
-Protocol TCP -LocalPort 5986 -Action Allow
Connect from a client using HTTPS. If using a self-signed certificate, you must trust it or use -SkipCACheck and -SkipCNCheck in a PSSessionOption:
$SessionOption = New-PSSessionOption -SkipCACheck -SkipCNCheck
Enter-PSSession -ComputerName SRV-WEB01.contoso.com -UseSSL `
-Credential $Cred -SessionOption $SessionOption
SSH-Based Remoting with PowerShell 7
PowerShell 7 (which can be installed side-by-side with Windows PowerShell 5.1 on Server 2022) adds support for SSH as an alternative transport for remoting. This enables cross-platform remoting between Windows, Linux, and macOS without WinRM.
Install OpenSSH Server on the Windows Server 2022 target:
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Start-Service sshd
Set-Service -Name sshd -StartupType Automatic
Configure the SSH subsystem for PowerShell 7 by adding this line to C:ProgramDatasshsshd_config:
Subsystem powershell C:Program FilesPowerShell7pwsh.exe -sshs -NoLogo
Restart the SSH service after editing the config:
Restart-Service sshd
Connect using SSH-based PS remoting from a client running PowerShell 7:
Enter-PSSession -HostName SRV-WEB01.contoso.com -UserName administrator -SSHTransport
Using PS Remoting in Scripts
When writing scripts that use remoting, handle errors gracefully and always clean up sessions. Here is a production-quality pattern for running a command against a list of servers and reporting results:
$Servers = Get-Content "C:Scriptsserver_list.txt"
$Results = [System.Collections.Generic.List[PSObject]]::new()
foreach ($Server in $Servers) {
try {
$Data = Invoke-Command -ComputerName $Server -ErrorAction Stop -ScriptBlock {
[PSCustomObject]@{
Server = $env:COMPUTERNAME
OSBuild = (Get-ItemProperty "HKLM:SOFTWAREMicrosoftWindows NTCurrentVersion").CurrentBuild
LastBoot = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
DiskFree = [math]::Round(
(Get-PSDrive C).Free / 1GB, 2
)
Status = "OK"
}
}
$Results.Add($Data)
} catch {
$Results.Add([PSCustomObject]@{
Server = $Server
Status = "FAILED: $($_.Exception.Message)"
})
}
}
$Results | Export-Csv -Path "C:Reportsserver_status_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation
$Results | Format-Table -AutoSize