Generating Windows Ansible Playbooks With AI Safely
Use AI to draft win_* Ansible plays without smuggling Linux modules into Windows hosts. WinRM setup, win_feature, become, and verifying with win_ping.
- #ansible
- #ai
- #windows
- #winrm
- #automation
A vendor app forced a pair of Windows Server 2022 boxes into my otherwise all-Linux fleet, and I did what everyone does now: I asked an AI to draft the Ansible role to install IIS and a couple of services. The output looked beautiful. It also wanted to apt update, push a file with mode: '0644', and run a service task against a host that has never heard of systemd. The model had pattern-matched on a thousand Linux playbooks and quietly assumed my Windows server was just another Ubuntu box wearing a hat. That is the whole problem with generating Windows plays from AI: the syntax is identical, the modules are not, and the failure mode is a playbook that runs halfway and leaves a server in a state nobody asked for. AI is genuinely good at drafting and decoding this stuff. It is not good at knowing it crossed an OS boundary. You have to be the one watching that line.
WinRM Is the Connection, Not SSH (Usually)
The first thing AI gets wrong is the connection plugin. Windows hosts traditionally talk to Ansible over WinRM, not SSH. Modern Windows can do SSH, but most production fleets I touch still run WinRM, and the inventory vars are completely different. If your generated inventory doesn’t set ansible_connection: winrm, Ansible will try SSH and hang or throw UNREACHABLE.
Here is a sane WinRM inventory block. Put credentials in a vault, not in plain text — this is illustrative:
[windows]
win-app-01.corp.example.com
win-app-02.corp.example.com
[windows:vars]
ansible_connection=winrm
ansible_user=ansible_svc
ansible_password={{ vault_win_password }}
ansible_winrm_transport=ntlm
ansible_port=5986
ansible_winrm_server_cert_validation=validate
The ansible_winrm_* family is where the detail lives. ansible_winrm_transport is usually ntlm, kerberos (for domain-joined hosts done right), or credssp (needed for second-hop/CredSSP scenarios like installing from a network share). ansible_port should be 5986 for HTTPS and 5985 for HTTP — and please use 5986. The ansible_winrm_server_cert_validation knob is the one AI loves to set to ignore so its example “just works.” In production you want validate with a real cert. If a draft hands you ignore, treat it as a TODO, not a default.
If you do go the SSH route on newer Windows, the vars look more familiar (ansible_connection: ssh, ansible_shell_type: powershell), but mixing the two transports across a group is a classic AI-generated inconsistency. Pick one per group and verify it.
win_ Modules Are a Separate Universe
This is the core of safe Windows generation. Linux modules and Windows modules are not interchangeable, even when the intent is identical. The model knows both vocabularies and will blend them if you let it.
The mapping you should have burned into memory before reviewing any AI draft:
service/systemdbecomesansible.windows.win_servicepackage/apt/yumbecomesansible.windows.win_packageorchocolatey.chocolatey.win_chocolateycopybecomesansible.windows.win_copyfilebecomesansible.windows.win_filecommand/shellbecomesansible.windows.win_command/ansible.windows.win_shell- installing a server role (IIS, AD, DNS) is
ansible.windows.win_feature— there is no Linux equivalent
A real Windows task to install IIS and start the service looks like this:
- name: Ensure Web Server role is installed
ansible.windows.win_feature:
name: Web-Server
state: present
include_management_tools: true
register: iis_feature
- name: Reboot if the feature install requires it
ansible.windows.win_reboot:
when: iis_feature.reboot_required
- name: Ensure the W3SVC service is running and automatic
ansible.windows.win_service:
name: W3SVC
state: started
start_mode: auto
Notice win_service uses start_mode: auto, not the Linux enabled: true. Notice win_feature can flag reboot_required — Windows role installs reboot, and a Linux-trained draft will skip handling that entirely. For packages, prefer Chocolatey where it fits:
- name: Install 7-Zip via Chocolatey
chocolatey.chocolatey.win_chocolatey:
name: 7zip
state: present
Use ansible.windows.win_package for MSIs and EXEs with explicit product IDs, and win_chocolatey when you have a Chocolatey source. Either way, no apt, no yum, no dnf should survive review in a Windows play. If you want to go deeper on the module-translation problem in general, I wrote about it in converting shell scripts to Ansible with AI — the same “looks right, wrong primitive” trap applies.
The Danger: AI Smuggling Linux Modules Into Windows Plays
The failure I opened with is the one to watch for. Because YAML is YAML, a play that mixes win_service with a plain copy task will lint clean and may even partially run before it blows up. A copy task targeting a WinRM host often fails outright, but command, file mode, and lineinfile can do genuinely surprising things on a Windows path.
So when you prompt, be explicit. A prompt I actually use:
Generate an Ansible play targeting Windows Server 2022 over WinRM. Use only fully-qualified
ansible.windows.*andchocolatey.chocolatey.*modules. Do not use any Linux modules (no apt, yum, service, systemd, copy, file with mode, or command — use the win_ equivalents). The play installs the Web-Server feature, deploys a config file with win_copy, and ensures W3SVC is running. Add a win_ping verification play at the top.
Forcing fully-qualified collection names (FQCN) is the single highest-leverage instruction. ansible.windows.win_service is unambiguous; bare service is the Linux one. When every module in the draft is namespaced under ansible.windows or chocolatey.chocolatey, a stray ansible.builtin.copy stands out immediately on review. I keep a saved version of this in my prompt library so I’m not re-typing the guardrails every time.
become On Windows Doesn’t Mean sudo
Privilege escalation is another place the Linux muscle memory misfires. There is no sudo on Windows. The WinRM connection already runs as the configured user with their privileges, so for most tasks you don’t set become at all. When you do need it — to run a task as a different account, or under the machine account for access to a network resource — Windows uses become_method: runas:
- name: Run a task as the local Administrator
ansible.windows.win_command: whoami
become: true
become_method: runas
become_user: Administrator
If an AI draft sprinkles become: true with become_method: sudo across your Windows tasks, that’s a tell it never left Linux-land. Strip it. Add runas only where you genuinely need a different security context. Connection and become confusion across OS boundaries is common enough that it’s worth treating as its own review checklist item.
Verify Before You Trust the Draft
Never run a generated Windows play blind. The cheapest insurance is a connectivity check that exercises WinRM end to end without changing anything:
- name: Verify Windows connectivity
hosts: windows
gather_facts: false
tasks:
- name: Ping Windows hosts over WinRM
ansible.windows.win_ping:
ansible.windows.win_ping is the Windows analog of the Linux ping module — it confirms the WinRM transport, auth, and PowerShell execution all work. If a draft hands you bare ping for Windows hosts, that’s a Linux module and it will fail. Run the check, then dry-run the real play:
# Confirm WinRM auth and PowerShell on every Windows host
ansible-playbook verify_win.yml -i inventory/windows.ini
# Dry-run the real play and watch every change before it lands
ansible-playbook site.yml -i inventory/windows.ini --check --diff -vv
--check --diff won’t be perfect on Windows — some win_ modules don’t fully support check mode — but it surfaces obvious problems and shows you what each task intends to change. Read the task names in the output. If you see apt, systemd, or a mode: argument scroll past, the model crossed the line and you caught it before production did.
Where AI Earns Its Keep
None of this is an argument against generating Windows plays with AI. It’s fast at the boilerplate, it knows the win_feature names I’d otherwise have to look up, and it’s excellent at explaining why a credssp second-hop install is failing. What it doesn’t have is the judgment that this particular host is Windows and that copy is the wrong tool. That judgment is the job. Let the model draft, decode the WinRM errors, and propose the become_method: runas fix — then you confirm the connection vars, force FQCN modules, run win_ping, and --check the play before it touches a box.
The pattern holds across every Ansible task I delegate, not just Windows: the AI moves fast over familiar ground, and the human watches the boundaries it can’t see. For more on that workflow across the rest of the stack, the Ansible category collects the rest of what I’ve learned doing this in anger. Keep the model on a short leash for Windows specifically, and it’s a genuine accelerant. Take it off, and you’ll spend your afternoon explaining to a vendor why their app server tried to run apt.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.