Ansible Tips & Tricks - Improving readability of task output

I am a big fan of Ansible automation: its simplicity, the ease of developing and testing playbooks, the fact that you can run playbooks on any machine - there is no dedicated server installation required.

But there is one thing I really dont like about Ansible: the readability of its output, or better, its lack thereof.
Ansible can be quite verbose, which is perfect while developing and debugging playbooks. But during regular playbook runs that verbosity is cluttering the screen and making the output a bit cumbersome to read for human eyes. Especially when you use any loop construct, Ansible will print out the entire data structure while happily looping through data. This, again, is handy for small data, but will make your eyes water when working with larger data sets.

Fortunately there are a few things we can do about that:

  1. override the default message that gets printed out for each loop-iteration
  2. reduce the data required for the loop

Definitions

For the sake of demonstrating these techniques, we need to define Ansible variables and a simple playbook showing the effects of our improvements.

Variables and playbook

Here we define our variables. We define a list of dictionaries, where each list item describes an minion object which owns a bunch of properties, ie. name/value-pairs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
- name: Minion demo playbook
  hosts: localhost
  gather_facts: no

  vars:
    minions:
      - name: Dave
        loves: banana
        numberOfEyes: 1
        outfit: "blue trousers"
        hair: spiky

      - name: Stuart
        loves: even more bananas
        numberOfEyes: 2
        outfit: "golfer"
        hair: center-parted

      - name: Kevin
        loves: Gru
        numberOfEyes: 2
        outfit: "french maid"
        hair: a tiny clump

  tasks:
    - name: 01 - standard loop over list of minions
      debug:
        msg: |
                    Meet minion {{ item.name }} !
      loop: "{{ minions }}"


    - name: 02 - override loop item label
      debug:
        msg: |
                    Meet minion {{ item.name }} !
      loop: "{{ minions  }}"
      loop_control:
        label: "processing minion: {{ item.name }}"


    - name: 03 - loop over minion names (extracted/mapped)
      debug:
        msg: |
                    Meet minion {{ item }} !
      loop: "{{ minions | map(attribute='name') | list }}"

Improving loop readability

When run, output of task 01 will look like shown below. The entire, large Json data structure is printed for each loop iteration. The message line is almost lost in the middle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
PLAY [Minion demo playbook] ***************************************************

TASK [01 - standard loop over list of minions] ********************************
Tuesday 16 April 2019  23:22:18 +0200 (0:00:00.091)       0:00:00.091 *********
ok: [localhost] => (item={u'hair': u'spiky', u'loves': u'banana', u'name': u'Dave', u'numberOfEyes': 1, u'outfit': u'blue trousers'}) =>
  msg: |-
        Meet minion Dave !
ok: [localhost] => (item={u'hair': u'center-parted', u'loves': u'even more bananas', u'name': u'Stuart', u'numberOfEyes': 2, u'outfit': u'golfer'}) =>
  msg: |-
        Meet minion Stuart !
ok: [localhost] => (item={u'hair': u'a tiny clump', u'loves': u'Gru', u'name': u'Kevin', u'numberOfEyes': 2, u'outfit': u'french maid'}) =>
  msg: |-
        Meet minion Kevin !

Overriding the loop label

The next best thing to do is override the loop label. This is shown in task-02, which then changes the output as shown below. Much better!

Task-02:

1
2
3
   loop: "{{ minions  }}"
   loop_control:
      label: "processing minion: {{ item.name }}"

Output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
TASK [02 - override loop item label] *****************************************
Tuesday 16 April 2019  23:22:18 +0200 (0:00:00.063)       0:00:00.155 ********
ok: [localhost] => (item=processing minion: Dave) =>
  msg: |-
        Meet minion Dave !
ok: [localhost] => (item=processing minion: Stuart) =>
  msg: |-
        Meet minion Stuart !
ok: [localhost] => (item=processing minion: Kevin) =>
  msg: |-
        Meet minion Kevin !

Extracting the required key(s) from dictionary

Another option is to extract (map) the attribute we actually require in the loop. This is shown in task-03 using the Jinja filter map(attribute='xxx'). Using the map filter makes the expression a bit unwieldy (and twice so when you require more than one attribute!), but it works like a charm nevertheless: the <item> in the loop label now consist of only the values of “name” property.

loop: "{{ minions | map(attribute='name') | list }}"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
TASK [03 - loop over minion names (extracted/mapped)] *************************
Tuesday 16 April 2019  23:33:06 +0200 (0:00:00.056)       0:00:00.208 *********
ok: [localhost] => (item=Dave) =>
  msg: |-
        Meet minion Dave !
ok: [localhost] => (item=Stuart) =>
  msg: |-
        Meet minion Stuart !
ok: [localhost] => (item=Kevin) =>
  msg: |-
        Meet minion Kevin !
updatedupdated2019-04-182019-04-18