'Nested loop with user and folder in Ansible

I have the following task:

- name: Create required folders.
  become: true
  ansible.builtin.file:
    owner: "{{ item.key }}"
    group: ftp
    mode: '0755'  
    path: '/data/{{ item.key }}/in'
    state: directory
  loop: "{{ query('dict', ftp) | list }}"
  when: "'state' not in item.value or item.value.state == 'present'"

And the following host variables with different users:

ftp:
  test:
     ssh_public_key: "XXXX"
     password: "XXX" 
     home: /data/test
  test2:
     ssh_public_key: "XXXX"
     password: "XXX" 
     home: /data/test2

What I want is to create two directories for every user :

path: '/data/{{ user }}/in' # item.key, in the code above
path: '/data/{{ user }}/out' # item.key, in the code above

But I already need the loop for iterating over the users itself:

loop: "{{ query('dict', ftp) | list }}"

How can I handle this, for example, with nested loop?



Solution 1:[1]

Test it first, for example

    - debug:
        msg: "Create /data/{{ item.0.key }}/{{ item.1 }}"
      with_nested:
        - "{{ ftp|dict2items }}"
        - [in, out]
      when: item.0.value.state|d('present') == 'present'

gives (abridged)

  msg: Create /data/test/in
  msg: Create /data/test/out
  msg: Create /data/test2/in
  msg: Create /data/test2/out

Then try to create the dictionaries

    - file:
        owner: "{{ item.0.key }}"
        group: ftp
        mode: '0755'  
        path: "/data/{{ item.0.key }}/{{ item.1 }}"
        state: directory
      with_nested:
        - "{{ ftp|dict2items }}"
        - [in, out]
      when: item.0.value.state|d('present') == 'present'

(not tested)

Solution 2:[2]

Use a product filter to generate every possible combination of user/folder.

loop: "{{ ftp.keys() | product(['in', 'out']) }}"

Then, respectively,

  • item.0 contains the users dictionary keys
  • item.1 contains the folders

It is not fully clear what your condition when does actually, in order to adapt it too, but I guess that you do have an absent or present state in those use dictionaries.

So, the resulting task should be something along the lines of

- name: Create required folders
  ansible.builtin.file:
    owner: "{{ item.0 }}"
    group: ftp
    mode: '0755'
    path: "/data/{{ item.0 }}/{{ item.1 }}"
    state: directory
  loop: "{{ ftp.keys() | product(['in', 'out']) }}"
  loop_control:
    label: "/data/{{ item.0 }}/{{ item.1 }}"
  when: "ftp[item.0].state | default('absent') == 'present'"
  become: true 

Given the task above, when run on those data:

ftp:
  test:
    state: present
  test1:
  test2:
    state: present

It will yield:

TASK [Create required folders] ***************************************
ok: [localhost] => (item=/data/test/in)
ok: [localhost] => (item=/data/test/out)
skipping: [localhost] => (item=/data/test1/in) 
skipping: [localhost] => (item=/data/test1/out) 
ok: [localhost] => (item=/data/test2/in)
ok: [localhost] => (item=/data/test2/out)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vladimir Botka
Solution 2