AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Check if File Exists: stat Module with when Conditional (Guide)

By Luca Berton · Published 2024-01-01 · Category: installation

How to check if a file exists in Ansible with the stat module and when conditional. Verify file existence, check directories, test before actions.

Ansible Check if File Exists: stat Module with when Conditional (Guide)

How to check if a file exists in 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 Check If Directory Exists: stat Module Guide

Ansible check file exists

Today we're talking about the Ansible module stat. The full name is ansible.builtin.stat, 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 retrieves a file entry or a file system status. For Windows target use the ansible.windows.win_stat module instead.

Mandatory Parameters

• path string

See also: Ansible win_stat: Check if File or Directory Exists on Windows (Examples)

Main Return Values

• stat complex - exists

The only mandatory parameter is "path" which is the filesystem full path of the object to check. The module returns a complex object, the property that is interesting for us is "exists". This attribute is "true" if the object exists.

## Playbook Let's jump in a real-life playbook to check if a file exists with Ansible.

code

• file_exist.yml
---
- name: check if a file exist
  hosts: all
  become: false
  vars:
    myfile: /home/devops/test.txt
  tasks:
    - name: check if a file exists
      ansible.builtin.stat:
        path: "{{ myfile }}"
      register: file_data
    - name: report file exists
      ansible.builtin.debug:
        msg: "The file {{ myfile }} exist"
      when: file_data.stat.exists
    - name: report file not exists
      ansible.builtin.debug:
        msg: "The file {{ myfile }} doesn't exist"
      when: not file_data.stat.exists

code with ❤️ in GitHub

Conclusion

Now you know how to check if a file exists with Ansible.

See also: Ansible Set File Permissions 755: chmod with file Module Guide

Advanced File Checks

Check file and take action

- name: Check if config exists
  ansible.builtin.stat:
    path: /etc/myapp/config.yml
  register: config_file

- name: Copy default config if missing ansible.builtin.copy: src: files/default-config.yml dest: /etc/myapp/config.yml mode: '0644' when: not config_file.stat.exists

- name: Read existing config ansible.builtin.slurp: src: /etc/myapp/config.yml register: config_content when: config_file.stat.exists

Verify file checksum (integrity check)

- name: Get file checksum
  ansible.builtin.stat:
    path: /opt/myapp/binary
    checksum_algorithm: sha256
  register: binary_info

- name: Fail if binary is corrupted ansible.builtin.fail: msg: "Binary checksum mismatch! Expected {{ expected_checksum }}" when: - binary_info.stat.exists - binary_info.stat.checksum != expected_checksum

Check file age

- name: Get file info
  ansible.builtin.stat:
    path: /var/log/myapp/app.log
  register: log_file

- name: Rotate if file is larger than 100MB ansible.builtin.command: logrotate -f /etc/logrotate.d/myapp when: - log_file.stat.exists - log_file.stat.size > 104857600 become: true

Check multiple files exist

- name: Check required files
  ansible.builtin.stat:
    path: "{{ item }}"
  register: required_files
  loop:
    - /etc/ssl/certs/myapp.crt
    - /etc/ssl/private/myapp.key
    - /etc/myapp/config.yml

- name: Fail if any required file is missing ansible.builtin.fail: msg: "Missing required file: {{ item.item }}" loop: "{{ required_files.results }}" when: not item.stat.exists

Check if file is a symlink

- name: Check path type
  ansible.builtin.stat:
    path: /usr/bin/python
  register: python_path

- name: Show Python path details ansible.builtin.debug: msg: > Path: /usr/bin/python Exists: {{ python_path.stat.exists }} Is symlink: {{ python_path.stat.islnk | default(false) }} Links to: {{ python_path.stat.lnk_source | default('N/A') }}

stat Module vs Other Approaches

| Approach | Use Case | |----------|----------| | stat + when | Check before acting (most flexible) | | copy with force: false | Copy file only if dest doesn't exist | | file with state: file | Fails if file doesn't exist | | lineinfile with create: true | Create file if missing, add line | | template | Always overwrites with template output |

FAQ

How do I check if a file exists on Windows?

- name: Check file on Windows
  ansible.windows.win_stat:
    path: C:\Program Files\MyApp\config.ini
  register: win_file
  
- name: File status
  ansible.builtin.debug:
    msg: "File exists: {{ win_file.stat.exists }}"

Why does stat fail with "Permission denied"?

The Ansible connection user needs read access to the parent directory. Use become: true to check files in restricted directories.

How do I get the file's content along with stat?

stat doesn't read content. Use slurp (for small files) or fetch (to download):

- name: Read file content
  ansible.builtin.slurp:
    src: /etc/myapp/config.yml
  register: file_content
  when: config_file.stat.exists

- name: Decode content ansible.builtin.debug: msg: "{{ file_content.content | b64decode }}"

Check File Exists

- ansible.builtin.stat:
    path: /etc/myapp/config.yml
  register: config_file

- name: Create config only if missing ansible.builtin.template: src: config.yml.j2 dest: /etc/myapp/config.yml when: not config_file.stat.exists become: true

Check Multiple Files

- stat: { path: "{{ item }}" }
  loop:
    - /etc/nginx/nginx.conf
    - /etc/myapp/config.yml
    - /opt/myapp/app.jar
  register: file_checks

