Skip to content

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: directory

state: 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: link

state: 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: absent

state: 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: touch

state: 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: touch

A 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 mode values as strings ('0755', never 0755) — this isn’t a style preference, it’s avoiding a genuine, environment-dependent parsing ambiguity.
  • Use state: directory/state: absent for idempotent creation and removal of both files and directories — no need for a separate command-based approach.
  • Use state: touch for marker or flag files, rather than reaching for command: touch, which would report changed unconditionally every time, unlike this module’s genuine idempotency.
Last updated on