Sitemap

Ansible to manage Windows servers

5 min readJan 12, 2026

--

This is mostly focused on having the control node as Gitlab runner and the managed nodes as Windows EC2 servers

1. Control node

  • This is the machine(docker, Linux EC2, WSL on windows) where Ansible itself is installed
  • It runs ansible and ansible-playbook
  • It parses playbooks and sends instructions to other machines
  • Ansible cannot run natively on Windows as a control node. This is a design limitation, not a configuration issue

How to install Ansible in Windows 11?

Windows is not supported for the control node. Because Ansible controller uses heavy use of possix fork system call, Linux/Unix tools, SSH, fork, pipes, etc.

Cygwin is not reliable as it breaks.

So WSL is preferred to install Ansible in Windows 11.

On the windows, using powershell command ‘wsl — install’, WSL can be installed.

Linux control node (EC2, laptop, WSL, etc.)
|
| WinRM
v
Windows EC2 server (managed node)

✅ Option 1: Linux EC2 control node (most common)

  • Amazon Linux / Ubuntu EC2
  • Install Ansible
  • Use it to control Windows EC2 via WinRM
  • For example, a GitLab Runner can act as an Ansible control node, as long as the runner is running on Linux

✅ Option 2: WSL on Windows (for testing)

  • Install WSL (Ubuntu)
  • Install Ansible inside WSL
  • Control Windows hosts remotely

✅ Option 3: Ansible Tower / AWX

  • Web-based Ansible execution
  • Runs on Linux containers
  • Controls Windows machines

More on Gitlab runner

A GitLab Runner:

  • Is just an execution environment for CI jobs
  • Can install and run Ansible inside a pipeline job
GitLab Runner (Linux)
↓
Ansible installed here
↓
Controls Linux / Windows hosts

Important condition (very important)

✅ Runner must be Linux-based

Examples that work:

  • GitLab SaaS runners (Docker-based Linux)
  • Self-hosted runner on:
  • Ubuntu
  • Amazon Linux
  • RHEL
  • Kubernetes GitLab Runner (Linux containers)

Runner on Windows

  • Cannot act as Ansible control node
  • Same limitation as before: Ansible doesn’t run natively on Windows
GitLab CI Pipeline
|
v
GitLab Runner (Linux, Docker)
|
| WinRM
v
Windows EC2 instances

Ansible uses:

  • WinRM to connect
  • PowerShell modules on Windows

Example .gitlab-ci.yml

Using Docker runner (recommended)

ansible_deploy:
image: python:3.11
stage: deploy
before_script:
- pip install ansible pywinrm
script:
- ansible-playbook -i inventory/windows.ini site.yml

Windows inventory example

[windows]
win-ec2-01 ansible_host=10.0.1.10
[windows:vars]
ansible_user=Administrator
ansible_password=MyPassword
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore

Benefits of using GitLab Runner as control node

✅ No need for a dedicated Ansible server
✅ Playbooks run from CI/CD (auditable & repeatable)
✅ Secrets managed via GitLab CI variables
✅ Easy automation for infra & app deployment

Things to watch out for

⚠️ Network access:

  • Runner must reach Windows hosts on 5985/5986

⚠️ Credentials:

  • Use GitLab CI protected variables
  • Never hardcode passwords

⚠️ Runner lifecycle:

  • Ephemeral runners → install Ansible every run (OK)
  • Persistent runners → install once

Below is a complete, minimal, working Ansible + GitLab CI demo repository structure that uses a Linux GitLab Runner as the Ansible control node and manages Windows EC2 instances via WinRM.

Get Developer Notes’s stories in your inbox

Join Medium for free to get updates from this writer.

You can copy this 1:1 and it will work with only small environment-specific changes.

Repository Structure

ansible-gitlab-windows-demo/
├── .gitlab-ci.yml
├── ansible.cfg
├── inventory/
│ └── windows.ini
├── playbooks/
│ └── site.yml
├── roles/
│ └── windows_common/
│ ├── tasks/
│ │ └── main.yml
│ └── vars/
│ └── main.yml
└── README.md

1️⃣ .gitlab-ci.yml

stages:
- deploy
ansible_deploy:
stage: deploy
image: python:3.11
variables:
ANSIBLE_HOST_KEY_CHECKING: "False"
before_script:
- pip install --no-cache-dir ansible pywinrm
script:
- ansible --version
- ansible-playbook -i inventory/windows.ini playbooks/site.yml

✅ Works with GitLab SaaS runners
✅ Runner acts as Ansible control node

2️⃣ ansible.cfg

[defaults]
inventory = inventory/windows.ini
roles_path = roles
host_key_checking = False
retry_files_enabled = False
stdout_callback = yaml
timeout = 60

3️⃣ inventory/windows.ini

[windows]
win-ec2-01 ansible_host=10.0.1.10
[windows:vars]
ansible_user=Administrator
ansible_password={{ lookup('env', 'WIN_PASSWORD') }}
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore

🔐 Password is injected via GitLab CI variable

  • Add WIN_PASSWORD in GitLab → Settings → CI/CD → Variables
  • Mark it Protected and Masked

4️⃣ playbooks/site.yml

---
- name: Windows configuration via Ansible
hosts: windows
gather_facts: no
roles:
- windows_common