- debug: msg: "{{ item.item }} exists: {{ item.stat.exists }}" loop: "{{ file_checks.results }}"

File Type Checks

- stat: { path: /opt/myapp }
  register: path

- debug: msg: - "Is file: {{ path.stat.isreg | default(false) }}" - "Is directory: {{ path.stat.isdir | default(false) }}" - "Is symlink: {{ path.stat.islnk | default(false) }}" - "Is socket: {{ path.stat.issock | default(false) }}"

File Properties

- stat: { path: /opt/myapp/deploy.sh }
  register: script

- debug: msg: - "Size: {{ script.stat.size }} bytes" - "Mode: {{ script.stat.mode }}" - "Owner: {{ script.stat.pw_name }}" - "Group: {{ script.stat.gr_name }}" - "Modified: {{ script.stat.mtime }}" when: script.stat.exists

Checksum Verification

- stat:
    path: /opt/myapp/app.jar
    checksum_algorithm: sha256
  register: app_file

- debug: msg: "SHA256: {{ app_file.stat.checksum }}" when: app_file.stat.exists

# Compare checksums - fail: msg: "File integrity check failed!" when: - app_file.stat.exists - app_file.stat.checksum != expected_checksum

Deployment Patterns

Skip if already deployed

- stat: { path: /opt/myapp-{{ version }}/app.jar }
  register: deployed

- block: - get_url: url: "https://releases.example.com/app-{{ version }}.jar" dest: /opt/myapp-{{ version }}/app.jar - service: name=myapp state=restarted when: not deployed.stat.exists become: true

Backup before overwrite

- stat: { path: /etc/nginx/nginx.conf }
  register: nginx_conf

- copy: src: /etc/nginx/nginx.conf dest: "/etc/nginx/nginx.conf.bak.{{ ansible_date_time.epoch }}" remote_src: true when: nginx_conf.stat.exists become: true

File age check

- stat: { path: /tmp/cache.json }
  register: cache

- set_fact: cache_expired: "{{ (ansible_date_time.epoch | int) - (cache.stat.mtime | int) > 3600 }}" when: cache.stat.exists

- command: /opt/refresh-cache.sh when: not cache.stat.exists or cache_expired | default(true)

Windows Equivalent

- ansible.windows.win_stat:
    path: C:\Program Files\MyApp\config.xml
  register: win_file

- debug: msg="Config exists" when: win_file.stat.exists

FAQ

Does stat fail if file doesn't exist?

No — stat always succeeds. Check result.stat.exists for existence.

stat vs find?

stat checks a single known path. find searches for files matching patterns, size, or age criteria.

How do I check a remote file from the controller?

stat runs on the remote host. For controller files, use delegate_to: localhost or lookup('file').

Check File Exists

- ansible.builtin.stat:
    path: /etc/myapp/config.yml
  register: config_file

- debug: msg: "Config {{ 'exists' if config_file.stat.exists else 'missing' }}"

Conditional Task

- stat:
    path: /opt/myapp/.installed
  register: installed

- name: Run installer only if not already installed command: /opt/myapp/install.sh when: not installed.stat.exists become: true

Check Multiple Files

- stat:
    path: "{{ item }}"
  loop:
    - /etc/myapp/config.yml
    - /opt/myapp/app.jar
    - /var/log/myapp
  register: file_checks

- debug: msg: "MISSING: {{ item.item }}" loop: "{{ file_checks.results }}" when: not item.stat.exists

Check File Properties

- stat:
    path: /opt/scripts/deploy.sh
  register: script

- debug: msg: | Size: {{ script.stat.size }} bytes Mode: {{ script.stat.mode }} Owner: {{ script.stat.pw_name }} Modified: {{ script.stat.mtime }} Is executable: {{ script.stat.executable }} when: script.stat.exists

Create If Missing

- stat:
    path: /etc/myapp/config.yml
  register: config

- template: src: default-config.yml.j2 dest: /etc/myapp/config.yml when: not config.stat.exists become: true

Check If File Is Empty

- stat:
    path: /var/log/myapp/error.log
  register: error_log

- debug: msg: "Error log has content!" when: error_log.stat.exists and error_log.stat.size > 0

- stat:
    path: /opt/myapp/current
  register: current_link

- debug: msg: "Points to: {{ current_link.stat.lnk_source }}" when: current_link.stat.exists and current_link.stat.islnk

Verify Checksum

- stat:
    path: /opt/myapp/app.jar
    checksum_algorithm: sha256
  register: current_file

- debug: msg: "File needs updating" when: not current_file.stat.exists or current_file.stat.checksum != expected_checksum

FAQ

stat vs creates/removes?

creates/removes only work with command/shell modules. stat works everywhere and returns full metadata.

Why check before creating?

For idempotency when using command/shell. Modules like file, copy, and template are already idempotent — they check internally.

How to check on the controller (local)?

- stat: { path: /local/file }
  delegate_to: localhost
  register: local_file

Related Articles

Ansible conditional patternsthe Ansible become referencethe Ansible roles overviewWindows DSC and Ansible

Category: installation

Watch the video: Ansible Check if File Exists: stat Module with when Conditional (Guide) — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home