Ops Notes

Ansible Playbook Windows Server Setup Tutorial 2026: WinRM, Kerberos, and Production-Grade Automation

Cloud & DevOps Visualization

Why Bother with Ansible for Windows in 2026?

Let’s be honest: Ansible + Windows has always felt like a second-class citizen. On Linux, you SSH in and you’re done. On Windows, you’re dealing with WinRM, PowerShell Remoting, Kerberos tickets, and a dozen authentication protocols that sound like they were designed by a committee in 2005.

But here’s the reality check. My team took on a client last year running their core business on Windows Server 2022, with a bunch of 2016 stragglers dragging their feet. The boss said “we need automation,” and the knee-jerk reaction was PowerShell scripts. The problem? Scripts scattered across fifty servers, no version control, no one knowing who changed what.

That’s where Ansible earns its keep. Not because it’s better than PowerShell—it’s not, for pure Windows tasks. But because it gives you a declarative framework. You tell it the end state, and it figures out the path. That’s powerful when you’re managing a fleet, not just one box.

Over on Reddit’s r/homelab, someone nailed the sentiment: “What started off as a mini PC running Home Assistant and Pi-hole has somehow escalated into a full-blown VLAN-separated network and self-hosting project. I currently use this setup for messing about with Windows Servers — Domain Controllers, SQL Servers, and cyber security type stuff.” Sound familiar? That snowball effect is exactly why you need automation before your homelab or prod environment becomes unmanageable.

WinRM Setup: The Part Everyone Gets Wrong

Before you write a single playbook, you need to get the communication channel working. WinRM is Microsoft’s remote management protocol, and Ansible talks to Windows through it. Default configuration? Locked down tighter than a drum. Secure, yes. Annoying as hell, also yes.

Quick Enablement (Dev/Test Only)

Run this PowerShell as Administrator on your Windows Server:

Enable-PSRemoting -Force
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force
Restart-Service WinRM

Warning: Setting TrustedHosts to * in production is asking for trouble. We’ll cover Kerberos and HTTPS hardening later.

Test the Connection from Your Control Node

ansible windows -m win_ping -i inventory.ini

If you get pong, you’re in business. If you get unreachable, 99% of the time it’s a WinRM config issue.

The Three Most Common Faceplants

  1. WinRM service not running: Check with Get-Service WinRM.
  2. Firewall blocking ports 5985/5986: New-NetFirewallRule -DisplayName "WinRM HTTP" -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow.
  3. Authentication method disabled: Set-Item WSMan:\localhost\Service\Auth\Basic -Value $true (dev only, please).

Production-Grade WinRM: Stop Running Naked

The quick-and-dirty config above is fine for your laptop. For production, you need HTTPS + Kerberos. Period.

Setting Up HTTPS WinRM

$cert = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My -DnsName "win-srv-01.example.com" -FriendlyName "WinRM HTTPS"
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * -CertificateThumbprint $cert.Thumbprint -Force
Restart-Service WinRM

Then in your Ansible inventory:

[windows]
win-srv-01.example.com ansible_user=admin@EXAMPLE.COM ansible_password=your_pass ansible_connection=winrm ansible_winrm_server_cert_validation=ignore ansible_winrm_transport=kerberos ansible_port=5986

Key point: ansible_winrm_server_cert_validation=ignore is a necessary evil for self-signed certs. If you have an enterprise CA, get a proper certificate signed and switch to validate.

Kerberos vs. Basic: No Contest

Basic authentication sends credentials in plain text. Even over HTTPS, it’s vulnerable to man-in-the-middle attacks if someone’s got a foothold on the network. Kerberos handles ticket renewal automatically and is the gold standard in domain environments. It’s more work to set up—you need a Kerberos client on your control node and proper DNS resolution—but it’s worth it.

Your First Playbook: IIS on Windows Server

Enough theory. Here’s a playbook that installs IIS, starts the service, and deploys a static page.

---
- name: Setup IIS on Windows Server 2026
  hosts: windows
  gather_facts: yes
  tasks:
    - name: Install IIS Web-Server role
      ansible.windows.win_feature:
        name: Web-Server
        state: present
        include_management_tools: yes

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

    - name: Create website directory
      ansible.windows.win_file:
        path: C:\inetpub\wwwroot\myapp
        state: directory

    - name: Deploy index.html
      ansible.windows.win_copy:
        src: files/index.html
        dest: C:\inetpub\wwwroot\myapp\index.html

    - name: Create IIS website
      ansible.windows.win_iis_website:
        name: MyApp
        state: started
        port: 80
        ip: '*'
        physical_path: C:\inetpub\wwwroot\myapp

Run it:

ansible-playbook -i inventory.ini setup-iis.yml

If everything works, hit http://win-srv-01 in your browser. If not, the WinRM debugging gauntlet awaits.

Module Selection: Don’t Write Linux Playbooks for Windows

This is the #1 mistake I see from newcomers. ansible.builtin.copy works great on Linux. On Windows, the path separators are different, the permission model is different, and the module doesn’t handle either correctly.

ScenarioLinux ModuleWindows Module
Install softwareapt / yumansible.windows.win_feature (roles/features)
ansible.windows.win_chocolatey (third-party packages)
File operationsansible.builtin.copyansible.windows.win_copy
Service managementansible.builtin.systemdansible.windows.win_service
RegistryN/Aansible.windows.win_regedit
Execute commandansible.builtin.shell / commandansible.windows.win_shell / win_command

