84

In ansible, I can do this:

file: dest=/foo/bar/somedir owner=root group=apache mode=0775 recurse=yes

And it recursively sets the owner, group, and permissions to 0775 on all directories and files in that path. But I want to set directories to 0775, and files to 0664. Is there some way to make ansible do this?

Edward Ned Harvey
  • 1,420
  • 2
  • 12
  • 14

9 Answers9

84
file: dest=/foo/bar/somedir owner=root group=apache mode=u=rwX,g=rX,o=rX recurse=yes

will set directories to 755, and files to 644.

Adam Chance
  • 1,759
  • 1
  • 15
  • 8
  • 2
    this does not work for files. – mirza May 22 '18 at 18:14
  • 3
    @Adam Chance Where is the magic? How does specifying u,g,o make it different than the number (and where is 644 written?) – linuxbandit Jul 12 '18 at 09:04
  • 11
    The source code mentions that the symbolic u.g.o handling is the same as "man chmod"'s handling -- capital X has special handling based on the dir/file status and existing execute bits. https://github.com/ansible/ansible/blob/8606fb33f0e8954b588eaecec2d99b4a120fd4ad/lib/ansible/module_utils/basic.py#L1419-L1421 – Marc Tamsky Jul 16 '18 at 16:47
  • 11
    In other words, `X` is `x` for directories, and nothing for files. And that's how [`chmod`](https://jlk.fjfi.cvut.cz/arch/manpages/man/core/coreutils/chmod.1.en), not `ansible` treats the mode string. I wish `S` would be treated likewise. – x-yuri Jan 24 '19 at 12:02
  • 2
    But do note, that it makes files executable by owner executable by [all](https://stackoverflow.com/questions/28778738/ansible-mode-755-for-directories-and-644-for-files-recursively#comment95518029_29793833). – x-yuri Jan 24 '19 at 16:35
34

The Ansible file/copy modules don't give you the granularity of specifying permissions based on file type so you'd most likely need to do this manually by doing something along these lines:

- name: Ensure directories are 0755
  command: find {{ path }} -type d -exec chmod 0755 {} \;

- name: Ensure files are 0644
  command: find {{ path }} -type f -exec chmod 0644 {} \;

These would have the effect of recursing through {{ path }} and changing the permissions of every file or directory to the specified permissions.

Source: https://stackoverflow.com/a/28782805/1306186

DavidPostill
  • 153,128
  • 77
  • 353
  • 394
Zulakis
  • 1,644
  • 5
  • 19
  • 29
12

If you want to use the module file in ansible, you can:

file: dest=/foo/bar/somedir owner=root group=apache mode=0644 recurse=yes

file: dest=/foo/bar/somedir owner=root group=apache mode=0775

With this method you first set all the file (recurse=yes) to '644' and then you set /foo/bar/somedir to '775'.

This is not perfect because it will change your directory permission each time you play your playbook. But at least it is idempotent, not like the module command.

If you don't want to have 'changed' status, you can use the module stat. It will list all the files and directory in /foo/bar/somedir so you register the answer and then make a loop on those files only.

gmangin
  • 137
  • 1
  • 3
  • 7
    Your answer will set all subfiles and subdirectories to 644, while setting only the top-level directory to 775. The need is for all *directories* to be 775, including subdirectories. – Edward Ned Harvey Sep 15 '16 at 23:38
  • 3
    This suggestion is not idempotent. Setting permissions first to one value, and later to another, means that the permissions will be changed on every run. – Kevin Keane Apr 29 '19 at 21:51
  • This is idempotent - the state is the same if run multiple times. Idempotent != no changes. – Geoff Jan 17 '22 at 04:50
7

I'm not sure how much sense it would be to set directories to 0775 (rwxrwxr-x) and files to 0644 (rw-r--r--): group-writeable directories but not files?

If you meant to set files to 0664 (rw-rw-r--) to ensure that files are not executable while directories are traversable then there is an elegant solution involving only one chmod command:

chmod -c -R ug=rw,o=r,a-x+X "{{top_dir}}"

Here is how it can be used in Ansible:

- name: recursive chmod example
  command: |
    chmod -c -R ug=rw,o=r,a-x+X "{{item}}"
  register: chmod_status
  changed_when: chmod_status.stdout != ""
  with_items:
    - "/home/user/sample/dir"

chmod -c prints all the changes that we can conveniently use to populate "changed" status in Ansible. I hope it make sense.

Onlyjob
  • 384
  • 3
  • 7
6

To only change mods when needed:

- name: make dirs 0755   
  command: find {{ your_path }} -type d ! -perm 0755 -exec chmod 0755 {} \;

- name: make files 0644   
  command: find {{ your_path }} -type f ! -perm 0644 -exec chmod 0644 {} \;
sjas
  • 413
  • 6
  • 14
3

You can use copy: module and set the directory_mode to 0755 and mode to 644 for files. Note that you need the forward slash "/" at the end for recursive copying.

Example:

    - name: Recursively copy testing directory
      copy:
        src: testing/
        dest: /opt/testing/
        directory_mode: 0755
        owner: test
        group: test
        mode: 0644
Tom Phan
  • 31
  • 2
3
- name: Change permissions on /remote/file/path to ansible:user so we can write to it 
  file:
    path: /remote/file/path/(or file)
    owner: root (or other)
    # recursive if you need it
    # recurse:true 
    group: users (or other)
    mode: "0770"

Or, making more 'Ansible-ish' the appreciated answer above

 name: Change permissions from /SM/data-drive/sm_sites etc...
  file:
    dest: "{{ item }}"
    owner: www-data
    group: www-data    
    recurse: true 
    # supposed to set directories to 755 and files to 644
    mode: u=rwX,g=rX,o=rX
  with_items:
    - /SM/data-drive/sm_sites
    - /SM/data-drive/smw_dams
    - /SM/data-drive/smw_wikis
Jay Lepore
  • 31
  • 3
2

A more modern approach would be to first filter out the dirs like so:

  - name: Evaluating home dirs    
    find:    
      path: "/home"    
      file_type: "directory"    
    register: homedirs    

  # This returns the actual dir-path's 
  - set_fact: 
      homes: "{{ homedirs.files | map(attribute='path') | list }}"           

  - name: Changing permissions of /home/*
    file:    
      path: "{{ item }}"  
      mode: 0700  
    with_items:    
      - "{{ homes }}"

NOTE: Change "file_type" parameter to "file", for files to be affected

phuturer
  • 21
  • 2
1

As an example, as if "securing" a hypothetical Joomla! instance:

- name: Ensure to secure the Joomla installation
  shell: "find . -type d -exec chmod 755 {} \\; && find . -type f -exec chmod 644 {} \\; && chown -R www-data: cache modules images media modules logs tmp administrator/cache"
  args:
      chdir: "{{ production_backend_root_path }}"
  tags: 
      - joomla_secure

Where root owns all but a few designated directories owned by the webserver.