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)?

Slava Fomin II
  • 1,701
  • 4
  • 17
  • 23

4 Answers4

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
Christopher Karel
  • 6,582
  • 1
  • 28
  • 34
  • 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 08 '14 at 16:39
  • Solid logic. Looks like `shell` is your best bet with ACLs and recursion. – Christopher Karel Jul 08 '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 08 '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 08 '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   
chicks
  • 3,793
  • 10
  • 27
  • 36
Egidijus
  • 109
  • 1
  • 4
2

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]

slm
  • 7,615
  • 16
  • 56
  • 76
Max Murphy
  • 151
  • 3
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
user42826
  • 101