Reputation: 19258
How can I nest with_filetree and a loop?
Here is my attempt using a block:
- name: Deploy
hosts: all
connection: ssh
become: true
vars:
instances:
abcd:
admin_port: 12345
tasks:
- name: Template cfg
vars:
file_path: "{{ item.path }}"
block:
- name: Templating config files
vars:
instance: "{{ item.value }}"
template:
src: "config-templates/{{ file_path }}"
dest: "{{ install_dir }}/{{ instance.name }}/"
loop: "{{ instances | dict2items }}"
with_filetree: "config-templates"
when: item.state == 'file'
But ansible complains:
ERROR! 'with_filetree' is not a valid attribute for a Block
I must be missing something obvious, but I don't see how to do this. It does not seem to work using with_nested/with_cartesian.
Please help.
Upvotes: 3
Views: 1536
Reputation: 44809
Using include to nest loops is the only viable solution when you have several loops in the include task.
Meanwhile on most scenario including yours, you can reduce the work to a single loop. The basic idea: rather than nesting loops, you create a list with all elements to loop over. In this case, the product
filter should do the job.
The other "trick" (since there are no real examples in the doc for filetree
...) is to remember that a with_<some_lookup>
loop can generally be transformed to loop: {{ lookup|query('some_lookup', args....) }}
.
Putting it all together, here is a (not fully tested) example that should meet your requirement:
- name: Templating from filetree and instances
vars:
file: "{{ item.0 }}"
instance: "{{ item.1.value }}"
template:
src: "config-templates/{{ file.path }}"
dest: "{{ install_dir }}/{{ instance.name }}/"
loop: "{{ lookup('filetree', 'templates/config') | product(instances | dict2items) | list }}"
when: file.state == 'file'
Edit: not trying to push towards "MY !!!" solution :), but if you don't have too many tasks, here is a possible scenario to keep everything in the same file.
Edit2: You actually don't even need a task, especially if your play targets several hosts. Updated with a full example pseudo playbook.
- name: Deploy
hosts: all
connection: ssh
become: true
vars:
my_files: "{{ lookup('filetree', 'templates/config') | selectattr('state', 'eq', 'file') | list }}"
action1_list: ['some', 'list']
action2_list: ['other', 'list']
tasks:
- name: action 1
debug:
msg: action1
vars:
file: item.0
action1_item: item.1
loop: "{{ my_files | product(action1_list) | list }}"
- name: action 2
debug:
msg: action2
vars:
file: item.0
action2_item: item.1
loop: "{{ my_files | product(action2_list) | list }}"
Upvotes: 3
Reputation: 19258
Thanks to Zeitounator's comment, I was able to create a solution. Is has a drawback, however, in that the inner loop task(s) need to be put in a separate file.
tasks:
- name: Template cfg
include_tasks: inner-template-tasks.yaml
with_filetree: "templates/config"
when: item.state == 'file'
inner-template-tasks.yaml would look something like this:
- name: Templating {{ item.path }}
vars:
instance: "{{ loop_item.value }}"
template:
src: "config-templates/{{ item.path }}"
dest: "{{ install_dir }}/{{ instance.name }}/"
loop: "{{ instances | dict2items }}"
loop_control:
loop_var: loop_item
I only wonder why ansible forces us to use a separate file for this...
Upvotes: 1