Ansible Windows WinRM Setup — Remote Management Guide

Introduction

Ansible manages Windows hosts through WinRM (Windows Remote Management) instead of SSH. Setting up WinRM correctly — with HTTPS, proper authentication, and firewall rules — is the critical first step for Windows automation. This guide covers everything from basic setup to enterprise GPO deployment.

Prerequisites

# On the Ansible controller (Linux)
pip install pywinrm
pip install pywinrm[credssp]  # For CredSSP authentication

# Verify
python -c "import winrm; print(winrm.__version__)"

Quick WinRM Setup (PowerShell)

Run on each Windows host as Administrator:

# Enable WinRM with HTTPS
winrm quickconfig -transport:https

# Or use the Ansible setup script (recommended)
$url = "https://raw.githubusercontent.com/ansible/ansible-documentation/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
$file = "$env:temp\ConfigureRemotingForAnsible.ps1"
(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)
powershell.exe -ExecutionPolicy ByPass -File $file

Manual WinRM Configuration

# Step 1: Enable WinRM service
Enable-PSRemoting -Force

# Step 2: Create self-signed certificate
$cert = New-SelfSignedCertificate -DnsName "$env:COMPUTERNAME" `
  -CertStoreLocation Cert:\LocalMachine\My `
  -NotAfter (Get-Date).AddYears(5)

# Step 3: Create HTTPS listener
winrm create winrm/config/Listener?Address=*+Transport=HTTPS `
  "@{Hostname=`"$env:COMPUTERNAME`"; CertificateThumbprint=`"$($cert.Thumbprint)`"}"

# Step 4: Open firewall port
New-NetFirewallRule -DisplayName "WinRM HTTPS" `
  -Direction Inbound -LocalPort 5986 `
  -Protocol TCP -Action Allow

# Step 5: Configure authentication
Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $true
Set-Item -Path WSMan:\localhost\Service\Auth\CredSSP -Value $true

# Step 6: Allow unencrypted (only for testing with HTTP)
# Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $true

Ansible Inventory for Windows

# inventory.yml
all:
  children:
    windows:
      hosts:
        win-web01:
          ansible_host: 192.168.1.100
        win-web02:
          ansible_host: 192.168.1.101
        win-db01:
          ansible_host: 192.168.1.200
      vars:
        ansible_user: ansible_admin
        ansible_password: "{{ vault_windows_password }}"
        ansible_connection: winrm
        ansible_winrm_transport: ntlm
        ansible_winrm_server_cert_validation: ignore
        ansible_port: 5986
        ansible_winrm_scheme: https

Authentication Methods

# NTLM (most common, works with local and domain accounts)
ansible_winrm_transport: ntlm

# Kerberos (Active Directory, most secure)
ansible_winrm_transport: kerberos
ansible_winrm_kerberos_delegation: true

# CredSSP (supports double-hop authentication)
ansible_winrm_transport: credssp

# Basic (only over HTTPS, local accounts only)
ansible_winrm_transport: basic

Kerberos Setup

# On the Ansible controller
pip install pywinrm[kerberos]
sudo apt install krb5-user  # Debian/Ubuntu
sudo dnf install krb5-workstation  # RHEL

# Configure /etc/krb5.conf
[realms]
EXAMPLE.COM = {
    kdc = dc01.example.com
    admin_server = dc01.example.com
}

[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
# Inventory with Kerberos
windows:
  vars:
    ansible_user: ansible_admin@EXAMPLE.COM
    ansible_winrm_transport: kerberos
    ansible_connection: winrm
    ansible_port: 5986

Test Connection

# Ping Windows hosts
ansible windows -m win_ping -i inventory.yml

# Gather facts
ansible windows -m setup -i inventory.yml

GPO Deployment (Enterprise)

# Deploy WinRM settings via Group Policy
# Computer Configuration → Administrative Templates → Windows Components → Windows Remote Management

# GPO settings:
# 1. Allow remote server management through WinRM: Enabled
#    - IPv4 filter: * (or specific subnet)
# 2. Turn on Script Execution: Allow all scripts
# 3. Windows Firewall: Allow inbound on 5985/5986

Common Windows Playbook

---
- name: Configure Windows servers
  hosts: windows
  tasks:
    - name: Install IIS
      ansible.windows.win_feature:
        name: Web-Server
        include_management_tools: true
        state: present

    - name: Create website directory
      ansible.windows.win_file:
        path: C:\inetpub\myapp
        state: directory

    - name: Deploy configuration
      ansible.windows.win_template:
        src: web.config.j2
        dest: C:\inetpub\myapp\web.config

    - name: Ensure service is running
      ansible.windows.win_service:
        name: W3SVC
        start_mode: auto
        state: started

    - name: Open firewall port
      community.windows.win_firewall_rule:
        name: "HTTP Inbound"
        localport: 80
        action: allow
        direction: in
        protocol: tcp
        state: present
        enabled: true

    - name: Install Windows updates
      ansible.windows.win_updates:
        category_names:
          - SecurityUpdates
          - CriticalUpdates
        reboot: true
        reboot_timeout: 3600
      register: updates

    - name: Show update results
      ansible.builtin.debug:
        msg: "{{ updates.installed_update_count }} updates installed"

WinRM Verification Commands

# Check WinRM service status
Get-Service WinRM

# List listeners
winrm enumerate winrm/config/Listener

# Check configuration
winrm get winrm/config

# Test WinRM locally
Test-WSMan -ComputerName localhost

# Check authentication settings
winrm get winrm/config/Service/Auth

# Check HTTPS certificate
Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -match $env:COMPUTERNAME }

Troubleshooting

IssueSolution
Connection refusedEnable WinRM: Enable-PSRemoting -Force
SSL certificate errorAdd ansible_winrm_server_cert_validation: ignore
Authentication failedCheck username format: user (local) vs user@DOMAIN (Kerberos)
"Access denied"User must be in local Administrators group
Timeout errorsCheck firewall port 5986, increase ansible_winrm_operation_timeout_sec
Double-hop failsUse CredSSP: ansible_winrm_transport: credssp
Kerberos "Clock skew"Sync time between controller and DC: ntpdate dc01.example.com

Best Practices

  1. Always use HTTPS (port 5986) — never HTTP (5985) in production
  2. Use Kerberos for Active Directory environments — most secure
  3. Use CredSSP when double-hop auth is needed (accessing network shares)
  4. Deploy WinRM via GPO — don't manually configure each host
  5. Use win_ping to verify before running playbooks — catches auth issues early
  6. Store passwords in Vault — never plain text in inventory

Conclusion

WinRM setup is the gateway to Windows automation with Ansible. Once configured with HTTPS and proper authentication, you have full access to Windows modules — win_feature, win_service, win_updates, win_package, and hundreds more. The initial setup investment pays off immediately when you can manage your entire Windows fleet as code.