AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible WinRM: Configure Windows Remote Management Connection (Complete Guide)

By Luca Berton · Published 2024-01-01 · Category: installation

How to configure WinRM for Ansible Windows automation. Set up WinRM HTTPS, authentication methods, pywinrm, troubleshoot connection errors.

Ansible + Windows: How It Works

Ansible manages Windows machines over WinRM (Windows Remote Management) instead of SSH. No agent is installed on Windows — Ansible sends PowerShell commands over WinRM and collects results.

┌──────────────┐    WinRM/HTTPS     ┌──────────────┐
│   Control     │ ──────────────────▶│   Windows     │
│   Node        │   (port 5986)      │   Server      │
│   (Linux)     │                    │               │
└──────────────┘                    └──────────────┘
   Ansible + pywinrm                  WinRM enabled

Requirements:

  • Control node: Linux or macOS (Ansible doesn't run on Windows as control node)
  • Managed nodes: Windows Server 2016+ or Windows 10+
  • Python package: pywinrm
  • WinRM enabled on Windows hosts

Enable WinRM on Windows

# Run as Administrator on each Windows host
# Download and run the Ansible WinRM setup script
$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 Setup

# Enable WinRM
Enable-PSRemoting -Force

# Set up HTTPS listener (recommended for production)
$cert = New-SelfSignedCertificate -DnsName $env:COMPUTERNAME -CertStoreLocation Cert:\LocalMachine\My
winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"$env:COMPUTERNAME`";CertificateThumbprint=`"$($cert.Thumbprint)`"}"

# Allow basic auth (for testing only)
winrm set winrm/config/service/auth '@{Basic="true"}'

# Allow unencrypted (NEVER in production)
# winrm set winrm/config/service '@{AllowUnencrypted="true"}'

# Set execution policy
Set-ExecutionPolicy RemoteSigned -Force

# Configure firewall
New-NetFirewallRule -Name "WinRM-HTTPS" -DisplayName "WinRM over HTTPS" -Enabled True -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow

Enable WinRM via Group Policy (Domain)

Computer Configuration → Administrative Templates → Windows Components → Windows Remote Management → WinRM Service
  → Allow remote server management through WinRM → Enabled
  → IPv4 filter: * (or specific subnets)

See also: AAP 2.6 Windows Automation: WinRM, PowerShell, and Active Directory Management

Inventory for Windows

# inventory/windows.yml
all:
  children:
    windows:
      children:
        web_servers:
          hosts:
            web-win-01:
              ansible_host: 10.0.1.10
            web-win-02:
              ansible_host: 10.0.1.11
        domain_controllers:
          hosts:
            dc-01:
              ansible_host: 10.0.0.1
            dc-02:
              ansible_host: 10.0.0.2
        sql_servers:
          hosts:
            sql-01:
              ansible_host: 10.0.2.10
      vars:
        ansible_connection: winrm
        ansible_winrm_transport: ntlm
        ansible_winrm_server_cert_validation: ignore
        ansible_port: 5986
        ansible_winrm_scheme: https
        ansible_user: "{{ vault_win_user }}"
        ansible_password: "{{ vault_win_password }}"

Kerberos Authentication (Domain-Joined)

# ansible.cfg
[defaults]
# Kerberos requires: pip install pywinrm[kerberos]

# inventory vars:
# ansible_winrm_transport: kerberos
# ansible_user: ansible@COMPANY.COM
# ansible_password: vault_password
# Install Kerberos support
pip install pywinrm[kerberos]

# Configure /etc/krb5.conf
# [realms]
#   COMPANY.COM = {
#     kdc = dc-01.company.com
#     admin_server = dc-01.company.com
#   }

Windows Features and Roles

---
- name: Configure Windows Server roles
  hosts: web_servers
  tasks:
    - name: Install IIS
      ansible.windows.win_feature:
        name:
          - Web-Server
          - Web-Asp-Net45
          - Web-Mgmt-Console
          - Web-Scripting-Tools
        state: present
        include_management_tools: true
      register: iis_install

    - name: Reboot if required
      ansible.windows.win_reboot:
        reboot_timeout: 600
      when: iis_install.reboot_required

    - name: Install .NET Framework 4.8
      ansible.windows.win_feature:
        name: NET-Framework-45-Features
        state: present
        include_sub_features: true

    - name: Remove unnecessary features
      ansible.windows.win_feature:
        name:
          - Telnet-Client
          - TFTP-Client
        state: absent

See also: Ansible Windows Server 2025 Automation: WinRM, PSRP, Active Directory, IIS, Hyper-V Complete Guide

IIS Web Server Management

---
- name: Configure IIS
  hosts: web_servers
  tasks:
    - name: Create application pool
      community.windows.win_iis_webapppool:
        name: MyAppPool
        state: started
        attributes:
          managedRuntimeVersion: v4.0
          managedPipelineMode: Integrated
          startMode: AlwaysRunning
          processModel.identityType: ApplicationPoolIdentity
          processModel.idleTimeout: "00:00:00"
          recycling.periodicRestart.time: "00:00:00"

    - name: Create website
      community.windows.win_iis_website:
        name: MyWebApp
        state: started
        port: 443
        ssl: true
        ip: "*"
        hostname: app.company.com
        physical_path: C:\inetpub\myapp
        application_pool: MyAppPool
        parameters: |
          logFile.directory:C:\logs\iis

    - name: Create virtual directory
      community.windows.win_iis_virtualdirectory:
        name: api
        site: MyWebApp
        physical_path: C:\inetpub\myapp\api
        state: present

    - name: Deploy application files
      ansible.windows.win_copy:
        src: app/
        dest: C:\inetpub\myapp\
        force: true
      notify: restart iis app pool

  handlers:
    - name: restart iis app pool
      community.windows.win_iis_webapppool:
        name: MyAppPool
        state: restarted

Software Installation

---
- name: Install software on Windows
  hosts: windows
  tasks:
    # Chocolatey packages
    - name: Install Chocolatey
      chocolatey.chocolatey.win_chocolatey:
        name: chocolatey
        state: present

    - name: Install common tools
      chocolatey.chocolatey.win_chocolatey:
        name: "{{ item }}"
        state: present
      loop:
        - googlechrome
        - 7zip
        - notepadplusplus
        - git
        - vscode
        - python3

    # MSI packages
    - name: Install application from MSI
      ansible.windows.win_package:
        path: https://download.company.com/app-2.0.msi
        product_id: '{12345678-1234-1234-1234-123456789012}'
        arguments: /quiet /norestart INSTALLDIR=C:\App
        state: present

    # EXE installers
    - name: Install agent
      ansible.windows.win_package:
        path: C:\temp\agent-setup.exe
        arguments: /S /v"/qn SERVERIP=10.0.0.100"
        expected_return_code: [0, 3010]
        state: present

See also: Ansible for Windows Server Enterprise Management: Active Directory, IIS, and Group Policy

Active Directory

ansible-galaxy collection install microsoft.ad
---
- name: Manage Active Directory
  hosts: domain_controllers
  tasks:
    - name: Create OU structure
      microsoft.ad.ou:
        name: "{{ item.name }}"
        path: "{{ item.path }}"
        state: present
        protected: true
      loop:
        - { name: "Company", path: "DC=company,DC=com" }
        - { name: "Users", path: "OU=Company,DC=company,DC=com" }
        - { name: "Groups", path: "OU=Company,DC=company,DC=com" }
        - { name: "Servers", path: "OU=Company,DC=company,DC=com" }
        - { name: "Workstations", path: "OU=Company,DC=company,DC=com" }

    - name: Create AD users
      microsoft.ad.user:
        name: "{{ item.username }}"
        firstname: "{{ item.first }}"
        surname: "{{ item.last }}"
        password: "{{ item.password }}"
        state: present
        path: "OU=Users,OU=Company,DC=company,DC=com"
        email: "{{ item.username }}@company.com"
        groups:
          add:
            - Domain Users
            - "{{ item.department }}"
        update_password: on_create
      loop: "{{ ad_users }}"
      no_log: true

    - name: Create security groups
      microsoft.ad.group:
        name: "{{ item }}"
        scope: global
        category: security
        path: "OU=Groups,OU=Company,DC=company,DC=com"
        state: present
      loop:
        - IT-Admins
        - Developers
        - Finance
        - HR

    - name: Join computer to domain
      microsoft.ad.membership:
        dns_domain_name: company.com
        domain_admin_user: "{{ vault_domain_admin }}"
        domain_admin_password: "{{ vault_domain_password }}"
        domain_ou_path: "OU=Servers,OU=Company,DC=company,DC=com"
        state: domain
        reboot: true

Windows Services

    - name: Configure services
      ansible.windows.win_service:
        name: "{{ item.name }}"
        start_mode: "{{ item.start_mode }}"
        state: "{{ item.state }}"
      loop:
        - { name: wuauserv, start_mode: auto, state: started }       # Windows Update
        - { name: W3SVC, start_mode: auto, state: started }          # IIS
        - { name: Spooler, start_mode: disabled, state: stopped }    # Print Spooler (security)
        - { name: RemoteRegistry, start_mode: disabled, state: stopped }

    - name: Create custom Windows service
      ansible.windows.win_service:
        name: MyAppService
        path: C:\App\myapp.exe --service
        display_name: "My Application Service"
        description: "Custom application service"
        start_mode: auto
        state: started
        username: .\svc_myapp
        password: "{{ vault_svc_password }}"
        dependencies:
          - LanmanServer
          - MSSQLSERVER

Registry Management

    - name: Configure registry settings
      ansible.windows.win_regedit:
        path: "{{ item.path }}"
        name: "{{ item.name }}"
        data: "{{ item.data }}"
        type: "{{ item.type }}"
      loop:
        # Disable SMBv1
        - path: HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters
          name: SMB1
          data: 0
          type: dword
        # Enable audit policy
        - path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Audit
          name: ProcessCreationIncludeCmdLine_Enabled
          data: 1
          type: dword
        # Set RDP timeout
        - path: HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services
          name: MaxIdleTime
          data: 900000
          type: dword

Windows Updates

---
- name: Patch Windows servers
  hosts: windows
  serial: 2    # Patch 2 at a time

  tasks:
    - name: Install security updates
      ansible.windows.win_updates:
        category_names:
          - SecurityUpdates
          - CriticalUpdates
        state: installed
        reboot: true
        reboot_timeout: 1800
      register: update_result

    - name: Report installed updates
      ansible.builtin.debug:
        msg: "Installed {{ update_result.installed_update_count }} updates on {{ inventory_hostname }}"
      when: update_result.installed_update_count > 0

PowerShell Execution

    - name: Run PowerShell script
      ansible.windows.win_powershell:
        script: |
          $services = Get-Service | Where-Object { $_.Status -eq 'Running' }
          $services | Select-Object Name, DisplayName, StartType |
            ConvertTo-Json
      register: ps_result

    - name: Run PowerShell with parameters
      ansible.windows.win_powershell:
        script: |
          param($Path, $Days)
          Get-ChildItem -Path $Path -Recurse |
            Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$Days) } |
            Remove-Item -Force
        parameters:
          Path: C:\Logs
          Days: 30

