How to Implement User Management with Associative Arrays in Ansible Playbooks


2 views

When managing multiple users in Ansible, a common challenge is assigning unique passwords to each user. The basic approach of using a list variable with a single password variable leads to all users inheriting the same credentials, which is both insecure and impractical.

Ansible's dictionary variables (YAML's associative arrays) provide the perfect solution. Here's how to structure your user data:

vars:
  users:
    user1:
      password: $6$rounds=656000$saltstring$encryptedhash1
      groups: sudo,adm
    user2:
      password: $6$rounds=656000$saltstring$encryptedhash2
      groups: developers
    user3:
      password: $6$rounds=656000$saltstring$encryptedhash3
      groups: docker,sudo

Here's the complete playbook implementation:

- hosts: all
  become: yes
  vars:
    users:
      webadmin:
        password: $6$J7s8K9l0$encryptedhashXYZ123
        groups: www-data,sudo
      dbadmin:
        password: $6$A1b2C3d4$encryptedhashABC456
        groups: postgres,adm
      devuser:
        password: $6$X5y6Z7w8$encryptedhashDEF789
        groups: docker,developers

  tasks:
    - name: Create users with individual passwords
      ansible.builtin.user:
        name: "{{ item.key }}"
        password: "{{ item.value.password }}"
        groups: "{{ item.value.groups | default(omit) }}"
        shell: /bin/bash
        append: yes
      loop: "{{ users | dict2items }}"

For more complex scenarios, consider these patterns:

# Using when conditions
- name: Create admin users
  ansible.builtin.user:
    name: "{{ item.key }}"
    password: "{{ item.value.password }}"
    groups: "{{ item.value.groups }}"
  loop: "{{ users | dict2items }}"
  when: "'sudo' in item.value.groups"

# Combining with vault for security
- name: Load encrypted passwords
  ansible.builtin.include_vars:
    file: passwords.yml
    name: user_passwords

- name: Create users with vault-protected passwords
  ansible.builtin.user:
    name: "{{ item.key }}"
    password: "{{ user_passwords[item.key] }}"
  loop: "{{ users | dict2items }}"

To create the encrypted passwords for your playbook:

# On Linux systems
mkpasswd --method=SHA-512 --rounds=656000

# Ansible alternative
ansible localhost -m debug -a "msg={{ 'plaintext' | password_hash('sha512', 'salt') }}"

1. Store sensitive passwords in Ansible Vault
2. Use consistent naming conventions
3. Document your user structure in playbook comments
4. Consider using ansible-galaxy roles for reusable user management


When automating user creation across multiple servers, a common pain point emerges: how to assign unique encrypted passwords to each user while maintaining clean playbook structure. The initial approach of using parallel lists falls short when dealing with user-specific attributes.

Ansible handles this elegantly through YAML dictionaries (associative arrays). Here's the proper implementation:

vars:
  users:
    myuser1:
      password: $6$rounds=656000$saltstring$hashedvalue1
      groups: sudo,adm
    myuser2: 
      password: $6$rounds=656000$saltstring$hashedvalue2
      groups: developers
      shell: /bin/zsh

The corresponding task adapts to handle dictionary iteration:

tasks:
  - name: Create users with individual passwords
    ansible.builtin.user:
      name: "{{ item.key }}"
      password: "{{ item.value.password }}"
      groups: "{{ item.value.groups | default('users') }}"
      shell: "{{ item.value.shell | default('/bin/bash') }}"
    loop: "{{ users | dict2items }}"

For production environments, consider generating passwords programmatically:

- name: Generate encrypted passwords
  set_fact:
    user_passwords: |
      {% set result = {} %}
      {% for user in ['dev1','qa2','ops3'] %}
        {% set salt = 12345|random(seed=user) %}
        {% set pass = 'defaultPass' ~ salt %}
        {% set encrypted = pass | password_hash('sha512', salt|string) %}
        {% set _ = result.update({user: encrypted}) %}
      {% endfor %}
      {{ result }}

For sensitive deployments, integrate Ansible Vault:

vars_files:
  - secrets/users_encrypted.yml  # encrypted with ansible-vault

Where users_encrypted.yml contains:

users:
  admin:
    password: !vault |
      $ANSIBLE_VAULT;1.1;AES256
      623133653966623430613934643...

For heterogeneous environments, add OS-specific logic:

tasks:
  - name: Set Windows password policy
    win_user_right:
      name: SeNetworkLogonRight
      users: "{{ users | dict2items | selectattr('value.groups', 'search', 'remote') | map(attribute='key') | list }}"
    when: ansible_os_family == 'Windows'