Ansible file Module Tutorial + Examples


Percy Grunwald's Profile Picture

Written by Percy Grunwald

— Last Updated February 5, 2024

Ansible Course: Productive with Ansible (2024)
Ansible Course: Productive with Ansible (2024)
Go from Ansible beginner to Ansible pro with this full video course.

What does the Ansible file module do?

Ansible’s file module can:

  • Create files, directories and symlinks
- name: create an empty file if it doesn't exist
  file:
    path: $HOME/test_file
    state: touch
  • Delete files, directories and symlinks
- name: delete a file (or symlink) if it exists
  file:
    path: $HOME/test_file
    state: absent
  • Modify permissions and properties of files and directories
- name: modify permissions and properties of a directory (or create it if it doesn't exist)
  file:
    path: $HOME/test_directory/inner_test_directory
    state: directory
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0755
    modification_time: "201812271410.15"
    access_time: now

Ansible file Module Video Tutorial

If you prefer watching to reading, here’s a full video tutorial from the TopTechSkills YouTube channel covering a many of the points and examples from this article. Feel free to comment on this article or the video if you have any questions.

▶️ Watch on YouTube

state: file vs state: touch

There are 2 states for dealing specifically with files using the file module: state: file and state: touch.

  • state: file ensures a path is a file, but will throw an error if the path does not exist or if the path is not a file
- name: check file that doesn't exist with state file
  file:
    path: $HOME/absent_test_file
    state: file
fatal: [123.123.123.123]: FAILED! => {"changed": false, 
  "msg": "file (/home/ubuntu/absent_test_file) is absent, cannot continue", 
  "path": "/home/ubuntu/absent_test_file", "state": "absent"}
- name: check directory with state file
  file:
    path: $HOME/test_directory
    state: file
fatal: [123.123.123.123]: FAILED! => {"changed": false, "gid": 1000, "group": "ubuntu", "mode": "0775", 
  "msg": "file (/home/ubuntu/test_directory) is directory, cannot continue", "owner": "ubuntu", 
  "path": "/home/ubuntu/test_directory", "size": 4096, "state": "directory", "uid": 1000}
  • state: touch will create an empty file if a path does not exist and will not throw an error if the path is a directory or symlink
- name: check file that doesn't exist with state touch
  file:
    path: $HOME/absent_test_file
    state: touch
ok: [123.123.123.123] => {
    "file_output": {
        "changed": true,
        "dest": "/home/ubuntu/absent_test_file",
        ...
        "state": "file",
        "uid": 1000
    }
}
- name: check directory with state touch
  file:
    path: $HOME/test_directory
    state: touch
ok: [123.123.123.123] => {
    "file_output": {
        "changed": true,
        "dest": "/home/ubuntu/test_directory",
        ...
        "state": "directory",
        "uid": 1000
    }
}

This behavior of state: touch matches the touch command.

Examples

Create a directory or ensure a directory exists

You can ensure a directory exists by setting the state parameter to directory. If the directory already exists, Ansible will do nothing. Note that state: directory behaves like mkdir -p and will create any parent directories if they don’t exist. In the example below, test_directory (the parent directory) will be created in addition to inner_test_directory if it doesn’t already exist.

- name: ensure a directory exists
  file:
    path: $HOME/test_directory/inner_test_directory
    state: directory

Create multiple directories in a loop

Use the loop keyword and {{ item }} to create multiple directories in a loop.

- name: create multiple directories in a loop
  file:
    path: "$HOME/test_directory/{{ item }}"
    state: directory
  register: file_output
  loop:
    - inner_test_directory
    - another_test_directory/another_inner_directory
.
└── test_directory
    ├── another_test_directory
    │   └── another_inner_directory
    └── inner_test_directory

Delete a directory recursively

You can delete a directory by setting the state parameter to absent. If the directory does not exist, Ansible will do nothing.

- name: delete a directory recursively if it exists
  file:
    path: $HOME/test_directory
    state: absent

Change permissions of a directory

The owner, group and mode parameters of the file module give you fine control over ownership and file permissions. Remember that you will need become: true if you are setting the owner or group to values that don’t match the ansible_user.

The mode parameter accepts the following:

  • Octal mode format: e.g. 0755
  • Symbolic mode format: e.g. u=rwx,g=rx,o=rx (where u is the owner, g is the group, and o is others). The permissions are r for read, w for write and x for execute.

With octal mode:

- name: modify ownership & permissions of a directory (octal mode)
  file:
    path: $HOME/test_directory/inner_test_directory
    state: directory
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0755

With symbolic mode (equivalent to 0755):

- name: modify ownership & permissions of a directory (symbolic mode)
  file:
    path: $HOME/test_directory/inner_test_directory
    state: directory
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: u=rwx,g=rx,o=rx

Setting a different owner with become: true:

- name: change ownership & permissions of directory with become
  file:
    path: /var/log/nginx
    state: directory
    owner: root
    group: root
    mode: 0755
  become: true

Change permissions of a directory recursively

You can use the recurse keyword to change directory permissions recursively. This will affect the path, as well as all the files and directories inside it.

- name: modify ownership & permissions of a directory recursively
  file:
    path: $HOME/test_directory
    state: directory
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: 0755
    recurse: true

Set directories to 0755 and files to 0644 recursively

A common task when ensuring correct file permissions on a system is to recursively apply a mode of 0755 on a directory so that all files and directories inside can be read by all users. Applying a mode of 755 to directories is fine, but applying that mode to files is not good practice because it allows all users to execute them, which is insecure. It’s better practice to set files to a mode of 0644, which lets all users read a file but not execute it.

It’s possible to recursively apply a mode of 0755 to directories and 0644 to files with the file module by using symbolic mode:

- name: recursively set 0755 on directories and 0644 on files
  file:
    path: $HOME/test_directory
    state: directory
    mode: u=rwX,g=rX,o=rX
    recurse: true

The X (instead of x) will affect only directories or files that already allow execution.

You can create symlinks by setting the state parameter to link. You will need to set src and dest parameters for the symlink. The src is the original file and dest is the symlink.

- name: create a symlink if it doesn't exist
  file:
    src: $HOME/test_file
    dest: $HOME/test_directory/inner_test_directory/test_file_symlink
    state: link

You can delete a symlink with the file module in the same way as a file or directory: simply use state: absent.

- name: delete a symlink if it exists
  file:
    path: $HOME/test_directory/inner_test_directory/test_file_symlink
    state: absent

Create an empty file or ensure a file exists

You can ensure a file exists at a certain path by setting state to touch, which will do nothing if a file already exists, or create an empty file if it doesn’t.

- name: create an empty file if it doesn't exist
  file:
    path: $HOME/test_file
    state: touch

Delete a file if it exists

You can delete a file by setting state to absent. If the file doesn’t exist, Ansible will do nothing.

- name: delete a file if it exists
  file:
    path: $HOME/test_file
    state: absent

Update the modified time or access time of a file

Since Ansible 2.7, you can update the access and modified times of a file by using the access_time and modification_time parameters. The parameters accept the following values:

  • preserve - no change (default when no value is provided)
  • now - set to the current time
  • "YYYYmmddHHMM.SS" - set to a specific time (note the quotation marks, without them Ansible interprets it as a float)
- name: modify access and modified times of a file
  file:
    path: $HOME/test_file
    state: file
    access_time: now
    modification_time: "201812271410.15"

How to capture file module output

Use the register keyword to capture the output of the file module.

- name: ensure a directory exists
  file:
    path: $HOME/test_directory/inner_test_directory
    state: directory
  register: file_output

- debug: var=file_output

The debug task above will output the following:

ok: [123.123.123.123] => {
    "file_output": {
        "changed": true,
        "diff": {
            "after": {
                "path": "/home/ubuntu/test_directory/inner_test_directory",
                "state": "directory"
            },
            "before": {
                "path": "/home/ubuntu/test_directory/inner_test_directory",
                "state": "absent"
            }
        },
        "failed": false,
        "gid": 1000,
        "group": "ubuntu",
        "mode": "0775",
        "owner": "ubuntu",
        "path": "/home/ubuntu/test_directory/inner_test_directory",
        "size": 4096,
        "state": "directory",
        "uid": 1000
    }
}

How to capture file module output from a loop

When using the file module in a loop, the output of each item in the loop will go into the results key of the registered variable.

- name: create multiple directories in a loop
  file:
    path: "$HOME/test_directory/{{ item }}"
    state: directory
  register: file_output
  loop:
    - inner_test_directory
    - another_test_directory/another_inner_directory

- debug: var=file_output
ok: [123.123.123.123] => {
    "file_output": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                ...
                "changed": true,
                "diff": {
                    "after": {
                        "path": "/home/ubuntu/test_directory/inner_test_directory",
                        "state": "directory"
                    },
                    "before": {
                        "path": "/home/ubuntu/test_directory/inner_test_directory",
                        "state": "absent"
                    }
                },
                "failed": false,
                "gid": 1000,
                "group": "ubuntu",
                "invocation": {...},
                "item": "inner_test_directory",
                "mode": "0775",
                "owner": "ubuntu",
                "path": "/home/ubuntu/test_directory/inner_test_directory",
                "size": 4096,
                "state": "directory",
                "uid": 1000
            },
            {
                ...
                "changed": true,
                "diff": {
                    "after": {
                        "path": "/home/ubuntu/test_directory/another_test_directory/another_inner_directory",
                        "state": "directory"
                    },
                    "before": {
                        "path": "/home/ubuntu/test_directory/another_test_directory/another_inner_directory",
                        "state": "absent"
                    }
                },
                "failed": false,
                "gid": 1000,
                "group": "ubuntu",
                "invocation": {...},
                "item": "another_test_directory/another_inner_directory",
                "mode": "0775",
                "owner": "ubuntu",
                "path": "/home/ubuntu/test_directory/another_test_directory/another_inner_directory",
                "size": 4096,
                "state": "directory",
                "uid": 1000
            }
        ]
    }
}

Further reading

Ansible file Module on Ansible Docs

Comment & Share