File Module — Managing Files and Directories
file is one of the most foundational modules in Ansible, and it’s been conspicuously absent from this entire course until now — creating directories, setting permissions and ownership, symlinks, and cleaning things up, all through one module with a consistent state: parameter.
Creating A Directory
- name: Create a directory
ansible.builtin.file:
path: /etc/myapp
state: directorystate: directory ensures the path exists as a directory — creates it if missing, does nothing if it’s already there and genuinely is a directory. Idempotent, exactly like every other properly-built module this course has favored over command/shell.
Setting Permissions And Ownership
- name: Create a directory with specific permissions
ansible.builtin.file:
path: /etc/myapp
state: directory
owner: alice
group: alice
mode: '0755'owner, group, and mode work exactly the way you’d expect from standard Unix permissions. Note mode is written as a quoted string, '0755' — covered in full in this chapter’s gotcha section, since getting this wrong has real, security-relevant consequences.
Creating A Symlink
- name: Create a symlink
ansible.builtin.file:
src: /etc/myapp/current.conf
dest: /etc/myapp/active.conf
state: linkstate: link makes dest a symlink pointing at src.
Removing Files Or Directories
- name: Remove a directory and everything in it
ansible.builtin.file:
path: /etc/myapp/old_config
state: absentstate: absent works for both files and directories — a directory is removed recursively, with everything inside it.
Touching A File
- name: Touch a file
ansible.builtin.file:
path: /etc/myapp/.initialized
state: touchstate: touch creates an empty file if it doesn’t exist, or updates its modification timestamp if it already does — the same behavior as the Unix touch command, without needing command: touch to get it.
A Realistic Example: Preparing A Directory Structure
Several states, combined into one coherent setup sequence:
- name: Ensure app directory exists with correct ownership
ansible.builtin.file:
path: /etc/myapp
state: directory
owner: alice
group: alice
mode: '0755'
- name: Ensure logs subdirectory exists
ansible.builtin.file:
path: /etc/myapp/logs
state: directory
owner: alice
group: alice
mode: '0750'
- name: Remove a deprecated config directory
ansible.builtin.file:
path: /etc/myapp/legacy
state: absent
- name: Mark setup as complete
ansible.builtin.file:
path: /etc/myapp/.setup_complete
state: touchA Gotcha: mode Must Be Quoted
# Risky — unquoted
mode: 0600# Correct — quoted
mode: '0600'Whether an unquoted leading-zero number like 0600 gets correctly interpreted as octal depends on which YAML specification and parser is in play — YAML 1.1 treats a leading zero as an implicit octal literal, but YAML 1.2 removed that behavior entirely, meaning the exact same unquoted value can resolve differently depending on your environment.
Ansible’s own documentation is explicit about this: always quote mode as a string, sidestepping the ambiguity completely rather than depending on which interpretation happens to apply wherever this playbook runs. Getting a permission value silently wrong on something like a config file containing a secret is exactly the kind of mistake worth eliminating at the source, rather than hoping the parsing happens to go your way.
Best Practices
- Always quote
modevalues as strings ('0755', never0755) — this isn’t a style preference, it’s avoiding a genuine, environment-dependent parsing ambiguity. - Use
state: directory/state: absentfor idempotent creation and removal of both files and directories — no need for a separatecommand-based approach. - Use
state: touchfor marker or flag files, rather than reaching forcommand: touch, which would reportchangedunconditionally every time, unlike this module’s genuine idempotency.