Ansible: pretty-print elements of a list

Viewed 854

I want to pretty-print each item of a list of results that a previous task has registered. My naïve approach was this:

- hosts: localhost
  gather_facts: no
  tasks:
    - command: "echo number {{item}}"
      with_items: [1, 2, 3]
      register: result
    - debug:
        msg: "=== {{ item.cmd|join(' ') }} ===\n{{ item.stdout|indent(first=true) }}"
      with_items: "{{ result.results }}"

I'm using stdout_callback = debug in ansible.cfg to make the output more human-readable.

The above works almost. The output looks as expected, but unfortunately the debug module logs the full loop item in addition to my msg expression, which garbles the output quite a bit:

TASK [debug] ************************************************************************************************************************************************************
ok: [localhost] => (item={'cmd': ['echo', 'number', '1'], 'stdout': 'number 1', 'stderr': '', 'rc': 0, 'start': '2021-12-20 13:36:29.488443', 'end': '2021-12-20 13:36:29.490032', 'delta': '0:00:00.001589', 'changed': True, 'invocation': {'module_args': {'_raw_params': 'echo number 1', 'warn': True, '_uses_shell': False, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['number 1'], 'stderr_lines': [], 'failed': False, 'item': 1, 'ansible_loop_var': 'item'}) => {}

MSG:

=== echo number 1 ===
    number 1

Setting no_log: true suppresses all output and is thus not helpful. Is there any way to print just the msg?

I've experimented quite a bit, but the only solution that came close to what I had in mind was to do away with the loop in the debug task and use a custom filter plugin to print the items.

    - debug:
        msg: "{{ result.results | map('format_result',  '=== {} ===\n{}') | join('\n\n') }}"

where filter_plugins/format_result.py looks like this:

def format_result(res, pattern):
    return (pattern.format(" ".join(res['cmd']), res['stdout']))

class FilterModule(object):
    def filters(self):
        return {
            'format_result': format_result,
        }

The filter plugin approach does what I want but I was asking myself whether there's no simpler solution.

Update 2021-12-21:

Thanks to @Zeitounator's comment below, I came up with this:

- hosts: localhost
  gather_facts: no
  tasks:
    - command: "echo number {{item}}"
      with_items: [1, 2, 3]
      register: result
      loop_control:
        label: "\nThe item is: {{ result.stdout|indent(first=true) }}\n"

I haven't yet figured out how to access the loop result inside the loop without using register.

Update 2022-02-03:

Trying to summarize what I've learned (thanks everyone and forgive my ignorance, as you could see I'm pretty much an ansible noob):

  • The mistake of my first approach was to ignore that ansible prints a result line for every simple task, and for every iteration for tasks with a loop.
  • A debug task with loop will print a result line for every iteration, followed by the actual message.
  • The lengthy ouput that I wanted to suppress is just this result line, not the output of the debug module itself.
  • For the reasons above, debug tasks with a loop generally don't cut it for pretty-printed output.
  • Thus the loop has to be moved into the template, like in β.εηοιτ.βε's solution, or in my original "custom filter" approach.
1 Answers

You can actually use Jinja rendering as you would use it in a template in a debug task, as suggested in the comment.

Given the playbook:

- hosts: localhost
  gather_facts: no

  tasks:
    - command: "echo number {{ item }}"
      with_items: [1, 2, 3]
      register: result

    - debug:
        msg: >-
          {% for _result in result.results %}
          === {{ _result.invocation.module_args._raw_params }} ===
               {{ _result.stdout }}
          {% endfor %}

This would give your expected otput:

ok: [localhost] => {}

MSG:

 === echo number 1 ===
     number 1
 === echo number 2 ===
     number 2
 === echo number 3 ===
     number 3
Related