Introduction

The "Unhandled exception while executing module win_user" error is one of the more confusing Ansible errors because it doesn't clearly tell you what's wrong. The root cause is almost always that the password you're setting doesn't meet Windows password complexity requirements. This article explains the error, Windows password policy, and multiple solutions.

The Error

Problematic Playbook

---
- name: Create Windows user
  hosts: windows
  tasks:
    - name: Create local user
      ansible.windows.win_user:
        name: example
        password: password
        state: present

Error Output

TASK [Create local user] **************
An exception occurred during task execution. The error was:
fatal: [WindowsServer]: FAILED! => {
    "changed": false,
    "msg": "Unhandled exception while executing module: Exception calling
    \"SetPassword\" with \"1\" argument(s): \"The password does not meet
    the password policy requirements. Check the minimum password length,
    password complexity and password history requirements.\r\n\""
}

Understanding Windows Password Policy

Windows Server (and domain-joined workstations) enforce password complexity by default:

RequirementDefault Setting
Minimum length8 characters (Server 2022: 14 for new installs)
ComplexityEnabled
HistoryRemember 24 passwords
Maximum age42 days
Minimum age1 day

Complexity Requirements

When enabled, passwords must contain characters from 3 of 4 categories:

  1. Uppercase letters (A-Z)
  2. Lowercase letters (a-z)
  3. Digits (0-9)
  4. Special characters (!@#$%^&* etc.)

Additionally:

  • Cannot contain the username or parts of the full name
  • Must meet minimum length

Solutions

Solution 1: Use a Complex Password

---
- name: Create Windows user
  hosts: windows
  tasks:
    - name: Create local user with complex password
      ansible.windows.win_user:
        name: deploy_user
        password: "Str0ng!P@ssw0rd#2026"
        password_never_expires: true
        state: present
        groups:
          - Administrators

Solution 2: Generate Passwords with Ansible

---
- name: Create user with generated password
  hosts: windows
  tasks:
    - name: Generate random password
      ansible.builtin.set_fact:
        user_password: "{{ lookup('password', '/dev/null length=20 chars=ascii_letters,digits,punctuation') }}"
      delegate_to: localhost

    - name: Create user
      ansible.windows.win_user:
        name: deploy_user
        password: "{{ user_password }}"
        state: present

    - name: Save password to vault
      ansible.builtin.copy:
        content: "{{ user_password }}"
        dest: "/tmp/{{ inventory_hostname }}_deploy_password.txt"
        mode: "0600"
      delegate_to: localhost

Solution 3: Use Ansible Vault for Passwords

# Create encrypted variable
# ansible-vault encrypt_string 'C0mpl3x!Pass#2026' --name 'user_password'

---
- name: Create user with vaulted password
  hosts: windows
  vars:
    user_password: !vault |
      $ANSIBLE_VAULT;1.1;AES256
      6262653733...
  tasks:
    - name: Create local user
      ansible.windows.win_user:
        name: deploy_user
        password: "{{ user_password }}"
        state: present

Solution 4: Check Policy Before Creating User

---
- name: Create user with policy awareness
  hosts: windows
  tasks:
    - name: Get current password policy
      ansible.windows.win_shell: |
        $policy = Get-ADDefaultDomainPasswordPolicy -ErrorAction SilentlyContinue
        if (-not $policy) {
          $policy = net accounts | Select-String "Minimum password length"
        }
        $policy | ConvertTo-Json
      register: password_policy
      changed_when: false
      failed_when: false

    - name: Display password policy
      ansible.builtin.debug:
        var: password_policy.stdout_lines

    - name: Create user with compliant password
      ansible.windows.win_user:
        name: deploy_user
        password: "{{ complex_password }}"
        state: present

Common win_user Operations

Create User with Full Options

- name: Create full user account
  ansible.windows.win_user:
    name: deploy_user
    fullname: "Deploy Service Account"
    description: "Ansible-managed service account"
    password: "{{ vault_deploy_password }}"
    password_never_expires: true
    user_cannot_change_password: true
    account_disabled: false
    state: present
    groups:
      - Remote Desktop Users
      - Administrators

Update Existing User Password

- name: Update user password
  ansible.windows.win_user:
    name: deploy_user
    password: "{{ new_password }}"
    update_password: always  # always (default) or on_create
    state: present

Remove a User

- name: Remove user account
  ansible.windows.win_user:
    name: old_user
    state: absent

Create Multiple Users

- name: Create multiple users
  ansible.windows.win_user:
    name: "{{ item.name }}"
    password: "{{ item.password }}"
    groups: "{{ item.groups }}"
    state: present
  loop:
    - name: web_admin
      password: "W3b@dm1n!2026"
      groups: ['Administrators']
    - name: db_reader
      password: "Db#R3ad3r!2026"
      groups: ['Remote Desktop Users']
    - name: deploy_svc
      password: "D3pl0y$vc!2026"
      groups: ['Administrators']
  no_log: true  # hide passwords from output

Other Causes of win_user Exceptions

WinRM Connection Issues

fatal: [server]: UNREACHABLE! => {
    "msg": "winrm connection error"
}

Fix: Verify WinRM is configured:

# inventory
[windows]
win01 ansible_host=192.168.1.100

[windows:vars]
ansible_user=Administrator
ansible_password="{{ vault_win_password }}"
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore
ansible_port=5986

User Already Exists with Different Properties

# Use update_password: on_create to only set password on first creation
- name: Create user (don't reset existing password)
  ansible.windows.win_user:
    name: existing_user
    password: "{{ initial_password }}"
    update_password: on_create
    state: present

Domain vs Local Users

# Local user (default)
- name: Create local user
  ansible.windows.win_user:
    name: local_admin
    password: "{{ password }}"
    state: present

# Domain user — use win_domain_user instead
- name: Create domain user
  microsoft.ad.user:
    name: domain_admin
    password: "{{ password }}"
    upn: domain_admin@example.com
    state: present

Password Complexity Cheat Sheet

# ❌ Fails complexity — only lowercase
password: simplepassword

# ❌ Fails complexity — only 2 categories (lower + digit)
password: password123

# ❌ Fails length — too short
password: P@ss1

# ✅ Passes — 3+ categories, 12+ chars
password: "Str0ng!Pass2026"

# ✅ Passes — all 4 categories
password: "C0mpl3x#P@ss!"

# ✅ Passphrase style — long, meets all requirements
password: "Ansible-Manages-Windows-2026!"

Best Practices

  1. Always use Ansible Vault for passwords — never hardcode in plaintext
  2. Use no_log: true on tasks that handle passwords
  3. Set update_password: on_create to avoid resetting passwords on every run
  4. Test passwords locally first with net user testuser P@ss /add on Windows
  5. Generate passwords with the password lookup plugin for service accounts
  6. Document password policy in your project README for team awareness

Conclusion

The "Unhandled exception while executing module win_user" error is caused by passwords that don't meet Windows complexity requirements. Use passwords with 12+ characters containing uppercase, lowercase, digits, and special characters. Store passwords in Ansible Vault, use no_log: true, and consider generating random passwords with the password lookup plugin for service accounts.