Hard lesson learned: Don’t use win_shell to run complex PowerShell scripts unless there’s absolutely no module for what you need. Modules give you idempotency, proper error handling, and state tracking. win_shell gives you a shell script that may or may not work twice.

Advanced Techniques: Facts, Rolling Updates, and Vault

Gathering Windows Facts with the setup Module

ansible windows -m setup

This returns everything—OS version, memory, disk, network config, installed patches. Use it for conditional logic:

- name: Reboot only if required by pending updates
  ansible.windows.win_reboot:
  when: ansible_os_family == "Windows" and ansible_architecture == "64-bit"

Rolling Updates: Zero-Downtime Deployments

Say you have three Windows Servers behind a load balancer. You can’t take them all down at once. Use serial to roll through them one at a time:

- name: Rolling update of Windows web servers
  hosts: windows_web
  serial: 1
  tasks:
    - name: Take server out of load balancer pool
      ansible.windows.win_shell: |
        Invoke-RestMethod -Uri "http://loadbalancer/api/drain?server={{ inventory_hostname }}"
      delegate_to: localhost

    - name: Update application files
      ansible.windows.win_copy:
        src: app_v2/
        dest: C:\MyApp\

    - name: Restart application service
      ansible.windows.win_service:
        name: MyAppService
        state: restarted

    - name: Add server back to load balancer pool
      ansible.windows.win_shell: |
        Invoke-RestMethod -Uri "http://loadbalancer/api/enable?server={{ inventory_hostname }}"
      delegate_to: localhost

Managing Secrets with Ansible Vault

Never put passwords in plaintext in your inventory. Use ansible-vault:

ansible-vault create group_vars/windows/vault.yml

Inside, store your credentials:

vault_ansible_password: "SuperSecretP@ssw0rd!"

Then reference it in your inventory:

[windows:vars]
ansible_password="{{ vault_ansible_password }}"

Run with --ask-vault-pass or set up a vault password file.

Best Practices Quick Reference

PracticeRecommendationWhy
AuthenticationKerberos (domain) or CredSSP (non-domain)Secure, supports double-hop
TransportHTTPS (port 5986)Encrypted, prevents MITM
CertificateEnterprise CA-signedAvoids ignore validation
Module selectionPrefer ansible.windows.* modulesIdempotency, better error handling
Fact gatheringEnable gather_facts: yesEnables conditional logic
Secret managementAnsible Vault or CyberArk/Azure Key VaultNever plaintext passwords
TestingDocker or Vagrant with Windows containersFast feedback on playbook syntax
Error handlingUse ignore_errors and failed_whenGranular control over execution

What the Community is Saying in 2026

Scrolling through Reddit and X, the sentiment on Ansible + Windows is split right down the middle.

The pro camp argues that Ansible’s declarative model is more intuitive than PowerShell DSC, and the learning curve is gentler. When your team has both Linux and Windows engineers, a single toolchain reduces fragmentation.

The con camp has a point: WinRM is brittle. It drops connections, debugging is painful, and the error messages are often useless. One r/devops user vented: “I spent more time debugging WinRM than actually writing the playbook. It’s 2026, why is this still a thing?”

My take: Both sides are right. WinRM is not SSH. But if you nail the HTTPS + Kerberos setup, it’s stable enough for daily use. The real pain point is the module ecosystem—it’s nowhere near as rich as Linux. You’ll struggle to find a well-maintained community module for win_nginx or win_docker. More often than not, you’ll fall back to win_shell to call PowerShell directly, which brings you back to the “scattered scripts” problem Ansible was supposed to solve.

Final Thoughts

Ansible + Windows Server in 2026 isn’t a “sweet spot” combination—it’s a pragmatic compromise. If you’re dealing with Windows legacy systems (AD, Exchange, SQL Server), Ansible is probably the least bad option for unified automation.

My advice: start small. Use Ansible for patch management or service restarts—low-risk operations that build your team’s confidence. Once you’ve got the WinRM kinks worked out, expand to application deployment and configuration management. Don’t try to automate your entire datacenter in week one. You’ll drown in WinRM error messages.

And if you get stuck, hit up r/ansible or r/homelab on Reddit. Chances are, someone’s already faceplanted into the exact same issue.

Frequently Asked Questions (FAQ)

Q: WinRM keeps disconnecting. What do I do?

Check network stability, firewall rules, and WinRM timeout settings. Increase ansible_winrm_operation_timeout_sec and ansible_winrm_read_timeout_sec in your inventory.

Q: Can I use Ansible to manage Windows without a domain?

Yes. Use HTTPS + self-signed certificates + Basic authentication, but this is not recommended for production. Consider CredSSP or NTLM as alternatives.

Q: Does Ansible work with Windows 10/11 desktop editions?

Yes, Pro and Enterprise editions support WinRM. However, Microsoft recommends using Windows Server for management nodes.

Q: How do I debug Ansible Windows modules?

Add -vvvv to your ansible-playbook command for verbose logging. On the Windows side, check WinRM operational logs: Get-WinEvent -LogName Microsoft-Windows-WinRM/Operational.

Q: PowerShell DSC vs. Ansible—which is better?

DSC is more native for Windows configuration management. Ansible wins on cross-platform uniformity and community ecosystem. Pure Windows shop? Consider DSC. Mixed environment? Go Ansible.

Elvin Hui

About the Author: Elvin Hui

Elvin is a Senior Infrastructure Engineer with 10+ years of experience spanning data centers, cloud-native architecture, and network security. Certified in CCNA and AWS Solutions Architecture, I focus on turning real-world production "war stories" into actionable, hardcore technical guides.