How to create a dynamic dictionary in Ansible

Solution 1:

Rather than having a complex dictionary that would read:

- tom: password123456
- jerry: password123456789
- muttley: password12345

Aim for a structure that would be normalized, like:

- name: tom
  password: password123456
- name: jerry
  password: password123456789
- name: muttley
  password: password12345

This can be achieved with the task:

- set_fact:
    users_with_passwords: >-
      {{
        users_with_passwords | default([]) + 
        [{
          'name': item,
          'password': lookup(
            'password', 
            '/dev/null chars=ascii_lowercase,ascii_uppercase,digits,punctuation length=20'
          )
        }]
      }}
  loop: "{{ users }}"

And, can be easily accessed via something like:

- debug:
    msg: "{{ item.name }}'s password is `{{ item.password }}`"
  loop: "{{ users_with_passwords }}"
  loop_control:
    label: "{{ item.name }}"

Given the playbook

- hosts: localhost
  gather_facts: no
  vars:
    users:
      - tom
      - jerry
      - muttley

  tasks:
    - set_fact:
        users_with_passwords: >-
          {{
            users_with_passwords | default([]) + 
            [{
              'name': item,
              'password': lookup(
                'password', 
                '/dev/null chars=ascii_lowercase,ascii_uppercase,digits,punctuation length=20'
              )
            }]
          }}
      loop: "{{ users }}"

    - debug:
        msg: "{{ item.name }}'s password is `{{ item.password }}`"
      loop: "{{ users_with_passwords }}"
      loop_control:
        label: "{{ item.name }}"

This yields:

TASK [set_fact] *************************************************************
ok: [localhost] => (item=tom)
ok: [localhost] => (item=jerry)
ok: [localhost] => (item=muttley)

TASK [debug] ****************************************************************
ok: [localhost] => (item=tom) => 
  msg: tom's password is `tLY>@jg6k/_|sqke{-mm`
ok: [localhost] => (item=jerry) => 
  msg: jerry's password is `Liu1wF@gPM$q^z~g|<E1`
ok: [localhost] => (item=muttley) => 
  msg: muttley's password is `BGHL_QUTHmbn\(NGW`pJ`

Solution 2:

How can I write a set_fact task to generate random password for each user and save it for later use?

This sound like a task for the password_lookup which "Retrieves or generate a random password, stored in a file", in Example

- name: Create random but idempotent password
  set_fact:
    toms_password: "{{ lookup('password', '/dev/null', seed=username) }}"

How can I then read that and use it in other tasks later?

Just by Using variable name which was defined during set_fact.

Further readings