using register stdout_lines in with_items loop

Hello All,

I’m ultimately attempting to pull a list of files with wildcarded paths and pass the results into the replace module so it can cycle through them all. However, starting with a more simple example, i’m having issues getting the list of files to print properly in even a simple test case.

Pastebin code:
http://pastebin.com/zFRzucat

I’m looking for this:

test.yml playbook

  • hosts: all
    tasks:

  • name: gather list of files
    shell: ls {{ item }}
    register: files
    with_items:

  • /app/psoft/test/*/list.txt

  • /app/psoft/test/*/context.xml

  • name: use shell to print list of file paths
    shell: “echo {{ item }}”
    with_items: “{{files.stdout_lines}}”

to print

/app/psoft/test/12.1.2.00/list.txt
/app/psoft/test/12.1.3.00/context.xml

However currently the result is:

TASK [gather list of files] ****************************************************
changed: [net12204] => (item=/app/psoft/test//list.txt)
changed: [net12204] => (item=/app/psoft/test/
/context.xml)

TASK [use shell to print list of file paths] ***********************************
[DEPRECATION WARNING]: Skipping task due to undefined attribute, in the future this will be a fatal error… This feature will
be removed in a future release. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.

Am I misusing or not understanding http://docs.ansible.com/ansible/playbooks_loops.html#iterating-over-the-results-of-a-program-execution properly?

Thanks!
Jason

So after some testing and reading i’m gonna attempt to answer my own question, and pose another one.

The documentation I cited for iterating the results of a program execution didn’t involve the use of with_items in the original program execution. Having multiple results in a register variable doesn’t seem to play nice with ‘with_items’.

I decided to work around the issue by having multiple tasks to gather the file names and iterate over each result in the replace module.

As a corollary to that, when using with_items and setting a failed_when condition, you can’t seem to use the ‘rc’ property of the register variable in a task because the rc code only exists if the task fails. I’m thinking there must be a way to say fail_when: the register.rc exists, but haven’t figured that part out yet.

The “problem” is in how ansible registers results when using with_items. It nests all of the results inside of a results key

Your second task could have been written to use:

  • name: use shell to print list of file paths
    shell: “echo {{ item }}”
    with_items: “{{ files.results|map(attribute=‘stdout_lines’)|list }}”

That would do it for you. That pulls the stdout_lines attribute out into a list of stdout_lines, then with_items natively handles the flattening, allowing you to loop over each individual item in the ls output.

There is some documentation about using register with a loop, which you can find at http://docs.ansible.com/ansible/playbooks_loops.html#using-register-with-a-loop

Matt -

I don’t know how you figured out how to do this (I would love to know), but it should DEFINITELY be in the documentation. It’s not pretty, but it works and it solved my issue. It’s definitely easier than breaking it into multiple tasks or having to populate a list manually.

- name: get list of directories
  shell: "egrep '^archive.directory|^outgoing.directory|^incoming.directory' {{conf_dir}}/{{item.1.config}}|cut -d '=' -f2|sort|uniq"
  with_subelements:
    - "{{loggers}}"
    - configs
  register: directoriesToBuild

- name: debug directory output
  debug:
    msg: "{{directoriesToBuild.results|map(attribute='stdout_lines')|list}}"

- name: build found directories
  file:
    state: directory
    path: "{{item}}"
  with_items:
    - "{{directoriesToBuild.results|map(attribute='stdout_lines')|list}}"

-Adam

I was over zealous & too hopeful… this doesn’t actually work for me. It looks like it treats the whole list of directories as one big directory. I’m not entirely sure why yet.

After spending several hours trying different combinations of tasks, filters, combinations of filters, etc, I decided I spent too much time trying to do it all in memory. Here was my final solution that works:

- name: make sure temp file doesn't exist
  file:
    state: absent
    path: "{{appdataRoot}}/directoriesToBuild.tmp"

- name: get list of directories to build
  shell: "egrep '^archive.directory|^outgoing.directory|^incoming.directory' {{conf_dir}}/{{item.1.config}}|cut -d '=' -f2|sort|uniq  >> {{appdataRoot}}/directoriesToBuild.tmp"
  with_subelements:
    - "{{loggers}}"
    - configs

- name: put list of directories into a variable
  shell: "cat {{appdataRoot}}/directoriesToBuild.tmp"
  register: directoriesToBuild

- name: build found directories
  file:
    state: directory
    path: "{{item}}"
  with_items:
    - "{{directoriesToBuild.stdout_lines}}"

This page was the starting point of me figuring out how to print the ‘results[*].stdout_lines’ of a registered return value. So, I thought I ought to share what I found to work, as it’s quite short and re-usable with any return value that produces a list. It also provides flexibility to add more fields to the output.

If ‘stdout_lines’ is the only variable in the select, the resulting array will be all 1 liners, rather than grouped by result. ‘item’ within each result in the list displays the item name provided when the list was produced.

  • name: Show ‘item’ & ‘stdout_lines’ per result
    debug: var=item
    loop: “{{ result | json_query(‘results[*].{item: item, stdout_lines: stdout_lines}’) }}”

  • name: Only show ‘stdout_lines’ per result

debug: var=item.stdout_lines
loop: “{{ result | json_query(‘results[*].{item: item, stdout_lines: stdout_lines}’) }}”

  • name: Use shell to print ‘stdout_lines’ per result
    shell: “echo {{ item.stdout_lines }}”
    loop: “{{ result | json_query(‘results[*].{item: item, stdout_lines: stdout_lines}’) }}”

  • name: Use shell to print flattened ‘stdout_lines’
    shell: “echo {{ item }}”
    loop: “{{ result | json_query(‘results[*].{stdout_lines: stdout_lines}’) }}”

Hopefully someone finds this useful.