How to Set Up Windows Remote Management for Ansible Control on Windows Server 2025

Ansible has become the de facto standard for agentless infrastructure automation, and while it was originally built around SSH for Linux hosts, it provides first-class Windows support through WinRM (Windows Remote Management). WinRM is Microsoft’s implementation of the WS-Management protocol, and when properly configured it gives an Ansible control node running on Linux full access to remote Windows hosts — including the ability to install features, manage services, copy files, set ACLs, and reboot machines in an idempotent, declarative way. Windows Server 2025 ships with WinRM available but not fully configured for Ansible use out of the box: listeners must be enabled, HTTPS certificates provisioned, CredSSP optionally enabled, and firewall rules adjusted. This guide covers the complete setup from the official Ansible configuration script through a working inventory and your first playbook tasks using the core win_* modules.

Prerequisites

  • Ansible control node running Linux (Ubuntu 22.04/24.04 recommended) with ansible and pywinrm installed
  • Windows Server 2025 target host(s) — domain-joined or workgroup
  • PowerShell 5.1 or later on the target (included in Windows Server 2025)
  • A local administrator account (or domain admin) on the Windows target
  • Network connectivity on TCP 5985 (WinRM HTTP) or 5986 (WinRM HTTPS) between control and target
  • On the control node: pip install pywinrm requests-credssp

Step 1 — Run the Ansible WinRM Configuration Script on Each Windows Target

Ansible maintains an official PowerShell script called ConfigureRemotingForAnsible.ps1 that automates the WinRM setup process. It creates an HTTPS listener with a self-signed certificate, enables the necessary WinRM service options, configures CredSSP, and sets firewall rules — all in one shot. Run this script on each Windows Server 2025 host you want to manage.

# On the Windows Server 2025 target — run as Administrator

# Option A: Download and execute directly (requires internet access from target)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

$ScriptUrl = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
$Script = (Invoke-WebRequest -Uri $ScriptUrl -UseBasicParsing).Content
Invoke-Expression $Script

# Option B: Save the script locally first (recommended for air-gapped or secure environments)
# Download it from your Ansible control node:
# wget https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
# Then copy to the Windows host and run:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
.ConfigureRemotingForAnsible.ps1

The script performs the following actions automatically:

  • Enables the WinRM service and sets it to start automatically
  • Creates an HTTPS WinRM listener on port 5986 with a self-signed certificate
  • Enables the Basic, Kerberos, Negotiate, and CredSSP authentication methods
  • Adds Windows Firewall rules for ports 5985 and 5986
  • Sets AllowUnencrypted to false (HTTPS only for sensitive environments)
# Verify WinRM listeners after running the script
winrm enumerate winrm/config/Listener

# Expected output should show two listeners:
# Listener[Source="GPO"] — HTTP on 5985
# Listener[Source="GPO"] — HTTPS on 5986 with CertificateThumbprint populated

# Confirm WinRM service is running
Get-Service WinRM | Select-Object Name, Status, StartType

# Check that firewall rules exist
Get-NetFirewallRule -DisplayName "Windows Remote Management*" |
    Select-Object DisplayName, Enabled, Direction, Profile

Step 2 — Manual WinRM Hardening (Optional but Recommended)

# Restrict WinRM access to a specific IP range (Ansible control node subnet)
# This replaces the any-source firewall rule created by the script
Set-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" `
    -RemoteAddress "10.10.20.0/24"    # replace with your Ansible control node subnet

# Enable PowerShell Remoting (required for some Ansible win_* modules)
Enable-PSRemoting -Force

# Increase MaxConcurrentOperationsPerUser if managing many hosts from one target
winrm set winrm/config/service @{MaxConcurrentOperationsPerUser="100"}

# Set the maximum memory per shell (increase for complex tasks)
winrm set winrm/config/winrs @{MaxMemoryPerShellMB="1024"}

Step 3 — Configure the Ansible Inventory for Windows Hosts

On the Linux Ansible control node, create an inventory file and group variable file that tells Ansible to use the WinRM connection plugin with your Windows-specific settings.

# On the Linux Ansible control node — create inventory file
# /etc/ansible/hosts or your project inventory/hosts.ini

[windows_servers]
ws2025-01.contoso.com
ws2025-02.contoso.com
ws2025-03.contoso.com

[windows_servers:vars]
ansible_user=Administrator
ansible_password=YourAdminPassword
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore
ansible_winrm_port=5986
ansible_winrm_scheme=https
# For domain-joined hosts with Kerberos authentication (more secure)
# Install krb5-user and python3-gssapi on the control node first
# apt install -y krb5-user python3-gssapi

