The Horrors of Ansible Complex Variables: Dictionaries According to Giles

This is part of the series "The Horrors of Ansible Complex Variables." The results below were achieved with Ansible version 2.8.0.

My attempt to find a working complex variable structure understood by both Ansible and Jinja2 led to this:

---
# filename: dictionary.yml

- name: lists
  hosts: localhost
  connection: local
  vars:
    users:
        - { username: alice, fullname: "Alice Appleworth" }
        - { username: bob,   fullname: "Bob Bananarama"   }

  tasks:
    - name: add several users
      debug:
        msg: "{{ item.name }} {{ item.groups }}"
      loop:
        - { name: 'testuser1', groups: 'wheel' }
        - { name: 'testuser2', groups: 'root' }

    - name: Print users (full object)
      debug:
        msg: "{{ item }}"
      loop: "{{ users }}"

    - name: Print users (detailed)
      debug:
        msg: "User {{ item.username }} is {{ item.fullname }}"
      loop: "{{ users }}"

    - name: template with the dictionary
      template:
        src=template.j2
        dest=template.txt

That's not strictly a dictionary. I suspect that what it is is a list of lists of dictionary items ... But I'm more concerned that it works than what exactly the structure is.

UPDATE: I used loop: above: with_items: will also work (although Ansible's documentation seems to indicate that loop: is the future). with_dict: does NOT work.

Here's the template that goes with it:

{# filename: template.j2 #}

List of complete "user" variables:
{% for user in users %}
{{ user }}
{% endfor %}

Showing "user.username, user.fullname":
{% for user in users %}
{{ user.username }}, {{ user.fullname }}
{% endfor %}

Run the playbook:

$ ansible-playbook dictionary.yml
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the
implicit localhost does not match 'all'


PLAY [lists] *****************************************************************************

TASK [Gathering Facts] *******************************************************************
ok: [localhost]

TASK [add several users] *****************************************************************
ok: [localhost] => (item={'name': 'testuser1', 'groups': 'wheel'}) => {
    "msg": "testuser1 wheel"
}
ok: [localhost] => (item={'name': 'testuser2', 'groups': 'root'}) => {
    "msg": "testuser2 root"
}

TASK [Print users (full object)] *********************************************************
ok: [localhost] => (item={'username': 'alice', 'fullname': 'Alice Appleworth'}) => {
    "msg": {
        "fullname": "Alice Appleworth",
        "username": "alice"
    }
}
ok: [localhost] => (item={'username': 'bob', 'fullname': 'Bob Bananarama'}) => {
    "msg": {
        "fullname": "Bob Bananarama",
        "username": "bob"
    }
}

TASK [Print users (detailed)] ************************************************************
ok: [localhost] => (item={'username': 'alice', 'fullname': 'Alice Appleworth'}) => {
    "msg": "User alice is Alice Appleworth"
}
ok: [localhost] => (item={'username': 'bob', 'fullname': 'Bob Bananarama'}) => {
    "msg": "User bob is Bob Bananarama"
}

TASK [template with the dictionary] ******************************************************
ok: [localhost]

PLAY RECAP *******************************************************************************
localhost                  : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

And look at the resulting template.txt file:

List of complete "user" variables:
{'username': 'alice', 'fullname': 'Alice Appleworth'}
{'username': 'bob', 'fullname': 'Bob Bananarama'}

Showing "user.username, user.fullname":
alice, Alice Appleworth
bob, Bob Bananarama

This is exactly what I wanted: a variable form that's easy to read and easy to reference in Ansible and Jinja2. In a practical sense, I needed to add some complexity: I wanted to be able to add a list to each user so that I could choose what groups they were members of. See the next entry.