Automation

Automate Windows Server Administration with Ansible

Ansible can manage Windows servers just as effectively as Linux. Instead of SSH, Ansible communicates with Windows hosts over WinRM (Windows Remote Management) – a built-in Windows protocol that accepts remote commands. This opens the door to automating software installs, service management, Active Directory tasks, Windows Updates, and full server provisioning from a single Linux control node.

Original content from computingforgeeks.com - post 72415

This guide walks through configuring WinRM on Windows Server, setting up Ansible inventory for Windows hosts, and running practical playbooks that cover the most common Windows administration tasks with Ansible. Every example uses tested Ansible Windows modules that work on Windows Server 2019, 2022, and 2025.

Prerequisites

Before starting, make sure you have these in place:

  • A Linux control node (Ubuntu 24.04, Rocky Linux 10, or any supported distro) with Ansible 2.15+ installed
  • One or more Windows Server hosts (2019, 2022, or 2025) with administrator access
  • Network connectivity between the control node and Windows hosts on port 5986 (HTTPS WinRM)
  • Python pywinrm library installed on the control node
  • PowerShell 5.1 or later on the Windows hosts (included by default in Server 2019+)

Install the required Python library and Ansible Windows collection on your control node:

pip install pywinrm
ansible-galaxy collection install ansible.windows

Step 1: Configure WinRM on Windows Server

WinRM must be enabled and configured on every Windows host that Ansible will manage. Open an elevated PowerShell prompt on the Windows server and run the following script. This enables WinRM over HTTPS with a self-signed certificate, creates a firewall rule for port 5986, and sets basic authentication.

$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

This is the official Ansible remoting script that handles all the WinRM configuration. After it finishes, verify WinRM is listening on HTTPS:

winrm enumerate winrm/config/Listener

You should see a listener entry with Transport = HTTPS and Port = 5986:

Listener
    Address = *
    Transport = HTTPS
    Port = 5986
    Hostname
    Enabled = true
    URLPrefix = wsman
    CertificateThumbprint = 8A2B4C6D8E0F...

If you prefer manual configuration instead of the script, run these PowerShell commands individually:

Enable-PSRemoting -Force
Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $true
Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $false

$cert = New-SelfSignedCertificate -DnsName $(hostname) -CertStoreLocation Cert:\LocalMachine\My
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * -CertificateThumbprint $cert.Thumbprint -Force

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

Step 2: Configure Ansible Inventory for Windows Hosts

Ansible needs specific connection variables to talk to Windows hosts. Create an inventory file that defines your Windows servers and the WinRM connection parameters.

Create the inventory directory and hosts file:

mkdir -p ~/ansible-windows/inventory

Open the inventory file:

vi ~/ansible-windows/inventory/hosts.yml

Add the following configuration, replacing the IP addresses and credentials with your actual values:

all:
  children:
    windows:
      hosts:
        win-server01:
          ansible_host: 192.168.1.50
        win-server02:
          ansible_host: 192.168.1.51
      vars:
        ansible_user: Administrator
        ansible_password: "YourSecurePassword123!"
        ansible_connection: winrm
        ansible_winrm_transport: basic
        ansible_winrm_server_cert_validation: ignore
        ansible_port: 5986
        ansible_winrm_scheme: https

For production environments, store the password in Ansible Vault instead of plain text. Use Kerberos or CredSSP transport instead of basic auth when the hosts are domain-joined.

Step 3: Test Ansible Connection to Windows with win_ping

Before running any playbooks, confirm that Ansible can reach the Windows hosts. The win_ping module is the Windows equivalent of the standard ping module – it connects over WinRM and returns a success response.

ansible windows -i ~/ansible-windows/inventory/hosts.yml -m ansible.windows.win_ping

A successful connection returns a pong response for each host:

win-server01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
win-server02 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

If the connection fails, check these common issues: WinRM service not running on the Windows host, firewall blocking port 5986, wrong credentials, or certificate validation problems. Run with -vvvv for detailed debug output.

Step 4: Install Software with win_chocolatey

Chocolatey is the standard package manager for Windows, and the win_chocolatey module lets you install, update, and remove software packages. Ansible installs Chocolatey automatically on the first run if it is not present.

Create a playbook to install common tools:

vi ~/ansible-windows/install-software.yml

Add the following playbook content:

