How to Enable and Configure WinRM for Remote Management on Windows Server 2025
Windows Remote Management (WinRM) is the Microsoft implementation of the WS-Management protocol, and it is the transport layer that powers PowerShell remoting, the Invoke-Command and Enter-PSSession cmdlets, Windows Admin Center, and third-party configuration management tools such as Ansible. On Windows Server 2025, WinRM can be configured to listen on HTTP (port 5985) for trusted internal networks or HTTPS (port 5986) for encrypted connections — with HTTPS being the correct choice whenever traffic crosses an untrusted network segment. This guide covers everything you need to get WinRM running on a fresh Windows Server 2025 installation: enabling the service, testing connectivity, running remote commands, configuring HTTPS with a self-signed or CA-issued certificate, managing trusted hosts for workgroup environments, integrating with Ansible, and safely disabling WinRM when it is no longer needed.
Prerequisites
- Windows Server 2025 with an elevated Windows PowerShell 5.1 session
- Local administrator or domain administrator privileges
- Network connectivity between the management workstation and the target server
- For HTTPS configuration: a certificate in the local machine certificate store
- For Ansible integration: Python and the
pywinrmpackage on the Ansible control node - For workgroup environments: the IP address or hostname of the target server known in advance
Step 1 — Enable WinRM with Enable-PSRemoting
Enable-PSRemoting is the simplest way to configure WinRM on a server. It starts the WinRM service, sets it to start automatically, creates the default HTTP listener, and adds the necessary firewall rules in a single command.
# Enable PowerShell Remoting (run as Administrator)
Enable-PSRemoting -Force
# What Enable-PSRemoting does:
# 1. Starts and sets the WinRM service to Automatic startup
# 2. Creates an HTTP listener on port 5985
# 3. Configures the WinRM service to accept requests
# 4. Enables the Windows Firewall rule for WinRM (all profiles)
# 5. Registers the PowerShell session configurations
# Verify the WinRM service is running
Get-Service WinRM
# Confirm the HTTP listener was created
winrm enumerate winrm/config/listener
# Alternative low-level configuration command (equivalent to Enable-PSRemoting)
winrm quickconfig -force
Step 2 — Verify WinRM is Listening
After enabling WinRM, confirm the service is actually accepting connections before attempting to connect remotely.
# Check what WinRM is listening on (should show 5985 for HTTP, 5986 for HTTPS)
netstat -ano | findstr ":5985"
netstat -ano | findstr ":5986"
# View the full WinRM configuration
winrm get winrm/config
# View just the listener configuration
winrm get winrm/config/listener?Address=*+Transport=HTTP
# Test WinRM is responding on the local machine
Test-WSMan
# Test WinRM on a remote server
Test-WSMan -ComputerName "SRVAPP01"
# Test with explicit credentials (for non-domain or cross-domain scenarios)
Test-WSMan `
-ComputerName "SRVAPP01" `
-Authentication Default `
-Credential (Get-Credential)
# Check the WinRM firewall rules
Get-NetFirewallRule -DisplayGroup "Windows Remote Management" |
Select-Object DisplayName, Enabled, Direction, Profile
Step 3 — Connect to a Remote Server with Enter-PSSession
Enter-PSSession opens an interactive remote PowerShell session — equivalent to logging into the server’s console, but over the network. Your prompt changes to indicate you are working inside a remote session.
# Connect to a remote server interactively (domain environment — uses current credentials)
Enter-PSSession -ComputerName "SRVAPP01"
# The prompt changes to: [SRVAPP01]: PS C:UsersadminDocuments>
# All commands you type run on the remote server
# Run a command on the remote server
Get-ComputerInfo | Select-Object CsName, OsName, OsVersion
# Exit the remote session and return to the local shell
Exit-PSSession
# Connect with explicit credentials (workgroup or cross-domain)
$cred = Get-Credential -UserName "SRVAPP01Administrator" -Message "Enter admin credentials"
Enter-PSSession -ComputerName "SRVAPP01" -Credential $cred
# Connect using SSL (port 5986) — requires WinRM HTTPS listener configured
Enter-PSSession -ComputerName "SRVAPP01" -UseSSL -Credential $cred
Step 4 — Run Remote Commands with Invoke-Command
Invoke-Command is used for non-interactive remote execution — running one or more commands on one or more remote servers and returning the results to the local session. This is the foundation of remote automation.
# Run a single command on a remote server
Invoke-Command -ComputerName "SRVAPP01" -ScriptBlock {
Get-Service | Where-Object { $_.Status -eq 'Stopped' }
}
# Run against multiple servers simultaneously
$servers = @("SRVAPP01", "SRVAPP02", "SRVDB01")
Invoke-Command -ComputerName $servers -ScriptBlock {
[PSCustomObject]@{
Server = $env:COMPUTERNAME
FreeSpaceGB = [math]::Round((Get-PSDrive C).Free / 1GB, 2)
Uptime = (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
}
}
# Pass a local variable into the remote script block using $Using:
$serviceName = "W3SVC"
Invoke-Command -ComputerName "SRVWEB01" -ScriptBlock {
Get-Service -Name $Using:serviceName
}
# Execute a local script file on a remote server
Invoke-Command -ComputerName "SRVAPP01" -FilePath "C:ScriptsCheckHealth.ps1"
# With explicit credentials
$cred = Get-Credential
Invoke-Command -ComputerName "SRVAPP01" -Credential $cred -ScriptBlock {
hostname
}
Step 5 — Create and Manage Persistent Sessions
Each Invoke-Command call creates and destroys a new connection, which has overhead. For multiple sequential commands against the same server, create a persistent PSSession and reuse it.
# Create a persistent session
$session = New-PSSession -ComputerName "SRVAPP01"
# Reuse the session for multiple commands — state is preserved between calls
Invoke-Command -Session $session -ScriptBlock { $x = 42 }
Invoke-Command -Session $session -ScriptBlock { Write-Host "x is $x" } # Outputs: x is 42
# Connect multiple sessions to multiple servers
$sessions = New-PSSession -ComputerName "SRVAPP01", "SRVAPP02", "SRVDB01"
# Run commands across all sessions
Invoke-Command -Session $sessions -ScriptBlock {
Get-EventLog -LogName System -Newest 5 -EntryType Error
}
# Copy files between the local machine and a remote server through a session
Copy-Item -Path "C:ScriptsDeploy.ps1" -Destination "C:Temp" -ToSession $session
Copy-Item -Path "C:TempReport.csv" -Destination "C:Reports" -FromSession $session
# List all active sessions
Get-PSSession
# Disconnect from a session without closing it (the session stays alive on the server)
Disconnect-PSSession -Session $session
# Reconnect to a disconnected session
$session = Connect-PSSession -ComputerName "SRVAPP01"
# Close and remove sessions when done
Remove-PSSession -Session $sessions
Step 6 — Configure a WinRM HTTPS Listener
HTTP WinRM transmits Kerberos-encrypted credentials but the payload is not encrypted unless you are in a domain with Kerberos. For workgroup environments, HTTPS connections, or any internet-facing management, configure the HTTPS listener on port 5986.
# Step A: Create a self-signed certificate (for lab/testing)
$serverFQDN = [System.Net.Dns]::GetHostByName("localhost").HostName
$cert = New-SelfSignedCertificate `
-DnsName $serverFQDN `
-CertStoreLocation "Cert:LocalMachineMy" `
-KeyUsage DigitalSignature, KeyEncipherment `
-KeyAlgorithm RSA `
-KeyLength 2048 `
-NotAfter (Get-Date).AddYears(2)
Write-Host "Certificate thumbprint: $($cert.Thumbprint)"
# Step B: Create the HTTPS WinRM listener
New-WSManInstance `
-ResourceURI winrm/config/listener `
-SelectorSet @{ Transport = "HTTPS"; Address = "*" } `
-ValueSet @{
Hostname = $serverFQDN
CertificateThumbprint = $cert.Thumbprint
}
# Step C: Open port 5986 in Windows Firewall
New-NetFirewallRule `
-DisplayName "WinRM HTTPS (5986)" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 5986 `
-Action Allow `
-Profile Domain, Private
# Step D: Verify both listeners are active
winrm enumerate winrm/config/listener
# Step E: Test the HTTPS connection
# The -SkipCACheck and -SkipCNCheck flags are only appropriate for self-signed certs in labs
$sessionOption = New-PSSessionOption -SkipCACheck -SkipCNCheck
Enter-PSSession -ComputerName $serverFQDN -UseSSL -SessionOption $sessionOption
Step 7 — Configure TrustedHosts for Workgroup Environments
In a domain, Kerberos handles mutual authentication between computers. In a workgroup, WinRM cannot verify the remote server’s identity, so you must explicitly list trusted hosts in the WinRM configuration on the client side.
# ── On the CLIENT machine that will initiate remote connections ──────────────
# Add a specific server by name
Set-Item WSMan:localhostClientTrustedHosts -Value "SRVAPP01" -Force
# Add multiple servers
Set-Item WSMan:localhostClientTrustedHosts -Value "SRVAPP01,SRVAPP02,192.168.1.30" -Force
# Append to existing list rather than replacing it
$existing = (Get-Item WSMan:localhostClientTrustedHosts).Value
Set-Item WSMan:localhostClientTrustedHosts -Value "$existing,SRVDB01" -Force
# Allow all hosts (NOT recommended for production — use only in isolated lab networks)
Set-Item WSMan:localhostClientTrustedHosts -Value "*" -Force
# Verify the current TrustedHosts list
Get-Item WSMan:localhostClientTrustedHosts
# Connect using explicit credentials (required in workgroup environments)
$cred = Get-Credential -UserName "SRVAPP01Administrator"
Enter-PSSession -ComputerName "SRVAPP01" -Credential $cred
Step 8 — Configure WinRM Firewall Rules
The Enable-PSRemoting command creates firewall rules automatically, but in tightly controlled environments you may need to restrict which source addresses can reach the WinRM ports.
# View existing WinRM firewall rules
Get-NetFirewallRule -DisplayGroup "Windows Remote Management" | Format-Table DisplayName, Enabled, Direction
# Restrict WinRM HTTP to specific management subnet only
$winrmRule = Get-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)"
Set-NetFirewallRule `
-InputObject $winrmRule `
-RemoteAddress "192.168.10.0/24" # Replace with your management network
# Create a dedicated HTTPS rule restricted to the management subnet
New-NetFirewallRule `
-DisplayName "WinRM HTTPS Management Subnet" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 5986 `
-RemoteAddress "192.168.10.0/24" `
-Action Allow `
-Profile Domain, Private
# Disable WinRM HTTP if you are using HTTPS exclusively
Disable-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)"
# Verify
Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*WinRM*" } |
Select-Object DisplayName, Enabled, Profile
Step 9 — Configure WinRM for Ansible Integration
Ansible manages Windows hosts via WinRM. The recommended approach for production is HTTPS with certificate authentication. For basic lab use, NTLM over HTTP is the quickest starting point.
# ── On the Windows Server being managed by Ansible ───────────────────────────
# Option A: Basic NTLM over HTTP (lab use only — credentials sent over the network)
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
# Option B: NTLM over HTTPS (recommended minimum for production)
# Ensure the HTTPS listener is configured (Step 6 above), then:
winrm set winrm/config/service/auth '@{Ntlm="true"}'
# AllowUnencrypted remains false — traffic is encrypted by HTTPS
# Option C: Kerberos (domain environments — most secure, no credential exposure)
# No additional WinRM configuration needed — works automatically in domain
# In Ansible inventory (ansible_winrm_transport: kerberos)
# Verify authentication methods
winrm get winrm/config/service/auth
# ── Ansible inventory file (hosts.ini or YAML) ──────────────────────────────
# [windows_servers]
# SRVAPP01 ansible_host=192.168.1.20
#
# [windows_servers:vars]
# ansible_user=Administrator
# ansible_password=SecurePassword
# ansible_connection=winrm
# ansible_winrm_transport=ntlm # or: kerberos, credssp, certificate
# ansible_winrm_scheme=https # use https for port 5986
# ansible_port=5986
# ansible_winrm_server_cert_validation=ignore # only for self-signed certs in lab
# ── Test the Ansible connection from the control node ────────────────────────
# ansible SRVAPP01 -m win_ping -i hosts.ini
Step 10 — Tune WinRM Service Settings
The default WinRM limits are suitable for small deployments but may need adjustment in busy environments with many concurrent management sessions.
# View current WinRM service configuration
winrm get winrm/config/service
# Increase the maximum number of concurrent operations per user
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="50"}'
# Increase the shell operation timeout (milliseconds — default is 60000 = 60 seconds)
winrm set winrm/config/winrs '@{OperationTimeout="PT120S"}'
# Increase the maximum memory per shell (MB — default is 150)
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="512"}'
# View current WinRS (WinRM Shell) settings
winrm get winrm/config/winrs
# Apply settings via PowerShell for scripting clarity
Set-Item WSMan:localhostServiceMaxConcurrentOperationsPerUser -Value 50
Set-Item WSMan:localhostShellMaxMemoryPerShellMB -Value 512
Step 11 — Disable WinRM When Not Needed
On servers that do not require remote management, WinRM should be disabled to reduce the attack surface. This is particularly important for internet-facing servers or those in highly sensitive environments.
# Remove the HTTP listener
Remove-WSManInstance `
-ResourceURI winrm/config/listener `
-SelectorSet @{ Transport = "HTTP"; Address = "*" }
# Remove the HTTPS listener
Remove-WSManInstance `
-ResourceURI winrm/config/listener `
-SelectorSet @{ Transport = "HTTPS"; Address = "*" }
# Stop and disable the WinRM service
Stop-Service WinRM -Force
Set-Service WinRM -StartupType Disabled
# Disable the WinRM firewall rules
Disable-NetFirewallRule -DisplayGroup "Windows Remote Management"
Disable-NetFirewallRule -DisplayName "WinRM HTTPS (5986)" -ErrorAction SilentlyContinue
# Verify WinRM is no longer listening
netstat -ano | findstr ":5985"
netstat -ano | findstr ":5986"
# Disable PowerShell remoting (removes session configurations)
Disable-PSRemoting -Force
Conclusion
WinRM is an essential building block of any Windows Server 2025 management strategy. Whether you are running interactive sessions with Enter-PSSession, executing scripts across dozens of servers in parallel with Invoke-Command, transferring files through persistent PSSession objects, or integrating Windows servers into an Ansible automation framework, every scenario depends on WinRM being correctly configured and secured. For production environments, the non-negotiable baseline is: HTTPS on port 5986 with a trusted certificate, firewall rules that restrict access to the management network, and Kerberos or certificate authentication rather than NTLM or Basic. Invest the time to configure WinRM properly from the start and you gain a secure, reliable, and incredibly capable remote management foundation that scales from a single server to an entire data centre fleet.