How to Install Ansible and Manage Windows Servers with Ansible on Windows Server 2022

Ansible is an agentless automation platform that uses SSH for Linux and WinRM for Windows to execute configuration management tasks, application deployments, and orchestrated workflows across infrastructure. Unlike agent-based tools, Windows servers do not require any Ansible software installed — only the WinRM service and the correct authentication configuration. This guide covers installing Ansible on a Linux control node, configuring Windows hosts, testing connectivity, and using the most common Ansible Windows modules.

Installing Ansible on the Linux Control Node

Ansible must be installed on a Linux (or macOS) control node. It cannot run its control plane natively on Windows, though WSL2 is a supported workaround for Windows development machines. For production use, a dedicated Linux server (Ubuntu 22.04 LTS or RHEL 9 are common choices) is recommended as the control node.

On Ubuntu 22.04, install Ansible using pip to ensure you get the latest version rather than the older package from the distribution repositories:

sudo apt update
sudo apt install -y python3 python3-pip python3-venv

# Create a virtual environment for Ansible
python3 -m venv /opt/ansible-venv
source /opt/ansible-venv/bin/activate

# Install Ansible and the WinRM library
pip install ansible pywinrm requests-kerberos requests-credssp

Verify the installation:

ansible --version

The pywinrm library is the Python package that Ansible uses to communicate with Windows hosts over WinRM. It must be installed in the same Python environment as Ansible. The requests-kerberos and requests-credssp packages are needed for Kerberos and CredSSP authentication modes respectively.

Configuring Windows Hosts for Ansible (WinRM Setup)

Windows Server 2022 has WinRM installed by default, but it is not configured for remote access. Microsoft provides a PowerShell script (ConfigureRemotingForAnsible.ps1) that automates the WinRM configuration. Download and run it on each Windows host you want to manage:

# Run this on the Windows Server 2022 host (elevated PowerShell)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
$file = "$env:tempConfigureRemotingForAnsible.ps1"
(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)
powershell.exe -ExecutionPolicy ByPass -File $file

This script performs several configuration steps: starts the WinRM service, creates HTTPS and HTTP listeners, configures the Windows Firewall to allow WinRM connections, and sets the WinRM service to start automatically. Verify WinRM is active:

winrm enumerate winrm/config/listener

You should see listeners for both HTTP (port 5985) and HTTPS (port 5986). For production environments, use HTTPS only and disable the HTTP listener after confirming HTTPS connectivity works.

Creating the Windows Inventory File

Ansible uses an inventory file to define the hosts and groups it manages. Create an inventory file on the control node:

# /etc/ansible/inventories/windows.ini

[windows]
ws2022-01 ansible_host=192.168.1.10
ws2022-02 ansible_host=192.168.1.11
ws2022-03 ansible_host=192.168.1.12

[windows:vars]
ansible_user=Administrator
ansible_password=YourAdminPassword
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore
ansible_port=5986

For larger environments, use a YAML-format inventory and store credentials in Ansible Vault rather than plaintext. A YAML inventory for Windows hosts:

# inventory/windows.yml
all:
  children:
    windows:
      hosts:
        ws2022-01:
          ansible_host: 192.168.1.10
        ws2022-02:
          ansible_host: 192.168.1.11
      vars:
        ansible_user: ansible_svc
        ansible_password: "{{ vault_windows_password }}"
        ansible_connection: winrm
        ansible_winrm_transport: ntlm
        ansible_winrm_server_cert_validation: ignore
        ansible_port: 5986

Configuring ansible.cfg

The ansible.cfg file controls Ansible’s behavior for the current project. Create it in the same directory as your playbooks:

[defaults]
inventory = ./inventory/windows.yml
remote_user = ansible_svc
host_key_checking = False
timeout = 30
forks = 10
log_path = /var/log/ansible.log

[winrm]
operation_timeout_sec = 60
read_timeout_sec = 70

The forks setting controls how many hosts Ansible manages in parallel. A value of 10 means Ansible will execute tasks on up to 10 Windows hosts simultaneously. Increase this for larger environments, but be mindful of the network and target host load.

Testing Connectivity with win_ping

