Hafdhi Atef
Hafdhi Atef

Reputation: 67

Ansible : Recursively change ownership Except for a specific folder

im new to ansible , i have this task that on deployment , it changes the ownership of the application files Recursively.

- name: Recursively change ownership
file:
  path: "{{ $PATH }}"
  state: directory
  recurse: yes
  owner: "{{ $USER }}"
  group: "{{ $USER }}"
notify:
  - Restart $service
when: deploy == "true"  

I have a specific app folder binded to aws EFS , that's shared between different apps and i don't want to change the ownership for this folder. I tried to add :

  fail : 
   when : ' "/$specific_folder " in {{ $PATH }} '

but it fails the whole task , also i can't remove the recurse because it fails the app to access other files. Any ideas ?

Upvotes: 0

Views: 7842

Answers (3)

Vladimir Botka
Vladimir Botka

Reputation: 68144

Given the tree

shell> pwd
/scratch/tmp8/test-992
shell> tree apps
apps
├── app1
│   └── app
├── app2
│   └── app
├── app3
│   └── app
├── file1
└── file2
shell> ls -la apps | grep app
drwxrwxr-x 2 user1 user1 4096 Sep  5 13:55 app1
drwxrwxr-x 2 user1 user1 4096 Sep  5 13:55 app2
drwxrwxr-x 2 user1 user1 4096 Sep  5 13:55 app3
shell> find apps -type f | xargs ls -la
-rw-rw-r-- 1 user1 user1 0 Sep  5 13:55 apps/app1/app
-rw-rw-r-- 1 user1 user1 0 Sep  5 13:55 apps/app2/app
-rw-rw-r-- 1 user1 user1 0 Sep  5 13:55 apps/app3/app
-rw-r--r-- 1 user1 user1 0 Sep  5 17:51 apps/file1
-rw-r--r-- 1 user1 user1 0 Sep  5 17:51 apps/file2

Declare the variables

user: user2
path: apps
exclude: [app2, app3]

root: "{{ playbook_dir }}"
root_path: "{{ root }}/{{ path }}"

Find the files and directories

    - name: Find any
      find:
        path: "{{ root_path }}"
        file_type: any
        recurse: true
      register: any

and declare the variables

dirs_exclude: "{{ [root_path]|product(exclude)|map('join', '/')|list }}"
any_select: "{{ any.files|rejectattr('path', 'regex', dirs_exclude|join('|')) }}"

Change the ownership

    - name: Change ownership of dirs and files
      file:
        state: "{{ item.isdir|ternary('directory', 'file') }}"
        path: "{{ item.path }}"
        owner: "{{ user }}"
        group: "{{ user }}"
      loop: "{{ any_select }}"
      when: item.isdir or item.isreg

Example of a complete playbook for testing

shell> cat pb.yml
- hosts: localhost
  become: true

  vars:

    user: user2
    path: apps
    exclude: [app2, app3]

    root: "{{ playbook_dir }}"
    root_path: "{{ root }}/{{ path }}"

    dirs_exclude: "{{ [root_path]|product(exclude)|map('join', '/')|list }}"
    any_select: "{{ any.files|rejectattr('path', 'regex', dirs_exclude|join('|')) }}"

  tasks:

    - name: Find any
      find:
        path: "{{ root_path }}"
        file_type: any
        recurse: true
      register: any

    - name: Change ownership of dirs and files
      file:
        state: "{{ item.isdir|ternary('directory', 'file') }}"
        path: "{{ item.path }}"
        owner: "{{ user }}"
        group: "{{ user }}"
      loop: "{{ any_select }}"
      loop_control:
        label: "{{ item.path }}"
      when: item.isdir or item.isreg

Run the playbook in check and diff mode to see what will happen

shell> ansible-playbook pb.yml --check --diff

PLAY [localhost] *****************************************************************************

TASK [Find any] ******************************************************************************
ok: [localhost]

TASK [Change ownership of dirs and files] ****************************************************
--- before
+++ after
@@ -1,3 +1,3 @@
-group: 1003
-owner: 1003
+group: 1004
+owner: 1004
 path: /export/scratch/tmp8/test-992/apps/file2

changed: [localhost] => (item=/export/scratch/tmp8/test-992/apps/file2)
--- before
+++ after
@@ -1,3 +1,3 @@
-group: 1003
-owner: 1003
+group: 1004
+owner: 1004
 path: /export/scratch/tmp8/test-992/apps/file1