5️⃣ roles/windows_common/tasks/main.yml

---
- name: Check connectivity
win_ping:
- name: Create a directory
win_file:
path: C:\AnsibleDemo
state: directory
- name: Create a demo file
win_copy:
content: "Hello from Ansible via GitLab Runner"
dest: C:\AnsibleDemo\hello.txt
- name: Ensure Print Spooler is running
win_service:
name: Spooler
state: started

6️⃣ roles/windows_common/vars/main.yml

---
demo_message: "Ansible + GitLab CI + Windows"

7️⃣ README.md (optional but recommended)

# Ansible GitLab Windows Demo
This repository demonstrates:
- GitLab Runner acting as an Ansible control node
- Managing Windows EC2 instances via WinRM
- Secure secret handling with GitLab CI variables
## Requirements
- GitLab Linux runner (Docker or Shell)
- Windows EC2 with WinRM enabled
- Port 5985/5986 open
## Run
Pipeline executes automatically on commit.

8️⃣ Windows EC2 prerequisites (VERY IMPORTANT)

Run once on the Windows EC2 instance (PowerShell as Admin):

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

Or for HTTPS (recommended in prod):

  • Configure WinRM over 5986 with cert

Pipeline Execution Flow

Git push
↓
GitLab CI
↓
Linux GitLab Runner
↓
Ansible installed
↓
Ansible playbook executed
↓
WinRM → Windows EC2

How to test locally (optional)

export WIN_PASSWORD=yourpassword
ansible-playbook -i inventory/windows.ini playbooks/site.yml

Common failure points (and fixes)

ProblemFixWinRM timeoutOpen ports 5985/5986Auth failedCheck user/passwordNTLM errorInstall pywinrmPermission deniedUse Administrator.

2. Managed nodes

  • These are the machines Ansible controls
  • They can be Linux or Windows

Managed Nodes (Windows)

  • Ansible can manage Windows servers But from a Linux-based control node
  • Communication happens using

1. WinRM (not SSH)

2. PowerShell on the Windows target

In simple terms, Ansible can manage Windows machines from Linux using PowerShell over WinRM.

When Ansible manages Windows machines:

  • It executes PowerShell commands remotely
  • Uses Windows-specific Ansible modules like:

1. win_service

2. win_file

3. win_shell

4. win_updates

  • You do not write Bash
  • You write Ansible YAML, which internally calls PowerShell

Verification of successful Ansible execution on Win EC2

You verify successful Ansible execution on a Windows EC2 instance at three levels:
CI logs → Ansible output → Windows-side validation.
Below is a clear, practical checklist you can follow.

1️⃣ Verify in GitLab CI pipeline (first and fastest check)

What to look for in the job logs

✅ Job status: Passed / Green

✅ Ansible output like:

PLAY [Windows configuration via Ansible] *******************
TASK [windows_common : Check connectivity] *****************
ok: [win-ec2-01]
TASK [windows_common : Create a directory] *****************
changed: [win-ec2-01]
TASK [windows_common : Create a demo file] *****************
changed: [win-ec2-01]
TASK [windows_common : Ensure Print Spooler is running] ****
ok: [win-ec2-01]
PLAY RECAP *************************************************
win-ec2-01 : ok=4 changed=2 unreachable=0 failed=0

Key indicators

FieldMeaningfailed=0No failuresunreachable=0WinRM workedchanged>0Actions actually executed

❌ If Ansible did NOT run successfully, you’ll see:

  • FAILED!
  • UNREACHABLE!
  • Red pipeline status

2️⃣ Verify directly on the Windows EC2 instance (definitive proof)

Log in to the Windows EC2 via RDP and check what Ansible changed.

A. Check directory creation

Test-Path C:\AnsibleDemo

Expected output:

True

B. Check the file content

Get-Content C:\AnsibleDemo\hello.txt

Expected output:

Hello from Ansible via GitLab Runner

C. Verify service state

Get-Service Spooler

Expected:

Status   Name     DisplayName
------ ---- -----------
Running Spooler Print Spooler

3️⃣ Verify using Ansible ad-hoc commands (optional but powerful)

From the GitLab runner or any Linux control node:

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

Expected:

win-ec2-01 | SUCCESS => {
"ping": "pong"
}

This confirms:

  • WinRM connectivity
  • Authentication
  • Ansible execution capability

4️⃣ Enable verbose logging (when debugging)

If you want proof-level detail, run:

ansible-playbook -vvv -i inventory/windows.ini playbooks/site.yml

You will see:

  • PowerShell commands sent
  • WinRM responses
  • Module execution details

5️⃣ Add an explicit verification task (best practice)

Add this to your playbook to make success self-verifying:

- name: Verify demo file exists
win_stat:
path: C:\AnsibleDemo\hello.txt
register: demo_file
- name: Fail if demo file missing
fail:
msg: "Ansible execution failed on Windows"
when: not demo_file.stat.exists

Now:

  • Pipeline fails automatically if Ansible didn’t apply changes

6️⃣ Cloud-level verification (EC2)

Optional but useful:

  • Check Windows Event Viewer
  • Windows Logs → System
  • WinRM events confirm remote execution

--

--

Developer Notes
Developer Notes

Written by Developer Notes

There is nothing either good or bad but thinking makes it so..... Shakespeare