The Horrors of Ansible Complex Variables: Lists

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

Save this as lists.yml:

---

- name: lists
  hosts: localhost
  connection: local
  vars:
    # how not to do it:
    users:
      alice
      bob
      charles
      giles

    # this works well:
    files:
      - file_a
      - file_b
      - file_c
      - file_g

    # indentation needs to be precise - this breaks:
    colours:
        - red
        - green
        - blue
          - octarine

    # the variable name cannot contain spaces, but items can:
    Beatles_Albums:
        - "Rubber Soul"
        - "Magical Mystery Tour"
        - "Abbey Road"
        - "A Hard Day's Night"

  tasks:
    - name: print out the users variable via YML
      debug:
        msg: '{{ item }}'
      with_items: '{{ users }}'

    - name: print out the files variable via YML
      debug:
        msg: '{{ item }}'
      with_items: '{{ files }}'

    - name: print out the colours variable via YML
      debug:
        msg: '{{ item }}'
      with_items: '{{ colours }}'

    - name: print out the Beatles albums via YML
      debug:
        msg: '{{ item }}'
      with_items: '{{ Beatles_Albums }}'

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

Save this template file as template.j2 in the same folder as the Ansible script:

{% for user in users %}
{{ user }}
{% endfor %}

delete: {% for file in files %}{{ file }}, {% endfor %}

watch for: {% for colour in colours %}{{ colour }}, {% endfor %}

listen to: {% for album in "Beatles_Albums" %}{{ album }}, {% endfor %}

listen to: {{ Beatles_Albums|join(', ') }}

The output of the run:

$ ansible-playbook lists.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 [print out the users variable via YML] ************************************************
ok: [localhost] => (item=alice bob charles giles) => {
    "msg": "alice bob charles giles"
}

TASK [print out the files variable via YML] ************************************************
ok: [localhost] => (item=file_a) => {
    "msg": "file_a"
}
ok: [localhost] => (item=file_b) => {
    "msg": "file_b"
}
ok: [localhost] => (item=file_c) => {
    "msg": "file_c"
}
ok: [localhost] => (item=file_g) => {
    "msg": "file_g"
}

TASK [print out the colours variable via YML] **********************************************
ok: [localhost] => (item=red) => {
    "msg": "red"
}
ok: [localhost] => (item=green) => {
    "msg": "green"
}
ok: [localhost] => (item=blue - octarine) => {
    "msg": "blue - octarine"
}

TASK [print out the Beatles albums via YML] ************************************************
ok: [localhost] => (item=Rubber Soul) => {
    "msg": "Rubber Soul"
}
ok: [localhost] => (item=Magical Mystery Tour) => {
    "msg": "Magical Mystery Tour"
}
ok: [localhost] => (item=Abbey Road) => {
    "msg": "Abbey Road"
}
ok: [localhost] => (item=A Hard Day's Night) => {
    "msg": "A Hard Day's Night"
}

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

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

And the resulting template.txt file:

a
l
i
c
e

b
o
b

c
h
a
r
l
e
s

g
i
l
e
s

delete: file_a, file_b, file_c, file_g,
watch for: red, green, blue - octarine,
listen to: B, e, a, t, l, e, s, _, A, l, b, u, m, s,
listen to: Rubber Soul, Magical Mystery Tour, Abbey Road, A Hard Day's Night

Conclusions

  • you need dashes to make a list: both Ansible and Jinja2 templates misinterpret a list without dashes - although in different ways
  • if you mis-indent something, you're in trouble - this doesn't seem unreasonable (so many other things about Ansible variables are)
  • don't quote variable names in a Jinja2 template - they're then treated as a string
  • for making a comma-separated list, the Jinja2 join filter is both more concise and produces better results (no trailing comma): {{ Beatles_Albums|join(', ') }}