changed: [localhost] => (item=/export/scratch/tmp8/test-992/apps/file1)
--- before
+++ after
@@ -1,3 +1,3 @@
-group: 1003
-owner: 1003
+group: 1004
+owner: 1004
 path: /export/scratch/tmp8/test-992/apps/app1

changed: [localhost] => (item=/export/scratch/tmp8/test-992/apps/app1)
--- before
+++ after
@@ -1,3 +1,3 @@
-group: 1003
-owner: 1003
+group: 1004
+owner: 1004
 path: /export/scratch/tmp8/test-992/apps/app1/app

changed: [localhost] => (item=/export/scratch/tmp8/test-992/apps/app1/app)

PLAY RECAP ***********************************************************************************
localhost: ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Upvotes: 2

Khaled
Khaled

Reputation: 838

The file module doesn't recognise subdirectories:

/tmp/testdir
├── folder1
├── folder2
└── specific_folder
- name: Recursively change ownership
  file:
    path: /tmp/testdir/
    state: directory
    recurse: yes
    owner: "{{ user }}"
    group: "{{ user }}"
changed: [localhost] => {
    "changed": true,
    "diff": {
        "after": {
            "path": "/tmp/testdir/"
        },
        "before": {
            "path": "/tmp/testdir/"
        }
    },
    "gid": 0,
    "group": "root",
    "invocation": {
        "module_args": {
            "owner": "root",
            "path": "/tmp/testdir/",
            "recurse": true,
            "state": "directory",
        }
    },
    "mode": "0755",
    "owner": "root",
    "path": "/tmp/testdir/",
    "size": 4096,
    "state": "directory",
    "uid": 0
}

So we should find a way to get a list of subdirectories, which is find module

---
- hosts: localhost
  gather_facts: no
  vars:
    user: root
    path: /tmp/testdir/
  tasks:
  - name: list directories
    find:
      paths: "{{ path }}"
      file_type: any
    register: directories

Output(shrinked):

ok: [localhost] => {
  "changed": false,
  "examined": 3,
  "files": [
      {
          "mode": "0755",
          "path": "/tmp/testdir/folder1",
          "pw_name": "root",
      },
      {
          "mode": "0755",
          "path": "/tmp/testdir/folder2",
          "pw_name": "root",
      },
      {
          "mode": "0755",
          "path": "/tmp/testdir/specific_folder",
          "pw_name": "root",
      }
  ],
  "invocation": {
  },
  "matched": 3,
  "msg": "All paths examined",
  "skipped_paths": {}

Now we got the output as an array calles files in the register directories.
We can loop over the attribute path and set a condition to skip the task if the path includes name of the excluded directory.

---
- hosts: localhost
  gather_facts: no
  vars:
    user: root
    path: /tmp/testdir/
    exclude_dir: specific_folder
  tasks:
  - name: list directories
    find:
      paths: "{{ path }}"
      file_type: any
    register: directories
  
  - name: Recursively change ownership
    file:
      path: "{{ item.path }}"
      state: directory
      owner: "{{ user }}"
      group: "{{ user }}"
    loop: "{{ directories.files }}"
    notify:
     - Restart service
    when: 
     - deploy
     - exclude_dir not in item.path
TASK [Recursively change ownership] *****************************************
ok: [localhost] => (item={'path': '/tmp/testdir/folder1', ....})
ok: [localhost] => (item={'path': '/tmp/testdir/folder2', ....})
skipping: [localhost] => (item={'path': '/tmp/testdir/specific_folder', ...})

Upvotes: 1

Houssem AZZOUZ
Houssem AZZOUZ

Reputation: 301

You can look for all files and directories in your parent folder with the 'find' module. Then, you pass the result as a list to a loop in a 'file' task. The 'file' task should test every item in the loop to check it does not belong to the excluded folder.

- name: "Find all files and folders in {{ PATH }}"
  find:
    path: "{{ PATH }}"
    file_type: any
    recurse: true
  register: folder_contents

- name: Recursively change ownership
  file:
    path: "{{ item.path }}"
    owner: "{{ USER }}"
    group: "{{ USER }}"
  loop: "{{ folder_contents.files }}"
  notify:
    - Restart service
  when: 
    - deploy
    - item.path is not search(excluded_folder_name)
  vars:
    - excluded_folder_name: 'foobar'

Voilà!

Upvotes: 3

Related Questions