Security Hardening

---
- name: Windows security hardening
  hosts: windows
  tasks:
    - name: Set password policy
      ansible.windows.win_security_policy:
        section: System Access
        key: "{{ item.key }}"
        value: "{{ item.value }}"
      loop:
        - { key: MinimumPasswordLength, value: 14 }
        - { key: PasswordComplexity, value: 1 }
        - { key: MaximumPasswordAge, value: 90 }
        - { key: LockoutBadCount, value: 5 }

    - name: Disable unnecessary features
      ansible.windows.win_feature:
        name:
          - Telnet-Client
          - TFTP-Client
          - SMB1Protocol
        state: absent

    - name: Configure Windows Firewall
      community.windows.win_firewall_rule:
        name: "Block Telnet Inbound"
        localport: 23
        protocol: tcp
        direction: in
        action: block
        enabled: true
        state: present

    - name: Enable Windows Defender
      ansible.windows.win_powershell:
        script: |
          Set-MpPreference -DisableRealtimeMonitoring $false
          Set-MpPreference -MAPSReporting Advanced
          Update-MpSignature

FAQ

Can Ansible run on Windows as a control node?

No. The Ansible control node must be Linux or macOS. You can manage Windows from Linux using WinRM. If you only have Windows, use WSL2 (Windows Subsystem for Linux) to run Ansible.

WinRM or SSH for Windows?

WinRM is the standard and best-supported connection method. Windows 10+ includes OpenSSH Server, and Ansible can use ansible_connection: ssh with it. However, WinRM provides better integration with Windows authentication (NTLM, Kerberos, CredSSP). Use WinRM unless you have specific SSH requirements.

How do I handle reboots?

Use ansible.windows.win_reboot after tasks that require reboot (Windows Updates, feature installation). Set appropriate reboot_timeout for long updates. Check reboot_required in task results to conditionally reboot.

What about Windows containers?

Ansible manages Windows containers with community.docker collection over WinRM. Docker Desktop or Docker EE on Windows Server works with the same docker_container module.

Conclusion

Ansible automates Windows at scale — IIS, Active Directory, Windows features, software deployment, security hardening, and patch management. Configure WinRM once, and you manage Windows the same way you manage Linux: declarative YAML playbooks, idempotent operations, version-controlled infrastructure. Use Kerberos for domain environments, Chocolatey for package management, and serial for safe rolling updates.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home