---
- name: Install software on Windows servers
  hosts: windows
  tasks:
    - name: Install Chocolatey packages
      ansible.windows.win_chocolatey:
        name: "{{ item }}"
        state: present
      loop:
        - googlechrome
        - notepadplusplus
        - 7zip
        - git
        - vscode

    - name: Install a specific version of Python
      ansible.windows.win_chocolatey:
        name: python3
        version: "3.12.4"
        state: present

    - name: Update all installed Chocolatey packages
      ansible.windows.win_chocolatey:
        name: all
        state: latest

Run the playbook:

ansible-playbook -i ~/ansible-windows/inventory/hosts.yml ~/ansible-windows/install-software.yml

Ansible reports each package installation with changed status. Packages already installed show ok with no changes made.

Step 5: Manage Windows Services with Ansible

The win_service module controls Windows services – start, stop, restart, and configure startup type. This is essential for managing IIS, SQL Server, or any custom service across multiple servers.

Create the service management playbook:

vi ~/ansible-windows/manage-services.yml

Add the following tasks:

---
- name: Manage Windows services
  hosts: windows
  tasks:
    - name: Ensure Windows Update service is running
      ansible.windows.win_service:
        name: wuauserv
        start_mode: auto
        state: started

    - name: Stop and disable Print Spooler on servers
      ansible.windows.win_service:
        name: Spooler
        start_mode: disabled
        state: stopped

    - name: Restart IIS service
      ansible.windows.win_service:
        name: W3SVC
        state: restarted
      when: "'webservers' in group_names"

    - name: Get service info
      ansible.windows.win_service_info:
        name: wuauserv
      register: svc_info

    - name: Display service status
      ansible.builtin.debug:
        msg: "Windows Update service is {{ svc_info.services[0].state }}"

The win_service_info module is useful for gathering service state before taking action. Combine it with conditionals to build playbooks that only restart services when configuration files change.

Step 6: Manage Windows Features and Roles

Windows Server roles and features – like IIS, DNS Server, DHCP, or Hyper-V – are managed with the win_feature module. This replaces the manual Server Manager workflow with repeatable automation.

Create the features playbook:

vi ~/ansible-windows/manage-features.yml

Add tasks to install and remove server features:

---
- name: Manage Windows Server features and roles
  hosts: windows
  tasks:
    - name: Install IIS web server with management tools
      ansible.windows.win_feature:
        name:
          - Web-Server
          - Web-Mgmt-Tools
          - Web-Mgmt-Console
        state: present
        include_sub_features: true
        include_management_tools: true
      register: iis_install

    - name: Reboot if IIS installation requires it
      ansible.windows.win_reboot:
        reboot_timeout: 300
      when: iis_install.reboot_required

    - name: Install DNS Server role
      ansible.windows.win_feature:
        name: DNS
        state: present
        include_management_tools: true

    - name: Install Telnet Client for diagnostics
      ansible.windows.win_feature:
        name: Telnet-Client
        state: present

    - name: Remove Windows Fax and Scan
      ansible.windows.win_feature:
        name: Fax
        state: absent

The win_reboot module handles graceful reboots when a feature installation requires it. Ansible waits for the host to come back online before continuing with the next task.

Step 7: Manage Files and Registry on Windows

File operations and registry edits are two of the most common admin tasks on Windows. Ansible provides win_copy, win_template, and win_regedit modules to handle these declaratively.

Create the file and registry management playbook:

vi ~/ansible-windows/files-registry.yml

Add the following tasks:

---
- name: Manage files and registry on Windows
  hosts: windows
  tasks:
    - name: Create a directory structure
      ansible.windows.win_file:
        path: C:\Apps\Configs
        state: directory

    - name: Copy configuration file to Windows host
      ansible.windows.win_copy:
        src: files/app-config.xml
        dest: C:\Apps\Configs\app-config.xml

    - name: Download a file from URL
      ansible.windows.win_get_url:
        url: https://download.sysinternals.com/files/SysinternalsSuite.zip
        dest: C:\Temp\SysinternalsSuite.zip

    - name: Set registry value to disable Server Manager at logon
      ansible.windows.win_regedit:
        path: HKLM:\SOFTWARE\Microsoft\ServerManager
        name: DoNotOpenServerManagerAtLogon
        data: 1
        type: dword

    - name: Configure RDP security settings via registry
      ansible.windows.win_regedit:
        path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp
        name: SecurityLayer
        data: 2
        type: dword

    - name: Set environment variable system-wide
      ansible.windows.win_environment:
        name: APP_ENV
        value: production
        level: machine
        state: present