Before running any playbooks, verify basic connectivity to your Windows hosts using the win_ping module. This module sends a test message to the Windows host over WinRM and expects a response — it is the Windows equivalent of the Linux ping module:

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

Successful output looks like:

ws2022-01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
ws2022-02 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

If you get connection refused or authentication errors, check that WinRM is running, the firewall rule is in place, and the credentials are correct. Use the -vvv flag for verbose output that shows the exact WinRM negotiation:

ansible windows -i inventory/windows.yml -m win_ping -vvv

Ansible Playbook Basics for Windows

Playbooks are YAML files that define the automation tasks to perform. A basic Windows playbook might configure IIS, copy web files, and start the web service:

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

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

    - name: Copy application files
      ansible.windows.win_copy:
        src: ./webapp/
        dest: C:inetpubwwwrootmyapp

    - name: Create firewall rule for HTTP
      community.windows.win_firewall_rule:
        name: "Allow HTTP 80"
        localport: 80
        action: allow
        direction: in
        protocol: tcp
        state: present
        enabled: true

Run the playbook with:

ansible-playbook -i inventory/windows.yml playbooks/configure_iis.yml

Common Windows Ansible Modules

The ansible.windows collection provides the core Windows modules. Install or update it with:

ansible-galaxy collection install ansible.windows community.windows

The win_command module runs a Windows command without shell processing. Use it for executables that do not require shell features:

- name: Run a program
  ansible.windows.win_command: C:Toolsmyapp.exe --install --silent

The win_shell module runs commands through PowerShell or cmd, enabling shell features like pipes, redirects, and variable expansion:

- name: Get disk usage
  ansible.windows.win_shell: Get-PSDrive C | Select-Object Used, Free
  register: disk_info

- name: Show disk info
  debug:
    var: disk_info.stdout

The win_copy module copies files and directories from the control node to Windows hosts:

- name: Copy configuration file
  ansible.windows.win_copy:
    src: configs/app.config
    dest: C:Appapp.config
    backup: true

The win_file module manages files and directories:

- name: Create directory
  ansible.windows.win_file:
    path: C:LogsMyApp
    state: directory

- name: Delete old log files
  ansible.windows.win_file:
    path: C:LogsMyAppold
    state: absent

The win_service module manages Windows services — starting, stopping, enabling, and disabling them:

- name: Ensure SQL Server is running
  ansible.windows.win_service:
    name: MSSQLSERVER
    state: started
    start_mode: auto

- name: Disable Windows Search
  ansible.windows.win_service:
    name: WSearch
    state: stopped
    start_mode: disabled

The win_feature module installs or removes Windows Server roles and features:

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

- name: Install Hyper-V management tools
  ansible.windows.win_feature:
    name:
      - Hyper-V-Tools
      - Hyper-V-PowerShell
    state: present

The win_package module installs or uninstalls Windows software packages (MSI, EXE installers):

- name: Install 7-Zip
  ansible.windows.win_package:
    path: C:Installers7z2301-x64.msi
    state: present
    arguments: /quiet /norestart

- name: Install from URL
  ansible.windows.win_package:
    path: https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.6.5/npp.8.6.5.Installer.x64.exe
    state: present
    arguments: /S

Running Ansible Over HTTPS WinRM

For production environments, use HTTPS WinRM (port 5986) to encrypt traffic between the Ansible control node and Windows hosts. Verify the HTTPS listener is active on the Windows host:

winrm enumerate winrm/config/listener

In your inventory, set ansible_port to 5986 and ansible_winrm_scheme to https. If using self-signed certificates (as created by ConfigureRemotingForAnsible.ps1), set ansible_winrm_server_cert_validation to ignore for initial testing, then replace with a proper certificate from your internal CA for production:

ansible_port: 5986
ansible_winrm_scheme: https
ansible_winrm_server_cert_validation: ignore   # Change to 'validate' with a trusted cert
ansible_winrm_transport: ntlm

To use a trusted certificate, export your internal CA certificate as a PEM file and reference it in your inventory:

ansible_winrm_ca_trust_path: /etc/ansible/certs/internal-ca.pem
ansible_winrm_server_cert_validation: validate

With HTTPS and certificate validation enabled, all Ansible communications to your Windows Server 2022 fleet are encrypted and authenticated, meeting the security requirements of most enterprise environments.