[windows_domain:vars]
ansible_user=CONTOSOsvc_ansible
ansible_password=SecureServiceAccountPassword
ansible_connection=winrm
ansible_winrm_transport=kerberos
ansible_winrm_server_cert_validation=ignore
ansible_winrm_port=5986
ansible_winrm_kerberos_delegation=true

Step 4 — Test Connectivity with win_ping

# On the Linux Ansible control node
ansible windows_servers -m win_ping

# Expected output for each host:
# ws2025-01.contoso.com | SUCCESS => {
#     "changed": false,
#     "ping": "pong"
# }

# Run an ad-hoc command to verify PowerShell version
ansible windows_servers -m win_shell -a "Get-ComputerInfo | Select-Object WindowsProductName, OsVersion"

# Check disk space
ansible windows_servers -m win_shell -a "Get-PSDrive C | Select-Object Used, Free"

Step 5 — Essential win_* Modules for Windows Management

The Ansible ansible.windows collection provides a comprehensive suite of Windows-specific modules. Below are the most commonly used modules with practical examples.

# win_feature — install or remove Windows Server features
- name: Install IIS Web Server
  ansible.windows.win_feature:
    name: Web-Server
    state: present
    include_management_tools: yes

# win_package — install or uninstall software
- name: Install 7-Zip
  ansible.windows.win_package:
    path: "https://www.7-zip.org/a/7z2301-x64.msi"
    product_id: '{23170F69-40C1-2702-2301-000001000000}'
    state: present

# win_service — manage Windows services
- name: Ensure Windows Update service is running
  ansible.windows.win_service:
    name: wuauserv
    state: started
    start_mode: auto

# win_copy — copy files from control node to Windows target
- name: Copy configuration file
  ansible.windows.win_copy:
    src: files/app.config
    dest: C:AppsMyAppapp.config

# win_file — manage files and directories
- name: Create application directory
  ansible.windows.win_file:
    path: C:AppsMyAppLogs
    state: directory

# win_acl — set NTFS permissions
- name: Grant IIS AppPool read access to config
  ansible.windows.win_acl:
    path: C:AppsMyAppapp.config
    user: "IIS AppPool\DefaultAppPool"
    rights: Read
    type: allow
    state: present

# win_shell — run arbitrary PowerShell commands
- name: Get Windows activation status
  ansible.windows.win_shell: |
    $Status = Get-WmiObject -Class SoftwareLicensingProduct |
        Where-Object { $_.PartialProductKey } |
        Select-Object -ExpandProperty LicenseStatus
    return $Status
  register: activation_result

Step 6 — Handling Reboots in Playbooks

Many Windows configuration tasks — installing features, applying patches, joining a domain — require a reboot. Ansible’s win_reboot module handles this gracefully, waiting for the host to come back online before continuing the play.

---
- name: Install .NET Framework 4.8 and reboot if required
  hosts: windows_servers
  gather_facts: false

  tasks:
    - name: Install .NET Framework 4.8 Feature
      ansible.windows.win_feature:
        name: NET-Framework-45-Core
        state: present
      register: dotnet_result

    - name: Reboot if .NET installation requires it
      ansible.windows.win_reboot:
        reboot_timeout: 600
        pre_reboot_delay: 5
        post_reboot_delay: 30
        test_command: whoami
      when: dotnet_result.reboot_required

    - name: Install IIS after reboot
      ansible.windows.win_feature:
        name: Web-Server
        state: present
        include_management_tools: yes

    - name: Start and enable IIS service
      ansible.windows.win_service:
        name: W3SVC
        state: started
        start_mode: auto

    - name: Verify IIS is responding
      ansible.windows.win_shell: |
        $Response = Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing
        return $Response.StatusCode
      register: iis_check

    - name: Display IIS status code
      debug:
        msg: "IIS responded with HTTP {{ iis_check.stdout | trim }}"

Conclusion

Windows Server 2025 and Ansible make a powerful combination for infrastructure automation. By running the official ConfigureRemotingForAnsible.ps1 script on each target host, configuring a proper WinRM inventory on the control node, and mastering the core win_* modules, you can automate virtually every aspect of Windows Server management — from feature installation and service control to file placement, permission management, and coordinated reboots — using the same declarative playbook approach you already use for Linux hosts. For production environments, graduating from basic authentication to Kerberos or certificate-based mutual TLS authentication will give you a hardened, auditable automation channel that meets enterprise security requirements.