The win_regedit module is idempotent – it only makes changes when the current registry value differs from the desired state. Always use the full registry path format with the hive prefix (HKLM, HKCU, etc.).

Step 8: Manage Active Directory Users with Ansible

For domain-joined environments, Ansible can create and manage Active Directory users, groups, and organizational units using the microsoft.ad collection. Install it first on your control node:

ansible-galaxy collection install microsoft.ad

Create the AD management playbook:

vi ~/ansible-windows/manage-ad-users.yml

Add tasks to manage users, groups, and OUs:

---
- name: Manage Active Directory users and groups
  hosts: windows
  tasks:
    - name: Create an Organizational Unit
      microsoft.ad.ou:
        name: DevOps
        path: "DC=example,DC=com"
        state: present
        description: "DevOps team accounts"

    - name: Create AD user
      microsoft.ad.user:
        name: jdoe
        firstname: John
        surname: Doe
        password: "Str0ngP@ssword!"
        state: present
        path: "OU=DevOps,DC=example,DC=com"
        email: [email protected]
        enabled: true
        password_never_expires: false
        user_cannot_change_password: false
        update_password: on_create

    - name: Create AD security group
      microsoft.ad.group:
        name: DevOps-Admins
        scope: global
        path: "OU=DevOps,DC=example,DC=com"
        state: present
        description: "DevOps administrators group"

    - name: Add user to group
      microsoft.ad.group:
        name: DevOps-Admins
        members:
          add:
            - jdoe
        state: present

    - name: Disable a terminated user account
      microsoft.ad.user:
        name: former_employee
        state: present
        enabled: false

The update_password: on_create setting prevents Ansible from resetting the password on subsequent runs. This is important for user accounts where the person has already changed their password.

Step 9: Automate Windows Updates with Ansible

Patching Windows servers manually is time-consuming and error-prone. The win_updates module downloads and installs Windows updates with full control over categories, reboot behavior, and blacklisted KBs.

Create the Windows Update playbook:

vi ~/ansible-windows/windows-updates.yml

Add the update tasks:

---
- name: Apply Windows Updates
  hosts: windows
  tasks:
    - name: Install all critical and security updates
      ansible.windows.win_updates:
        category_names:
          - CriticalUpdates
          - SecurityUpdates
        state: installed
        reboot: true
        reboot_timeout: 600
      register: update_result

    - name: Display update results
      ansible.builtin.debug:
        msg: >
          Installed {{ update_result.installed_update_count }} updates.
          Reboot required: {{ update_result.reboot_required }}

    - name: Install all updates except specific KBs
      ansible.windows.win_updates:
        category_names:
          - CriticalUpdates
          - SecurityUpdates
          - UpdateRollups
        reject_list:
          - KB5001234
          - KB5005678
        state: installed
        reboot: true

    - name: Search for available updates without installing
      ansible.windows.win_updates:
        category_names:
          - CriticalUpdates
          - SecurityUpdates
        state: searched
      register: available_updates

    - name: Show pending updates
      ansible.builtin.debug:
        msg: "{{ available_updates.found_update_count }} updates available"

The state: searched option is valuable for audit playbooks – it checks what updates are available without installing anything. Use reject_list to exclude known problematic KBs from automated patching.

Step 10: Complete Windows Server Setup Playbook

Combining everything into a single playbook gives you a repeatable server provisioning workflow. This playbook takes a fresh Windows Server and configures it to your standard – hostname, features, software, security settings, and monitoring agent.

Create the master setup playbook:

vi ~/ansible-windows/full-server-setup.yml

Add the complete server provisioning tasks:

