When working with Ansible, we often encounter complex data structures that need to be processed in playbooks. The example shows a common scenario where we have:
access:
username-foo:
- path: /
permissions: rwX
recursive: true
username-bar:
- path: /
permissions: rX
# ... more entries ...
The most elegant way to handle this in Ansible is using the with_subelements
loop construct. Here's how to implement it:
- name: Set filesystem permissions
shell: |
setfacl -R -m u:{{ item.0.key }}:{{ item.1.permissions }} {{ item.1.path }}
{% if item.1.recursive is defined and item.1.recursive %}
setfacl -R -m d:u:{{ item.0.key }}:{{ item.1.permissions }} {{ item.1.path }}
{% endif %}
with_subelements:
- "{{ access | dict2items }}"
- value
loop_control:
label: "{{ item.0.key }}:{{ item.1.path }}"
For more control over the iteration, we can use dict2items
filter combined with nested loops:
- name: Process permissions
include_tasks: process_permission.yml
loop: "{{ access | dict2items }}"
loop_control:
loop_var: user_entry
# In process_permission.yml
- name: Set permissions for each path
shell: |
setfacl -m u:{{ user_entry.key }}:{{ path_item.permissions }} {{ path_item.path }}
loop: "{{ user_entry.value }}"
loop_control:
loop_var: path_item
For more complex scenarios where you need additional processing:
- name: Advanced permission handling
block:
- name: Debug user permissions
debug:
msg: "User {{ user }} will get {{ perm.permissions }} on {{ perm.path }}"
loop: "{{ access[user] }}"
loop_control:
loop_var: perm
vars:
user: "{{ item }}"
with_items: "{{ access.keys() | list }}"
If the nested structure becomes too complex, consider flattening it:
flattened_access:
- user: username-foo
path: /
permissions: rwX
recursive: true
- user: username-bar
path: /
permissions: rX
# ... more entries ...
- name: Process flattened structure
shell: setfacl -m u:{{ item.user }}:{{ item.permissions }} {{ item.path }}
loop: "{{ flattened_access }}"
When dealing with complex permission structures in Ansible, we often encounter nested dictionaries containing lists. The example shows a common scenario for managing filesystem access:
access:
username-foo:
- path: /
permissions: rwX
recursive: true
username-bar:
- path: /
permissions: rX
- path: /css
permissions: rwX
recursive: true
The optimal way to handle this structure is using Ansible's with_subelements
lookup combined with dict2items
filter:
- name: Set filesystem permissions
ansible.builtin.shell: |
setfacl -R -m u:{{ item.0.key }}:{{ item.1.permissions }} {{ item.1.path }}
loop: "{{ access | dict2items | subelements('value') }}"
when: item.1.path is defined
For paths requiring recursive permission changes, we can add conditional logic:
- name: Set recursive permissions
ansible.builtin.shell: |
{% if item.1.recursive | default(false) %}
setfacl -R -m u:{{ item.0.key }}:{{ item.1.permissions }} {{ item.1.path }}
{% else %}
setfacl -m u:{{ item.0.key }}:{{ item.1.permissions }} {{ item.1.path }}
{% endif %}
loop: "{{ access | dict2items | subelements('value') }}"
For more complex scenarios, consider using Jinja2 templates to generate the commands:
- name: Generate permission commands
ansible.builtin.template:
src: permissions.j2
dest: /tmp/set_permissions.sh
mode: '0755'
# permissions.j2 content:
{% for user,paths in access.items() %}
{% for entry in paths %}
{% if entry.recursive | default(false) %}
setfacl -R -m u:{{ user }}:{{ entry.permissions }} {{ entry.path }}
{% else %}
setfacl -m u:{{ user }}:{{ entry.permissions }} {{ entry.path }}
{% endif %}
{% endfor %}
{% endfor %}
Always include validation in your playbook to catch potential issues:
- name: Validate permission entries
ansible.builtin.assert:
that:
- "'path' in item.1"
- "'permissions' in item.1"
fail_msg: "Missing required fields in permission entry"
loop: "{{ access | dict2items | subelements('value') }}"