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
ansibleandpywinrminstalled - 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, andCredSSPauthentication methods - Adds Windows Firewall rules for ports 5985 and 5986
- Sets
AllowUnencryptedto 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.