---
- name: Complete Windows Server setup
  hosts: windows
  vars:
    server_timezone: "Eastern Standard Time"
    ntp_server: "time.windows.com"
    admin_users:
      - name: sysadmin
        firstname: System
        surname: Admin
        password: "S3cureP@ss!"
    required_features:
      - Telnet-Client
      - RSAT-AD-Tools
      - Web-Server
    required_packages:
      - googlechrome
      - notepadplusplus
      - 7zip
      - git

  tasks:
    - name: Set timezone
      community.windows.win_timezone:
        timezone: "{{ server_timezone }}"

    - name: Set NTP server
      ansible.windows.win_shell: |
        w32tm /config /manualpeerlist:"{{ ntp_server }}" /syncfromflags:manual /reliable:YES /update
        Restart-Service w32time
        w32tm /resync

    - name: Disable unnecessary services
      ansible.windows.win_service:
        name: "{{ item }}"
        start_mode: disabled
        state: stopped
      loop:
        - Spooler
        - Fax
        - XblGameSave
        - XblAuthManager
      ignore_errors: true

    - name: Install required Windows features
      ansible.windows.win_feature:
        name: "{{ required_features }}"
        state: present
        include_management_tools: true
      register: features_result

    - name: Install required software via Chocolatey
      ansible.windows.win_chocolatey:
        name: "{{ item }}"
        state: present
      loop: "{{ required_packages }}"

    - name: Configure Windows Firewall - allow RDP
      community.windows.win_firewall_rule:
        name: "Allow RDP"
        localport: 3389
        action: allow
        direction: in
        protocol: tcp
        state: present
        enabled: true

    - name: Configure Windows Firewall - allow WinRM HTTPS
      community.windows.win_firewall_rule:
        name: "Allow WinRM HTTPS"
        localport: 5986
        action: allow
        direction: in
        protocol: tcp
        state: present
        enabled: true

    - name: Disable Server Manager at logon
      ansible.windows.win_regedit:
        path: HKLM:\SOFTWARE\Microsoft\ServerManager
        name: DoNotOpenServerManagerAtLogon
        data: 1
        type: dword

    - name: Enable RDP
      ansible.windows.win_regedit:
        path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server
        name: fDenyTSConnections
        data: 0
        type: dword

    - name: Set power plan to High Performance
      ansible.windows.win_shell: powercfg /setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c

    - name: Install all security updates
      ansible.windows.win_updates:
        category_names:
          - CriticalUpdates
          - SecurityUpdates
        state: installed
        reboot: true
        reboot_timeout: 900
      register: updates

    - name: Final reboot if features require it
      ansible.windows.win_reboot:
        reboot_timeout: 300
      when: features_result.reboot_required

    - name: Verify server configuration
      ansible.windows.win_shell: |
        $info = @{
          Hostname = hostname
          OS = (Get-CimInstance Win32_OperatingSystem).Caption
          Uptime = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
          Updates = "{{ updates.installed_update_count | default(0) }} installed"
        }
        $info | Format-Table -AutoSize
      register: verify

    - name: Display server status
      ansible.builtin.debug:
        var: verify.stdout_lines

Run the full setup playbook against new servers:

ansible-playbook -i ~/ansible-windows/inventory/hosts.yml ~/ansible-windows/full-server-setup.yml

This playbook is idempotent – running it again on an already-configured server makes zero changes. That makes it safe for both initial provisioning and ongoing compliance checks.

Reference: Common Ansible Windows Modules

The Ansible Windows documentation covers the full module list. Here are the modules you will use most often for Windows server administration:

ModulePurpose
win_pingTest WinRM connectivity to Windows hosts
win_copyCopy files from control node to Windows host
win_fileCreate, delete, and manage files and directories
win_templateDeploy Jinja2 templates to Windows hosts
win_serviceManage Windows services (start, stop, configure)
win_service_infoGather information about Windows services
win_featureInstall or remove Windows Server roles and features
win_chocolateyInstall, update, and remove Chocolatey packages
win_regeditAdd, modify, and remove registry keys and values
win_updatesSearch for and install Windows Updates
win_rebootReboot Windows host and wait for it to come back
win_shellExecute PowerShell commands on Windows hosts
win_commandExecute commands without PowerShell shell processing
win_get_urlDownload files from HTTP/HTTPS/FTP URLs
win_environmentManage environment variables at user or system level
win_scheduled_taskCreate and manage Windows Scheduled Tasks
win_userManage local Windows user accounts
win_groupManage local Windows groups
win_firewall_ruleCreate and manage Windows Firewall rules
win_dscRun PowerShell DSC resources on Windows hosts

Conclusion

Ansible gives you a single automation platform for both Linux and Windows infrastructure. With WinRM configured and the right inventory variables set, every Windows administration task – from user management to patching to full server provisioning – becomes a repeatable playbook.

For production deployments, use Kerberos authentication instead of basic auth, store credentials in Ansible Vault, and set up Ansible AWX for centralized job scheduling and audit logging. Keep your playbooks in Git and test changes in a staging environment before rolling out to production servers.

Related Articles

Email How To Configure Microsoft Outlook for Zimbra Containers Deploy Kubernetes with Ansible and Kubespray Ansible Solve “[WARNING]: sftp transfer mechanism failed” in Ansible Automation Install and Configure Foreman 3.x on Debian 11

Leave a Comment

Press ESC to close