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 Directory Exists: stat Module Guide

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

How to check if files and directories exist with Ansible stat module. Use stat results in conditionals, check permissions, size, and timestamps.

Ansible Check If Directory Exists: stat Module Guide

How to check if a directory exists in Ansible?

I’m going to show you a live Playbook and some simple Ansible code.

See also: Ansible Create Directory: file Module with state=directory (Guide)

Ansible check directory 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.

Main parameters and return values

  • path _string_
The only mandatory parameter is “path” which is the filesystem full path of the object to check.
  • stat _complex_
The module returns a complex object, the property that is interesting for us is “isdir”. This attribute is “true” if the object is a directory.

See also: Ansible Create Symlink: file Module with state=link (Guide)

Demo

Let’s jump in a real-life playbook to check if a directory exists with Ansible.

code

  • directory_check_exists.yml
---
- name: check if the directory exists
  hosts: all
  become: false
  vars:
    directory: "/tmp"
  tasks:
    - name: Check if the directory exists
      ansible.builtin.stat:
        path: "{{ directory }}"
      register: dir_to_check

    - name: Directory found
      ansible.builtin.debug:
        msg: "Directory {{ directory }} present"
      when: dir_to_check.stat.isdir is defined and dir_to_check.stat.isdir

code with ❤️ in GitHub

Conclusion

Now you know better the Ansible module stat and you could use it successfully in your playbook.

See also: Ansible Create File with Content: copy Module content Parameter

How It Works

The ansible.builtin.stat module retrieves file or directory metadata without modifying anything. Combined with register and when, you can create conditional logic based on filesystem state.

Complete Examples

Check directory and create if missing

---
- name: Ensure directory exists
  hosts: all
  tasks:
    - name: Check if /opt/myapp exists
      ansible.builtin.stat:
        path: /opt/myapp
      register: myapp_dir

    - name: Create directory if not present
      ansible.builtin.file:
        path: /opt/myapp
        state: directory
        owner: appuser
        group: appuser
        mode: '0755'
      become: true
      when: not myapp_dir.stat.exists

    - name: Show directory info
      ansible.builtin.debug:
        msg: "Directory exists: {{ myapp_dir.stat.exists }}, isdir: {{ myapp_dir.stat.isdir | default(false) }}"

Check multiple directories

- name: Verify required directories exist
  ansible.builtin.stat:
    path: "{{ item }}"
  register: dir_checks
  loop:
    - /opt/myapp
    - /var/log/myapp
    - /etc/myapp

- name: Report missing directories
  ansible.builtin.debug:
    msg: "MISSING: {{ item.item }}"
  loop: "{{ dir_checks.results }}"
  when: not item.stat.exists

Check if path is a directory (not a file)

- name: Check path type
  ansible.builtin.stat:
    path: /opt/myapp
  register: path_info

- name: Fail if path exists but is not a directory
  ansible.builtin.fail:
    msg: "/opt/myapp exists but is not a directory!"
  when: path_info.stat.exists and not path_info.stat.isdir

Useful stat Properties

PropertyTypeDescription
stat.existsboolPath exists
stat.isdirboolIs a directory
stat.isregboolIs a regular file
stat.islnkboolIs a symlink
stat.sizeintSize in bytes
stat.uidintOwner user ID
stat.gidintOwner group ID
stat.modestringPermissions (e.g., "0755")
stat.pw_namestringOwner username
stat.gr_namestringOwner group name
stat.mtimefloatLast modified timestamp

FAQ

Why not just use file with state: directory?

file state=directory is idempotent — it creates the directory if missing. Use stat when you need to make decisions based on whether the directory exists (e.g., skip deployment, trigger different logic).

How do I check directory permissions?

- name: Check permissions
  ansible.builtin.stat:
    path: /opt/myapp
  register: dir_info

- name: Fix permissions if wrong
  ansible.builtin.file:
    path: /opt/myapp
    mode: '0755'
  when: dir_info.stat.exists and dir_info.stat.mode != '0755'
  become: true

Check Directory Exists

- name: Check if directory exists
  ansible.builtin.stat:
    path: /opt/myapp
  register: dir_check

- name: Create only if missing
  ansible.builtin.file:
    path: /opt/myapp
    state: directory
  when: not dir_check.stat.exists
  become: true

- name: Skip if already deployed
  ansible.builtin.unarchive:
    src: myapp.tar.gz
    dest: /opt/myapp
  when: not dir_check.stat.exists

Check File Exists

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

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

Check File Type

- stat:
    path: /opt/myapp
  register: path_info

