Hi,
Recently I published a certified collection that supports WSL automation via Ansible, microsoft.wsl. I put together a brief demo of how the modules can support managing multiple WSL instances on one or more Windows hosts.
For transparency, I did use AI to help compile this post.
Managing WSL Developer Environments at Scale with Ansible
WSL has become a critical part of the developer toolchain on Windows. Teams standardize on specific distributions, versions, and configurations. Without automation, that standard erodes one laptop at a time.
One machine is missing a required distribution, another has a distribution stuck on WSL v1. Multiply that across a fleet and you have a real configuration management challenge.
The microsoft.wsl Ansible certified collection allows WSL to be managed as infrastructure-as-code. With Ansible, you get:
- Visibility: Run the audit playbook on a schedule or in CI to catch drift before it becomes a support ticket.
- Consistency: The enforce playbook brings every host to the same baseline, whether it’s a fresh install or a machine that’s been accumulating changes for months.
- Idempotency: Safe to run repeatedly. If nothing has drifted, nothing changes.
- Scale: The same playbook that manages three hosts manages three hundred.
In this post we’ll walk through two playbooks that demonstrate the core workflow: audit the current state of WSL across your hosts, then enforce a desired state idempotently.
Step 1: Audit — know what you have
Before changing anything, you want visibility. We have three Windows machines managed over WinRM, each in a slightly different state.
The audit playbook uses two info modules from the collection (microsoft.wsl.status_info and microsoft.wsl.distribution_info) to gather facts, then evaluates them against a policy:
- the latest Fedora distribution must be installed on WSL v2
- no distributions should be running on v1.
---
- name: Audit WSL State Across Hosts
hosts: windows
gather_facts: false
tasks:
- name: Gather WSL status and online distributions
microsoft.wsl.status_info:
gather_online: true
register: wsl_status
- name: Gather detailed distribution info
microsoft.wsl.distribution_info:
register: wsl_distro_info
- name: Determine the latest available Fedora distribution
ansible.builtin.set_fact:
latest_fedora: >-
{{ wsl_status.distributions.online
| dict2items
| selectattr('key', 'match', '^FedoraLinux-')
| sort(attribute='key', reverse=true)
| map(attribute='key')
| first }}
- name: Check if the latest Fedora distribution is installed
ansible.builtin.set_fact:
fedora_installed: "{{ latest_fedora in (wsl_distro_info.distributions | default({})) }}"
- name: Check if installed Fedora is using WSL version 2
ansible.builtin.set_fact:
fedora_version_ok: >-
{{ (wsl_distro_info.distributions[latest_fedora].version | default(0)) == 2 }}
when: fedora_installed | bool
- name: Set fedora_version_ok to false when not installed
ansible.builtin.set_fact:
fedora_version_ok: false
when: not (fedora_installed | bool)
- name: Find distributions running on WSL v1
ansible.builtin.set_fact:
v1_distros: >-
{{ wsl_distro_info.distributions | default({})
| dict2items
| selectattr('value.version', 'equalto', 1)
| map(attribute='key')
| list }}
- name: Determine overall compliance
ansible.builtin.set_fact:
host_compliant: >-
{{ fedora_installed | bool
and fedora_version_ok | bool
and (v1_distros | length == 0) }}
- name: Display audit results
ansible.builtin.debug:
msg:
- "===== WSL Audit Report for {{ inventory_hostname }} ====="
- "Latest Fedora Available: {{ latest_fedora }}"
- "WSL Default Version: {{ wsl_status.default_version }}"
- "Fedora Installed: {{ 'Yes' if fedora_installed | bool else 'No MISSING' }}"
- "Fedora on WSL v2: {{ 'Yes OK' if fedora_version_ok | bool else 'No NEEDS CONVERSION' }}"
- "WSL v1 Distributions: {{ 'None OK' if v1_distros | length == 0 else v1_distros | join(', ') + ' NEEDS CONVERSION' }}"
- "---"
- "Overall: {{ 'COMPLIANT' if host_compliant | bool else 'OUT OF SPEC' }}"
Running it against our three hosts gives a clear picture. Here is the audit report output for each host:
===== WSL Audit Report for demo1 =====
Latest Fedora Available: FedoraLinux-44
WSL Default Version: 1
Fedora Installed: Yes
Fedora on WSL v2: Yes OK
WSL v1 Distributions: Ubuntu NEEDS CONVERSION
---
Overall: OUT OF SPEC
===== WSL Audit Report for demo2 =====
Latest Fedora Available: FedoraLinux-44
WSL Default Version: 2
Fedora Installed: No MISSING
Fedora on WSL v2: No NEEDS CONVERSION
---
Overall: OUT OF SPEC
===== WSL Audit Report for demo3 =====
Latest Fedora Available: FedoraLinux-44
WSL Default Version: 2
Fedora Installed: Yes
Fedora on WSL v2: Yes OK
WSL v1 Distributions: None OK
---
Overall: COMPLIANT
Two out of three hosts are out of spec, each for different reasons. demo1 has an Ubuntu distribution stuck on WSL v1. demo2 is missing Fedora entirely. Only demo3 passes every check.
Step 2: Enforce — bring hosts into the desired state
Now that we know what’s wrong, the enforce playbook fixes it. It uses the same info modules to assess the current state, then applies changes only where needed:
---
- name: Enforce WSL Desired State
hosts: windows
gather_facts: false
tasks:
- name: Gather WSL status and online distributions
microsoft.wsl.status_info:
gather_online: true
register: wsl_status
- name: Gather detailed distribution info
microsoft.wsl.distribution_info:
register: wsl_distro_info
- name: Determine the latest available Fedora distribution
ansible.builtin.set_fact:
latest_fedora: >-
{{ wsl_status.distributions.online
| dict2items
| selectattr('key', 'match', '^FedoraLinux-')
| sort(attribute='key', reverse=true)
| map(attribute='key')
| first }}
- name: Find distributions running on WSL v1
ansible.builtin.set_fact:
v1_distros: >-
{{ wsl_distro_info.distributions | default({})
| dict2items
| selectattr('value.version', 'equalto', 1)
| map(attribute='key')
| list }}
- name: Convert WSL v1 distributions to v2
microsoft.wsl.distribution:
name: "{{ item }}"
version: 2
loop: "{{ v1_distros }}"
when: v1_distros | length > 0
- name: Ensure latest Fedora distribution is installed on WSL v2
microsoft.wsl.distribution:
name: "{{ latest_fedora }}"
version: 2
Each host got exactly the changes it needed — and nothing more:
- demo1 received one change: Ubuntu was converted from WSL v1 to v2.
- demo2 received one change: FedoraLinux-44 was installed on WSL v2.
- demo3 received zero changes. It was already compliant, so every mutating task was skipped. This is idempotency in action — running the playbook against an already-correct host is a no-op.
PLAY RECAP
demo1 : ok=6 changed=1 unreachable=0 failed=0 skipped=0
demo2 : ok=5 changed=1 unreachable=0 failed=0 skipped=1
demo3 : ok=5 changed=0 unreachable=0 failed=0 skipped=1
What the collection provides
The two playbooks above use three modules from microsoft.wsl:
| Module | Purpose |
|---|---|
microsoft.wsl.status_info |
Gathers WSL status: default version, available online distributions, and system-level configuration. |
microsoft.wsl.distribution_info |
Returns details about locally installed distributions: name, version (v1/v2), state, and configuration. |
microsoft.wsl.distribution |
Manages distributions declaratively: install, set the WSL version, or configure them. |
The info modules give you a read-only view that’s perfect for auditing and conditional logic. The distribution module is the workhorse for enforcement. It handles installation and version conversion in a single, idempotent resource.
Find more information on Automation Hub or in the GitHub repository.