ansible

How can I nest a with_filetree in Ansible?


I'm using Ansible to push config files for various apps (based on group_names) & need to loop through the config .j2 templates from a list variable. If I use a known list of config templates I can use a standard with_nested like this...

template:
  src: '{{ playbook_dir }}/templates/{{ item[1] }}/configs/{{ item[0] }}.j2'
  dest: /path/to/{{ item[1] }}/configs/{{ item[0] }}
with_nested:
  - ['file.1', 'file.2', 'file.3', 'file.4']
  - '{{ group_names }}'

However, since each app will have its own configs I can't use a common list for a with_nested. Every attempt to somehow use with_filetree nested fails. Is there any way to nest a with_filetree? Am I missing something painfully obvious?


Solution

  • The most straightforward way to deal with this is probably to imbricate loops through an include. I take for granted that your app directory only contains .j2 files. Adapt if this is not the case.

    In e.g. push_templates.yml

    ---
    - name: Copy templates for group {{ current_group }}
      template:
        src: "{{ item.src }}"
        dest: /path/to/{{ current_group }}/configs/{{ (item.src | splitext).0 | basename }}
      with_filetree: "{{ playbook_dir }}/templates/{{ current_group }}"
      # Or using the latest loop syntax
      # loop: "{{ query('filetree', playbook_dir + '/templates/' + current_group) }}"
      when: item.src is defined
    

    Note: on the dest line, I am removing the last found extension of the file and getting its name only without the leading directory path. Check the ansible doc on filters for splitext and basename for more info

    Then in your e.g. main.yml

    - name: Copy templates for all groups
      include_tasks: push_templates.yml
      loop: "{{ group_names }}"
      loop_control:
        loop_var: current_group
    

    Note the loop_var in the control section to disambiguate the possible item overlap in the included file. The var name is of course aligned with the one I used in the above included file. See the ansible loops documentation for more info.


    An alternative approach to the above would be to construct your own data structure looping over your groups with set_fact and calling the filetree lookup on each iteration (see example above with the newer loop syntax), then loop over your custom data structure to do the job.