- debug:
    msg:
      - "Exists: {{ path_info.stat.exists }}"
      - "Is directory: {{ path_info.stat.isdir | default(false) }}"
      - "Is file: {{ path_info.stat.isreg | default(false) }}"
      - "Is symlink: {{ path_info.stat.islnk | default(false) }}"
      - "Size: {{ path_info.stat.size | default(0) }} bytes"
      - "Owner: {{ path_info.stat.pw_name | default('N/A') }}"
      - "Mode: {{ path_info.stat.mode | default('N/A') }}"
  when: path_info.stat.exists

Common Patterns

Deploy if not already present

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

- get_url:
    url: "https://releases.example.com/app-{{ version }}.jar"
    dest: /opt/myapp/app.jar
  when: not app.stat.exists

Backup before overwriting

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

- copy:
    src: /etc/nginx/nginx.conf
    dest: /etc/nginx/nginx.conf.bak
    remote_src: true
  when: nginx_conf.stat.exists

Check file age

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

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

- debug:
    msg: "Cache is {{ cache_age }} seconds old"
  when: cache.stat.exists and (cache_age | int) > 3600

Check file size

- stat: { path: /var/log/myapp.log }
  register: log_file

- name: Rotate if > 100MB
  command: logrotate -f /etc/logrotate.d/myapp
  when: log_file.stat.exists and log_file.stat.size > 104857600

Stat Properties

PropertyDescription
existsPath exists
isdirIs directory
isregIs regular file
islnkIs symlink
sizeFile size (bytes)
modePermission mode
pw_nameOwner username
gr_nameGroup name
mtimeModification time (epoch)
checksumSHA1 hash
md5MD5 hash (if get_md5: true)

FAQ

Why does stat not fail when file doesn't exist?

By design — stat always succeeds. Check result.stat.exists to determine if the path exists.

How do I check on Windows?

- ansible.windows.win_stat:
    path: C:\Program Files\MyApp
  register: win_dir

stat vs find module?

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

Check Directory Exists

- ansible.builtin.stat:
    path: /opt/myapp
  register: app_dir

- debug:
    msg: "/opt/myapp exists: {{ app_dir.stat.exists }}"

- file:
    path: /opt/myapp
    state: directory
  when: not app_dir.stat.exists
  become: true

Check File Exists

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

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

Check File Type

- stat:
    path: /opt/myapp
  register: result

- debug:
    msg: |
      Exists: {{ result.stat.exists }}
      Is directory: {{ result.stat.isdir | default(false) }}
      Is file: {{ result.stat.isreg | default(false) }}
      Is link: {{ result.stat.islnk | default(false) }}

Check Permissions

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

- file:
    path: /opt/scripts/deploy.sh
    mode: '0755'
  when: script.stat.exists and script.stat.mode != '0755'
  become: true

Check File Size

- stat:
    path: /var/log/myapp/app.log
  register: logfile

- name: Rotate large log
  command: logrotate -f /etc/logrotate.d/myapp
  when: logfile.stat.exists and logfile.stat.size > 104857600  # 100MB
  become: true

Check Checksum

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

- get_url:
    url: "{{ download_url }}"
    dest: /opt/myapp/app.jar
    checksum: "sha256:{{ expected_checksum }}"
  when: not current.stat.exists or current.stat.checksum != expected_checksum

Check Multiple Paths

- stat:
    path: "{{ item }}"
  loop:
    - /opt/myapp/config.yml
    - /opt/myapp/data
    - /opt/myapp/logs
  register: paths

- debug:
    msg: "{{ item.item }}: {{ 'exists' if item.stat.exists else 'MISSING' }}"
  loop: "{{ paths.results }}"

stat Attributes

AttributeDescription
.existsPath exists
.isdirIs directory
.isregIs regular file
.islnkIs symlink
.modePermissions (e.g., '0755')
.sizeSize in bytes
.uid / .gidOwner/group IDs
.pw_name / .gr_nameOwner/group names
.mtimeModification time (epoch)
.checksumFile checksum
.lnk_sourceSymlink target

FAQ

Why use stat instead of just using creates?

creates only works with command/shell. stat works everywhere and gives you full file metadata for complex conditions.

How to handle "attribute not found" errors?

Use default() filter: result.stat.isdir | default(false). Attributes like isdir only exist when exists is true.

stat on remote vs local?

stat runs on the managed node. For local files, use delegate_to: localhost or lookup('file').

Category: troubleshooting

Watch the video: Ansible Check If Directory Exists: stat Module Guide — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home