TL;DR
- Set up WinRM properly once with HTTPS, certificate, and firewall rules, and you'll have a stable foundation for Ansible automation on Windows servers.
- With Polycrate, Ansible runs with all WinRM and Python dependencies in a container—no
pywinrmchaos, no local installations, no version debates within the team. - Windows automation is already built into the Polycrate Ansible image: including the ansible.windows collection (e.g.
win_service,win_updates), pywinrm, and the matching Ansible bundle—no customDockerfile, no manualansible-galaxyinstalls for typical use. You only configure the workspace, inventory, and playbooks. - A dedicated Polycrate block for Windows forms your reusable "Windows toolbox": manage services, install features, adjust the registry, distribute software, apply patches—all as clearly structured actions.
- Passwords and other secrets are stored in
secrets.polyand secured via built-in workspace encryption withage—not ininventory.yml. - ayedo supports Windows teams with practical automation blueprints, Polycrate implementation, and a focused Windows automation workshop—from WinRM basics to scalable patch management.
Why Windows Admins Should Consider Polycrate + Ansible
Many Windows admins view Ansible skeptically:
- "That's a Linux tool."
- "Setting up WinRM is annoying."
- "Python,
pywinrm, OpenSSL—I don't want to deal with all that locally."
Technically, Ansible is an excellent tool, especially for Windows:
- There are dedicated collections like
ansible.windowsandcommunity.windows. - You can manage services, roles/features, registry, software, and patches idempotently.
- You get clean, traceable changes with playbooks that are easy to version.
What hurts in practice is something else:
-
Dependency Problem:
Maintaining the same Python/Ansible/WinRM versions on every admin laptop and jump box is exhausting—and security-sensitive. -
Playbook Proliferation:
Five different script folders, slightly different playbooks, no one knows what's "official." -
Secrets Everywhere:
Passwords ininventory.yml, ingroup_vars, in scripts—and then comes the audit.
Polycrate addresses these issues:
- The bundled image ships Ansible for Windows with ansible.windows and pywinrm—Windows modules are not an add-on you install separately; they are part of the default toolchain. For typical
ansible.windows.*playbooks you need no extraDockerfile.polyand no manualansible-galaxy collection install ansible.windows. Only if you need additional collections do you extend the image (e.g.community.windows). - Ansible always runs in a container—identical on every machine.
- Your automation is structured into blocks that you can share locally or via an OCI registry and PolyHub.
- Workspace encryption with
ageensures you can securely store passwords insecrets.polywithout an extra vault solution.
In this post, we'll build this step by step—with a focus on Windows admins without cloud-native experience.
Set Up WinRM Correctly: HTTPS, Certificate, Firewall
Before Ansible can communicate with your Windows servers via Polycrate, WinRM must be properly configured. We focus on:
- HTTPS (Port 5986)
- Certificate (Self-Signed or from your PKI)
- Firewall allowance
- Authentication with NTLM
Step 1: Enable WinRM Service
On the target server (e.g., Windows Server 2022), open PowerShell as Administrator:
winrm quickconfig
If the service is not running, you will be asked if it should be configured. Confirm this.
This usually sets up an HTTP listener on port 5985. For Ansible over the internet or in sensitive networks, you should disable HTTP and use HTTPS.
Step 2: Create or Assign a Certificate for HTTPS
For testing and small environments, a self-signed certificate is sufficient. In a domain, you should use a certificate from your corporate PKI.
Self-Signed Example:
$newCert = New-SelfSignedCertificate `
-DnsName "win01.acme-corp.com" `
-CertStoreLocation "Cert:\LocalMachine\My"
Note the thumbprint:
$newCert.Thumbprint
In production environments, replace this step with a server certificate issued by your CA.
Step 3: Configure HTTPS Listener for WinRM
First, check existing HTTP listeners:
winrm enumerate winrm/config/Listener
Create an HTTPS listener with your certificate (insert thumbprint):
$thumb = "<YOUR_THUMBPRINT>"
winrm create winrm/config/Listener?Address=*+Transport=HTTPS `
"@{Hostname=`"win01.acme-corp.com`"; CertificateThumbprint=`"$thumb`"}"
If an HTTPS listener already exists, you can alternatively adjust it with winrm set.
Check security settings:
winrm get winrm/config
Important points:
AllowUnencryptedshould befalse.- Under
Service/Auth,Basicshould ideally befalse, NTLM/Kerberos are preferable.
Step 4: Open Firewall for WinRM HTTPS
The standard port for WinRM over HTTPS is 5986. Open the port in the Windows Firewall:
New-NetFirewallRule `
-Name "WINRM_HTTPS_IN" `
-DisplayName "WinRM over HTTPS" `
-Protocol TCP `
-LocalPort 5986 `
-Direction Inbound `
-Action Allow
Depending on the environment, you can restrict access to certain subnets or management servers.
Step 5: Test Connection from a Client
From an admin machine (or a jump box), you can test if HTTPS works with PowerShell:
Test-WsMan win01.acme-corp.com -UseSSL
If this works, the technical foundations for Ansible are in place. With plain Ansible, you would now install Python, Ansible, pywinrm, and SSL libraries and configure them accordingly.
With Polycrate, you skip this part entirely—bringing us to the workspace.
Polycrate Workspace for Windows Hosts
We create a simple Polycrate workspace that manages your Windows servers.
workspace.poly with Windows Block
In the root directory of your workspace is the workspace.poly:
name: acme-corp-automation
organization: acme
blocks:
- name: windows-baseline
from: windows-baseline
config:
windows_group: "windows"
What's happening here:
name: The name of your workspace, e.g., your team or project.blocks: List of block instances. We have a blockwindows-baselinethat comes from a local directory./blocks/windows-baseline.config.windows_group: The host group this block refers to by default.
More about workspaces can be found in the Workspace Documentation.
inventory.yml for Windows
Directly in the workspace root is inventory.yml. As in Multi-server management with Ansible inventories, all hosts live under all.hosts; under children, the windows group references them by name only—shared WinRM settings go in the group vars (no per-host duplication):
all:
hosts:
win01.acme-corp.com: {}
win02.acme-corp.com: {}
children:
windows:
hosts:
win01.acme-corp.com:
win02.acme-corp.com:
vars:
ansible_connection: winrm
ansible_winrm_transport: ntlm
ansible_port: 5986
ansible_winrm_server_cert_validation: ignore
Important:
ansible_connection: winrmtells Ansible to use WinRM.ansible_winrm_transport: ntlmchooses NTLM as the auth method (suitable for many classic Windows environments).ansible_winrm_server_cert_validation: ignoreis useful for self-signed certificates. In production environments, you should validate certificates correctly.
No passwords in the inventory. They will be placed in secrets.poly.
Further details on Ansible integration can be found in the Ansible Documentation for Polycrate.
Secrets for Windows: Passwords in secrets.poly Instead of inventory.yml
Polycrate comes with workspace encryption with age built-in. You don't need to run an external vault tool to securely store passwords.
Create secrets.poly
In the workspace root, create a file secrets.poly:
win_admin_user: "acme-admin"
win_admin_password: "SuperSecurePassword123!"
Before you commit this file to Git, encrypt the workspace:
polycrate workspace encrypt
Details can be found in the Workspace Encryption Documentation.
Polycrate makes these secrets available in the playbook as workspace.secrets.*. We'll use this to set ansible_user and ansible_password—without storing them in the inventory.
The Windows Block: Structured Automation Instead of Playbook Proliferation
Now we define our Windows block. The directory ./blocks/windows-baseline contains at least:
block.polybaseline.yml(Windows base configuration)patches.yml(Windows patch management)
block.poly for Windows
name: windows-baseline
version: 0.1.0
kind: generic
config:
windows_group: "windows"
actions:
- name: baseline
description: "Windows base configuration (services, features, registry, software)"
playbook: baseline.yml
- name: patches
description: "Windows patch management with win_updates"
playbook: patches.yml
kind: genericis suitable for classic Ansible blocks.- Under
actions, you define descriptive actions, each executing a playbook. - With
config.windows_group, you can centrally control the target group.
Best practices for blocks can be found in the Block Best Practices and the Blocks Documentation.
Execute Actions
You always execute an action with the same schema:
polycrate run windows-baseline baseline
or:
polycrate run windows-baseline patches
Polycrate starts a container with your defined toolchain, loads the workspace including encrypted secrets, and runs Ansible within it. You do not need to have Ansible, Python, or pywinrm installed on your laptop.
Automate Initial Windows Tasks: Services, Features, Registry, Software
Now it gets concrete: We write the playbook baseline.yml and use modules from ansible.windows and community.windows.
baseline.yml: Windows Base Configuration
- name: Apply Windows Baseline
hosts: "{{ block.config.windows_group }}"
gather_facts: false
vars:
ansible_user: "{{ workspace.secrets.win_admin_user }}"
ansible_password: "{{ workspace.secrets.win_admin_password }}"
ansible_connection: winrm
ansible_winrm_transport: ntlm
ansible_port: 5986
ansible_winrm_server_cert_validation: ignore
tasks:
- name: Ensure 'Spooler' service is running
ansible.windows.win_service:
name: Spooler
start_mode: auto
state: started
- name: Install 'Web-Server' (IIS) feature
ansible.windows.win_feature:
name: Web-Server
state: present
include_sub_features: true
restart: false
- name: Allow Remote Desktop via registry key
ansible.windows.win_regedit:
path: HKLM:\System\CurrentControlSet\Control\Terminal Server
name: fDenyTSConnections
data: 0
type: dword
- name: Install Chocolatey (if not present)
ansible.windows.win_chocolatey:
state: present
- name: Install 7zip via Chocolatey
ansible.windows.win_chocolatey:
name: 7zip
state: present
- name: Install custom MSI package
ansible.windows.win_package:
path: "\\fileserver.acme-corp.com\software\MyApp.msi"
product_id: "{12345678-ABCD-1234-ABCD-1234567890AB}"
state: present
What's happening here:
hosts: We use the group fromblock.config.windows_group, i.e.,windows. This way, you can use the same block in another workspace with a different group.vars: We set connection parameters (user/password, WinRM details) fromworkspace.secrets.*. That is the core idea: no plaintext passwords in the inventory.ansible.windows.win_service: The "Spooler" service must be running and set to start automatically. The module belongs to ansible.windows (not ansible-core); it is already included in the default Polycrate image (see TL;DR and “Polycrate addresses these issues”).ansible.windows.win_feature: Installs the IIS role including subfeatures.ansible.windows.win_regedit: Enables Remote Desktop (classic example).ansible.windows.win_chocolatey: Installs Chocolatey and then a package.ansible.windows.win_package: Installs an MSI from a central share.
You would write a similar playbook with plain Ansible—Ansible is fine here. The difference is everything around it:
- You would have to provide
ansible.windowsandcommunity.windowslocally. - Install
pywinrmand SSL dependencies cleanly. - Make sure every admin has the same environment.
With Polycrate, all of that ships in the bundled Ansible container (see python-requirements.txt / Polycrate Dockerfile: ansible bundle, pywinrm, additional Galaxy collections). Only if you need more collections do you extend the image (e.g. Dockerfile.poly). Your colleagues only need Polycrate and Docker—no local Python tuning.
Windows patch management with win_updates and reboot handling
Patches are a typical use case Windows admins want to automate cleanly—including controlled reboots.
patches.yml: install updates by category
- name: Install Windows patches
hosts: "{{ block.config.windows_group }}"
gather_facts: false
vars:
ansible_user: "{{ workspace.secrets.win_admin_user }}"
ansible_password: "{{ workspace.secrets.win_admin_password }}"
ansible_connection: winrm
ansible_winrm_transport: ntlm
ansible_port: 5986
ansible_winrm_server_cert_validation: ignore
tasks:
- name: Install security and critical updates
ansible.windows.win_updates:
category_names:
- SecurityUpdates
- CriticalUpdates
state: installed
reboot: yes
register: update_result
- name: Reboot if required
ansible.windows.win_reboot:
msg: "Rebooting for Windows updates."
pre_reboot_delay: 60
post_reboot_delay: 120
when: update_result.reboot_required
Explanation:
ansible.windows.win_updatescontrols which updates to install;category_nameslets you filter.reboot: yesallowswin_updatesto initiate a reboot if needed.ansible.windows.win_reboothandles the reboot in the Ansible context and waits until the host is reachable again.
From a Windows admin’s perspective, this is where Ansible shines: you can build a reproducible patch playbook and roll it out monthly via a Polycrate workflow, for example.
More on workflows: Workflows documentation.
Compared to plain Ansible: what Polycrate removes
Imagine building the same setup with plain Ansible—without Polycrate:
-
On your admin laptop: - Install Python in a suitable version. - Install Ansible (e.g. via
pipor the package manager). - Installpywinrmandrequests-ntlm. - Installansible.windowsandcommunity.windowscollections. - Adjustansible.cfg(inventory path, WinRM defaults, etc.). -
On your colleagues’ laptops: - The same setup—as identical as possible. - Coordinate updates regularly to avoid version drift.
-
In documentation: - Maintain install guides (“Please install Python 3.11.2, then
pip install …”). - Answer audit questions about supply-chain risk (“Who reviews PyPI packages?”).
With Polycrate, the picture changes:
- Ansible,
pywinrm, collections, etc. live in a container image. - You define the toolchain once (e.g. via
Dockerfile.poly) and share it with the team. - Polycrate starts the container, mounts your workspace, and runs actions.
The block model adds structure:
- Instead of loose playbooks on disk, you have a Windows block with clearly named actions (
baseline,patches). - You can share that block via an OCI registry—internally or through PolyHub.
- Other teams can consume your block without copying your entire workspace.
Technical details: Registry documentation and PolyHub.
Frequently asked questions
Does this only work with domain-joined servers?
No. Ansible over WinRM with ansible_connection: winrm and ansible_winrm_transport: ntlm also works for workgroup servers, as long as:
- name resolution is correct (DNS or hosts file),
- you have valid local or domain credentials,
- firewall rules allow WinRM access.
In a domain you can additionally use Kerberos, which is often preferable. For getting started, NTLM is often simpler, especially in mixed environments.
How do I handle different administrator accounts per server?
You have several options:
- Set different users per host in
inventory.ymland only pull the password fromsecrets.poly. - Store multiple credentials in
secrets.poly(e.g.win_admin_user_dmz,win_admin_user_internal) and useblock.configin the block to choose which account to use. - Define entirely different playbooks/blocks for especially sensitive systems.
Polycrate gives you enough flexibility with workspace.secrets.* and block.config.* without scattering passwords in plaintext files. See best practices.
We already use Ansible—is Polycrate still worth it?
If you already have a working Ansible setup, Polycrate mainly adds:
- Standardized toolchain: no more debates about who installed which Python/Ansible version.
- Structure through blocks: wrap existing playbooks in clear actions that non-specialists can run safely.
- Built-in encryption: secrets in
secrets.polyand encrypted workspace directories without extra tools.
For many teams, the first step is lift-and-shift of existing playbooks into a block and clean up gradually. We describe migration paths in other posts in this series.
More questions? See our FAQ.
Putting it into practice
What did we cover in this post?
- How to set up WinRM on Windows servers once, with HTTPS, certificate, and firewall rules.
- How a Polycrate workspace is built with
inventory.yml,workspace.poly,secrets.poly, and a Windows block (block.poly). - How to write Ansible playbooks that use
ansible.windowsandcommunity.windowsto manage services, features, registry, software, and updates—without a local Ansible install. - How to use Polycrate’s built-in security: workspace encryption with
ageand secrets insecrets.polyinstead ofinventory.yml.
For Windows teams, this is often where automation turns from “occasional scripts” into a repeatable, shareable, auditable platform.
How ayedo can help
We regularly help teams with setups like this:
- We develop Windows-specific blocks with you (e.g. baseline hardening, role-specific configuration, patch workflows).
- We help migrate existing scripts and playbooks into clean Polycrate blocks.
- We support integration with your processes—change management, compliance, and more.
If you want a practical, hands-on introduction with your team, our Windows automation workshop covers WinRM basics through scalable patch management with Polycrate.