Ansible chmod: Change File Permissions with file Module (Guide)
By Luca Berton · Published 2024-01-01 · Category: windows-automation
How to change file permissions (chmod) in Ansible with the file module. Set mode, owner, group, recursive permissions on files and directories.

How to change file or directory permission with Ansible?
I'm going to show you a live Playbook and some simple Ansible code. I'm Luca Berton and welcome to today's episode of Ansible Pilot.See also: Ansible Set File Permissions 755: chmod with file Module Guide
Ansible change file/directory permission
Today we're talking about the Ansible modulefile.
The full name is ansible.builtin.file, which means that is part of the collection of modules "builtin" with ansible and shipped with it.
It's a module pretty stable and out for years.
It works in a different variety of operating systems.
It manages files and file properties.
For Windows targets, use the ansible.windows.win_file module instead.
Main Parameters
• path _string_ (dest, name) - file path • owner _string_ - user • group _string_ - group • mode _raw_ - Ex: '0644' or 'u=rw,g=r,o=r' • state _string_ - file/absent/directory/hard/link/touch • setype/seuser/selevel - SELinuxThis module has some parameters to perform any tasks. The only required is "path", where you specify the filesystem path of the file you're going to edit. The parameter "owner" sets the user that should own the file/directory. The parameter "group" sets the group that should own the file/directory. The parameter "mode" sets the permissions in the UNIX way of the file/directory. The state defines the type of object we are modifying, the default is "file" but we could handle also directories, hardlink, symlink, or only update the access time with the "touch" option. Let me also highlight that we could also specify the SELinux properties.
## Playbook Let's jump in a real-life playbook to change file permission with Ansible.
code
• file.yml---
- name: file module demo
hosts: all
vars:
myfile: "/home/devops/test.txt"
become: false
tasks:
- name: check permission
ansible.builtin.file:
path: "{{ myfile }}"
owner: "devops"
group: "users"
mode: '0777'
See also: Ansible Create User Account: user Module Complete Guide
Conclusion
Now you know how to change file or directory permission with Ansible.Permission Format Options
Ansible accepts permissions in two formats:
Octal notation (recommended)
- name: Set file to 644
ansible.builtin.file:
path: /etc/myapp/config.yml
mode: '0644' # Always quote and include leading 0
become: true
Symbolic notation
- name: Set file permissions symbolically
ansible.builtin.file:
path: /etc/myapp/config.yml
mode: 'u=rw,g=r,o=r' # Same as 0644
become: true
See also: Ansible Remove User Account: Delete Users with user Module
Common Permission Patterns
| Octal | Symbolic | Meaning | Use Case |
|-------|----------|---------|----------|
| 0644 | u=rw,g=r,o=r | Owner read/write, others read | Config files |
| 0755 | u=rwx,g=rx,o=rx | Owner full, others read/execute | Scripts, directories |
| 0600 | u=rw,g=,o= | Owner only | Secrets, SSH keys |
| 0700 | u=rwx,g=,o= | Owner only (executable) | Private scripts |
| 0444 | u=r,g=r,o=r | Read-only for everyone | Reference files |
| 0775 | u=rwx,g=rwx,o=rx | Owner+group full | Shared directories |
Advanced Examples
Change ownership and permissions together
- name: Set ownership and permissions
ansible.builtin.file:
path: /var/www/html
owner: www-data
group: www-data
mode: '0755'
recurse: true # Apply to all files/dirs inside
become: true
Set different permissions for files vs directories
- name: Find all files
ansible.builtin.find:
paths: /var/www/html
file_type: file
recurse: true
register: website_files
- name: Set file permissions to 644
ansible.builtin.file:
path: "{{ item.path }}"
mode: '0644'
loop: "{{ website_files.files }}"
become: true
- name: Find all directories
ansible.builtin.find:
paths: /var/www/html
file_type: directory
recurse: true
register: website_dirs
- name: Set directory permissions to 755
ansible.builtin.file:
path: "{{ item.path }}"
mode: '0755'
loop: "{{ website_dirs.files }}"
become: true
Make a script executable
- name: Deploy and make executable
ansible.builtin.copy:
src: scripts/deploy.sh
dest: /usr/local/bin/deploy.sh
owner: root
group: root
mode: '0755' # rwxr-xr-x
become: true
Set SELinux context
- name: Set SELinux context for web content
ansible.builtin.file:
path: /var/www/custom
setype: httpd_sys_content_t
recurse: true
become: true
Set sticky bit, setuid, setgid
# Sticky bit on shared directory (only owner can delete their files)
- name: Set sticky bit on /tmp-style directory
ansible.builtin.file:
path: /shared/uploads
mode: '1777' # Leading 1 = sticky bit
become: true
# Setgid on group directory (new files inherit group)
- name: Set setgid on project directory
ansible.builtin.file:
path: /projects/teamwork
mode: '2775' # Leading 2 = setgid
group: developers
become: true
FAQ
Why must I quote the mode value?
Without quotes, YAML interprets 0644 as the integer 644 (octal 01204), which gives wrong permissions. Always use mode: '0644' with quotes.
How do I check current permissions?
- name: Get current permissions
ansible.builtin.stat:
path: /etc/myapp/config.yml
register: file_info
- name: Show permissions
ansible.builtin.debug:
msg: "Mode: {{ file_info.stat.mode }}, Owner: {{ file_info.stat.pw_name }}"
What does recurse: true do?
Applies the same owner/group/mode to all files AND directories inside the path. Be careful — files and directories often need different permissions (644 vs 755).
How do I change permissions on Windows?
Use ansible.windows.win_acl for Windows ACL permissions:
- name: Set Windows permissions
ansible.windows.win_acl:
path: C:\inetpub\wwwroot
user: IIS_IUSRS
rights: ReadAndExecute
type: allow
Basic Permission Change
- ansible.builtin.file:
path: /opt/myapp/start.sh
mode: '0755'
become: true
Change Owner and Group
- file:
path: /var/www/html
owner: www-data
group: www-data
recurse: true
become: true
Common Permission Patterns
# Executable script
- file:
path: /opt/scripts/deploy.sh
mode: '0755'
owner: deploy
# Private key
- file:
path: /home/deploy/.ssh/id_rsa
mode: '0600'
owner: deploy
group: deploy
# Config file (read-only)
- file:
path: /etc/myapp/config.yml
mode: '0644'
owner: root
group: root
# Shared directory
- file:
path: /opt/shared
state: directory
mode: '0775'
owner: root
group: developers
# Sensitive config
- file:
path: /etc/myapp/secrets.yml
mode: '0600'
owner: root
group: root
Symbolic Mode
# Add execute for owner
- file:
path: /opt/script.sh
mode: u+x
# Add read/write for group
- file:
path: /opt/shared/data
mode: g+rw
recurse: true
# Remove world read
- file:
path: /etc/myapp/secrets
mode: o-rwx
Recursive Permissions
# All files and directories
- file:
path: /var/www/mysite
owner: www-data
group: www-data
recurse: true
become: true
# Different permissions for files vs directories
- shell: |
find /var/www/mysite -type d -exec chmod 755 {} \;
find /var/www/mysite -type f -exec chmod 644 {} \;
become: true
SUID, SGID, Sticky Bit
# SGID on directory (new files inherit group)
- file:
path: /opt/shared
state: directory
mode: '2775'
group: developers
become: true
# Sticky bit (only owner can delete)
- file:
path: /tmp/shared
state: directory
mode: '1777'
become: true
ACLs (Extended Permissions)
- ansible.posix.acl:
path: /opt/myapp
entity: deploy
etype: user
permissions: rwx
state: present
become: true
# Default ACL for new files
- ansible.posix.acl:
path: /opt/myapp
entity: developers
etype: group
permissions: rx
default: true
state: present
become: true
Batch Permission Changes
- file:
path: "{{ item.path }}"
mode: "{{ item.mode }}"
owner: "{{ item.owner | default('root') }}"
loop:
- { path: /opt/myapp/bin/start.sh, mode: '0755' }
- { path: /opt/myapp/etc/config.yml, mode: '0644' }
- { path: /opt/myapp/etc/secrets.yml, mode: '0600' }
- { path: /opt/myapp/log, mode: '0755' }
become: true
Verify Permissions
- stat:
path: /etc/myapp/config.yml
register: file_info
- debug:
msg: "Mode: {{ file_info.stat.mode }}, Owner: {{ file_info.stat.pw_name }}"
- assert:
that:
- file_info.stat.mode == '0644'
- file_info.stat.pw_name == 'root'
Permission Reference
| Mode | Meaning |
|------|---------|
| 0755 | rwxr-xr-x (executables, directories) |
| 0644 | rw-r--r-- (config files) |
| 0600 | rw------- (secrets, private keys) |
| 0700 | rwx------ (private directories) |
| 0775 | rwxrwxr-x (shared directories) |
| 0666 | rw-rw-rw- (shared files, rarely used) |
| 2775 | rwxrwsr-x (SGID directory) |
| 1777 | rwxrwxrwt (sticky bit, like /tmp) |
FAQ
Octal string vs integer?
Always quote mode as a string: mode: '0755'. Without quotes, YAML may interpret it as an integer (493), which causes unexpected permissions.
How do I change permissions without changing content?
The file module only changes metadata (permissions, owner, timestamps). It never modifies file content.
recurse doesn't set different perms for files vs dirs?
Correct — recurse applies the same mode to everything. Use find + shell for different file vs directory permissions.
Related Articles
• Ansible become guide • role-based playbook organization in Ansible • file ownership and modes via ansible.builtin.file • Ansible Windows administration walkthroughCategory: windows-automation
Watch the video: Ansible chmod: Change File Permissions with file Module (Guide) — Video Tutorial