15

I have the following variable loaded via include_vars:

access:
    username-foo:
      - path: /
        permissions: rwX
        recursive: true

    username-bar:
      - path: /
        permissions: rX

      - path: /css
        permissions: rwX
        recursive: true

      - path: /data
        permissions: rX

      - path: /data/reviews.yml
        permissions: rw

      - path: /js
        permissions: rX

      - path: /js/*.js
        permissions: rw

I want to feed this information to the shell command in order to set appropriate permissions.

I've tried some techniques from here: http://docs.ansible.com/playbooks_loops.html but failed to come up with working solution.

Is it possible to iterate this structure? If not, how do I re-structure it in order to make it work? Is it possible to do this without breaking the DRY rule (e.g. include username into every record)?

21

First off, you might want to consider using the file module, rather than shell. It's less failure prone, and ostensibly idempotent. However, that might give you some issues with mixing directories, files, and file globs. YMMV.

As for the heart of the question, I would set up your variables like so:

users:
  - username: bar
    directories:
      - path: /data
        permissions: rX
      - path: /js
        permissions: rX
  - username: foo
    directories:
      - path: /
        permissions: rwX

The play would then look like this:

- name: Change mod/own
  shell: chown {{ item.0.username }} {{ item.1.path }};chmod u+{{ item.1.permissions }} {{ item.1.path }
  with_subelements:
    - users
    - directories
  • Great idea! Thanks! Works like a charm. BTW I'm using shell module because I need to do recursive ACL and that is not supported by the acl module. – Slava Fomin II Jul 8 '14 at 16:39
  • Solid logic. Looks like shell is your best bet with ACLs and recursion. – Christopher Karel Jul 8 '14 at 16:44
  • BTW is it possible to ignore missing hash key like recursive in my example? When I try to access it and it's missing, Ansible will stop the execution of the playbook and throw an exception. I prefer not to add recursive: false to every record. – Slava Fomin II Jul 8 '14 at 17:11
  • 1
    I think the default syntax should work: {{ some_variable | default() }}. So, in this case: {{ item.1.recursive | default(false) }} – Christopher Karel Jul 8 '14 at 17:51
  • How would the task change if the "directories" key is just a list instead of a dictionary also? – Chris F Jan 29 '18 at 21:36
7

This is a good output example that you can try yourself. Create a new playbook called iteration_loop.yml:

---

- name: Change mod/own
  hosts: all
  tasks:
  - name: show me the iterations
    debug: msg={{ item.0.username }} {{ item.1.path }} then {{ item.1.permissions }} {{ item.1.path }}
    with_subelements:
      - users
      - directories
  vars:
    users:
      - username: bar
        directories:
          - path: /data
            permissions: rX
          - path: /js
            permissions: rX
      - username: foo
        directories:
          - path: /
            permissions: rwX

Then run the playbook like this: ansible-playbook -i '172.16.222.131,' iteration_loop.yml

and the output should give you how the items are accessed:

PLAY [Change mod/own] ********************************************************* 

GATHERING FACTS *************************************************************** 
ok: [172.16.222.131]

TASK: [show me the iterations] ************************************************ 
ok: [172.16.222.131] => (item=({'username': 'bar'}, {'path': '/data', 'permissions': 'rX'})) => {
    "item": [
        {
            "username": "bar"
        }, 
        {
            "path": "/data", 
            "permissions": "rX"
        }
    ], 
    "msg": "bar"
}
ok: [172.16.222.131] => (item=({'username': 'bar'}, {'path': '/js', 'permissions': 'rX'})) => {
    "item": [
        {
            "username": "bar"
        }, 
        {
            "path": "/js", 
            "permissions": "rX"
        }
    ], 
    "msg": "bar"
}
ok: [172.16.222.131] => (item=({'username': 'foo'}, {'path': '/', 'permissions': 'rwX'})) => {
    "item": [
        {
            "username": "foo"
        }, 
        {
            "path": "/", 
            "permissions": "rwX"
        }
    ], 
    "msg": "foo"
}

PLAY RECAP ******************************************************************** 
172.16.222.131             : ok=2    changed=0    unreachable=0    failed=0   
1

Assuming that dict={a:[1,2,3],b:[1,2]} and so on:

- name: Flattened list
  set_fact:
    flattened: "{{ dict.values() | sum(start=[]) }}"

Now flattened == [1,2,3,1,2]

0

I will reformat your vars to below format:

access:
- username: foo
  directories:
    - path: /
      permissions: rwX
      recursive: true

- username: bar
  directories:
    - path: /
      permissions: rX
      recursive: false

    - path: /css
      permissions: rwX
      recursive: true

    - path: /data
      permissions: rX
      recursive: false

    - path: /data/reviews.yml
      permissions: rw
      recursive: false

    - path: /js
      permissions: rX
      recursive: false

    - path: /js/*.js
      permissions: rw
      recursive: false

and then my playbook as below:

tasks:
- name: Iterate the vars inside var4 when recursive
  debug: msg="username is {{ item.0.username }} and path is {{ item.1.path }} permission is {{ item.1.permissions }} and recursive"
  when: item.1.recursive
  ignore_errors: true
  with_subelements:
    - "{{ access }}"
    - directories
- name: Iterate the vars inside var4 when no recursive
  debug: msg="username is {{ item.0.username }} and path is {{ item.1.path }} permission is {{ item.1.permissions }}"
  when: not item.1.recursive
  ignore_errors: true
  with_subelements:
    - "{{ access }}"
    - directories

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.