Overview of WinRM and Its Role in Ansible Management
Windows Remote Management (WinRM) is Microsoft’s implementation of the WS-Management protocol, a DMTF standard for remote management of hardware and software. WinRM enables remote PowerShell sessions, remote command execution, and is the primary transport mechanism that Ansible uses when managing Windows hosts. Unlike Linux targets where Ansible uses SSH, Windows targets use WinRM by default — though SSH is now also supported on Windows (covered later). Understanding WinRM’s architecture is essential for setting up reliable Ansible automation against Windows Server 2022.
WinRM operates on two ports: HTTP on port 5985 for unencrypted transport (message-level encryption is still applied by the authentication mechanism in most cases) and HTTPS on port 5986 for transport-level TLS encryption. For production environments, HTTPS is strongly recommended. The WinRM service (Windows Remote Management) must be running on the target Windows Server 2022 machine, and appropriate firewall rules must permit inbound connections on the chosen port.
Enabling WinRM on Windows Server 2022
On Windows Server 2022, WinRM can be enabled quickly using the built-in quickconfig command. Open an elevated PowerShell session on the Windows Server 2022 target and run:
winrm quickconfig
This command starts the WinRM service, sets it to start automatically, creates an HTTP listener on port 5985, and adds a Windows Firewall rule permitting inbound WinRM traffic. Alternatively, use PowerShell’s remoting configuration cmdlet which also configures the necessary session configurations:
Enable-PSRemoting -Force
The -Force switch suppresses confirmation prompts. This is equivalent to winrm quickconfig plus it registers the PowerShell remoting session configurations (Microsoft.PowerShell, Microsoft.PowerShell32, and Microsoft.PowerShell.Workflow). Verify the listener was created successfully:
winrm enumerate winrm/config/listener
The output should show a listener on Transport=HTTP, Port=5985. To view the full WinRM service configuration:
winrm get winrm/config
Configuring WinRM HTTPS Listener
For Ansible in production, configure WinRM to use HTTPS. You need a certificate whose Subject or Subject Alternative Name matches the hostname Ansible will use to connect. First, create a self-signed certificate suitable for WinRM:
$hostname = $env:COMPUTERNAME
$cert = New-SelfSignedCertificate `
-DnsName $hostname `
-CertStoreLocation "Cert:LocalMachineMy" `
-KeyLength 2048 `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears(3)
Write-Host "Certificate thumbprint: $($cert.Thumbprint)"
Create the WinRM HTTPS listener using the certificate thumbprint:
$thumbprint = $cert.Thumbprint
$cmd = "winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname=`"$hostname`";CertificateThumbprint=`"$thumbprint`"}"
Invoke-Expression $cmd
Alternatively, using PowerShell’s WSMan provider:
New-Item -Path WSMan:LocalhostListener -Transport HTTPS -Address * -CertificateThumbprint $cert.Thumbprint -Force
Add the Windows Firewall rule for port 5986:
New-NetFirewallRule `
-Name "WinRM-HTTPS-Inbound" `
-DisplayName "WinRM HTTPS (5986)" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 5986 `
-Action Allow `
-Profile Domain, Private
Restart the WinRM service to apply all changes:
Restart-Service WinRM
Installing pywinrm on the Ansible Control Node
Ansible’s WinRM connection plugin requires the pywinrm Python library on the Ansible control node (which runs Linux — Ansible itself does not run on Windows). Install it using pip:
pip install pywinrm
pip install pywinrm[kerberos] # For Kerberos/AD authentication
pip install pywinrm[credssp] # For CredSSP authentication
Verify the installation:
python3 -c "import winrm; print(winrm.__version__)"
Creating an Ansible Inventory for Windows Hosts
Create an inventory file that specifies Windows Server 2022 hosts and their connection variables. A typical inventory in INI format:
[windows]
winserver01.contoso.com
winserver02.contoso.com
[windows:vars]
ansible_user=Administrator
ansible_password=YourSecurePassword123!
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_port=5985
ansible_winrm_scheme=http
ansible_winrm_server_cert_validation=ignore
For HTTPS with certificate validation disabled (self-signed cert scenario):
[windows:vars]
ansible_user=Administrator
ansible_password=YourSecurePassword123!
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_port=5986
ansible_winrm_scheme=https
ansible_winrm_server_cert_validation=ignore
For Kerberos authentication against a domain-joined Windows Server 2022 host (recommended for domain environments):
[windows:vars]
ansible_user=CONTOSOansible-svc
ansible_password=ServiceAcctPassword
ansible_connection=winrm
ansible_winrm_transport=kerberos
ansible_winrm_port=5985
ansible_winrm_kerberos_delegation=true
WinRM Authentication Methods Comparison
WinRM supports several authentication mechanisms, each with different security characteristics. Basic authentication sends credentials in Base64 — only acceptable over HTTPS, never plain HTTP. NTLM uses challenge-response and works on both domain and non-domain systems, transmitting a hash rather than the password. Kerberos is the most secure option for domain-joined hosts and avoids passing credentials over the wire entirely — the Ansible control node obtains a Kerberos ticket from the domain controller. CredSSP delegates credentials to the remote host (useful for double-hop scenarios), but requires additional setup and carries higher risk. Certificate-based auth uses a client certificate mapped to a Windows user account — the most secure option for non-domain scenarios.
To configure WinRM on the server to allow specific authentication methods:
# Enable NTLM authentication
Set-Item -Path WSMan:LocalhostServiceAuthNTLM -Value $true
# Enable Kerberos authentication
Set-Item -Path WSMan:LocalhostServiceAuthKerberos -Value $true
# Enable Basic authentication (HTTPS only)
Set-Item -Path WSMan:LocalhostServiceAuthBasic -Value $true
# Allow unencrypted (for HTTP/NTLM — not recommended for production)
Set-Item -Path WSMan:LocalhostServiceAllowUnencrypted -Value $true
Essential Ansible Windows Modules
Ansible provides a rich library of Windows-specific modules. The following are the most commonly used when managing Windows Server 2022:
win_command — runs a command that does not need shell features:
- name: Run ipconfig
win_command: ipconfig /all
register: ipconfig_output
win_shell — runs a command through PowerShell, supporting pipes, redirection, and PS syntax:
- name: Get disk usage
win_shell: Get-PSDrive C | Select-Object Used, Free
register: disk_info
win_service — manages Windows services:
- name: Ensure W32tm service is running
win_service:
name: w32tm
state: started
start_mode: auto
win_feature — installs or removes Windows Server features:
- name: Install IIS
win_feature:
name: Web-Server
state: present
include_management_tools: yes
include_sub_features: yes
win_package — installs MSI or EXE packages:
- name: Install 7-Zip
win_package:
path: C:Installers7z2301-x64.msi
state: present
arguments: /quiet /norestart
Running Your First Ansible Playbook Against Windows Server 2022
Create a simple playbook file named setup_windows.yml:
---
- name: Configure Windows Server 2022
hosts: windows
gather_facts: yes
tasks:
- name: Ensure Windows Firewall is enabled on all profiles
win_firewall:
profiles: Domain, Private, Public
state: enabled
enabled: yes
- name: Set timezone to UTC
win_timezone:
timezone: UTC
- name: Ensure Remote Registry service is disabled
win_service:
name: RemoteRegistry
state: stopped
start_mode: disabled
- name: Install Windows Updates (security only)
win_updates:
category_names:
- SecurityUpdates
reboot: no
state: installed
register: update_result
- name: Print update results
debug:
var: update_result.found_update_count
Run the playbook with verbosity enabled to see WinRM connection details:
ansible-playbook -i inventory.ini setup_windows.yml -vvv
Test connectivity before running a full playbook using the win_ping module:
ansible windows -i inventory.ini -m win_ping
A successful response returns “pong”, confirming WinRM connectivity, authentication, and that the Ansible Windows modules are functioning correctly on the target Windows Server 2022 host.
WinRM Firewall Rules and Hardening
On Windows Server 2022, restrict WinRM access to specific source IP ranges rather than allowing all connections. Replace the broad firewall rule created by Enable-PSRemoting with a restricted one:
# Remove the broad rule
Remove-NetFirewallRule -DisplayName "Windows Remote Management (HTTP-In)" -ErrorAction SilentlyContinue
# Create a restricted rule allowing only your Ansible control node
New-NetFirewallRule `
-Name "WinRM-Ansible-Only" `
-DisplayName "WinRM Ansible Control Node Only" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 5985 `
-RemoteAddress 10.10.10.50 `
-Action Allow `
-Profile Any
Also consider setting the MaxConcurrentOperationsPerUser and MaxConnections limits in WinRM to prevent resource exhaustion from misconfigured playbooks:
Set-Item -Path WSMan:LocalhostServiceMaxConcurrentOperationsPerUser -Value 50
winrm set winrm/config @{MaxTimeoutms="1800000"}