There can be a slew of reasons you might want to check if a file exists in Ansible. At least in my case I often have to include a check in my ansible playbooks to see if a file or directory exists on the remote host. Most of the time it is to check if a certain action has already been performed by looking for configuration files.
But did you know you can perform a totally unrelated action based on the existence of a specific file or directory?
You can use the built-in stat module to check if a file exists in Ansible. Stat will look at the file and get its properties and state. You can then check if the file exists by looking at the resulting variable.
I have compiled a step-by-step guide below to get an understanding of how the stat module works. Keep reading to get the full experience.
Get the state of the file
First of all, we need to create a playbook to test the functionality. Below I have the beginning of the playbook. Please replace the host and user variables to fit your environment.
--- - name: check if myfile.txt exists hosts: testserver.linuxdigest.com user: root tasks: - name: Get stats about myfile.txt stat: path: /etc/myfile.txt register: myvar - debug: msg: "The contents of myvar: {{ myvar }}"
As you can see, we are checking for a file at the path /etc/myfile.txt. We tell Ansible to put the results of stat into a variable called myvar. After getting the information we want, we print what the variable now contains. Here is the output we get if the file exists:
Wow, that is a lot of information! Now we can see the permissions of the file, if it is a symlink when it was created, modified and a bunch of other stuff we don’t need right now. But, obviously, we can use the stat module to check for much more than just if the file exists.
But in this example, we only care about one value:
'stat': {'exists': True.....
We now know that the file exists. So now we just need to act on that information.
Perform action if the file exists
In the output above, the exists variable was within a dictionary called “stat”. So we can access the result with this path:
myvar.stat.exists
The value of this variable will be “True” if the file exists and “False” if it does not. We can now use the “when” conditional keyword with the action we want to take.
Now, let’s add an action. Just to test this premise, we will use the debug module to print a message if this file exists.
- debug: msg: "The file myfile.txt exists!" when: myvar.stat.exists - debug: msg: "The file myfile.txt does not exists" when: myvar.stat.exists == False
This will print the correct line out, depending on if the file exists or not. Here is the output when the file exists:
We can see from the output, that the file exists. The second debug message is just skipped because it does not match.
Of course, we can perform any action that we want. Not just print the result. For example, we could install some piece of software if the file exists but uninstall it if it does not exist.
If for some reason, you wanted to make sure Apache is installed if the file exists, you could do something like this:
- apt: name: apache2 state: present when: myvar.stat.exists == True - apt: name: apache2 state: absent when: myvar.stat.exists == False
Check if it is a file or directory
As we saw in the output of the stat module, we can check for other things than just the existence of the file.
It might be important to know if we are working with a file or a directory. For this we can check for the “isdir” variable. If the path is not a directory, the “isdir” variable might be undefined. So we need to verify if it exists before checking the value. Otherwise, we might get an error.
- debug: msg: "myfile.txt is a directory!" when: myvar.stat.isdir is defined and myvar.stat.isdir
The simple way to create a file if it does not exist.
Just as a side note, you can also make sure a file exists with one command. I only include this in case somebody is using the stat module to create a file if it does not exist. Using the touch state in the file module. This way, the file will be created if it does not exist. Please note that if the file already exists it will be updated with a new modification date.
To skip updating the modification time and leave the file as is if it exists, you can add “modification_time: preserve”
- name: Touch a file. Creates the file if it does not exist. Otherwise, do nothing file: path: /etc/myfile.txt state: touch owner: root mode: 0644 modification_time: preserve access_time: preserve
Possible return values from stat
The ansible stat module is quite powerful and can provide us with a lot of information about a file. So using it in your ansible playbook can be useful for other things than checking if a file exists. I have compiled a list of possible return values from the stat module in the table below.
Variable | Type | Description |
---|---|---|
atime | float | Time of last access |
attributes | list of strings | List of file attributes |
charset | string | Character or encoding of file |
checksum | string | Hash of file |
ctime | float | Time of last metadata update or creation (depends on OS) |
dev | integer | Device the inode is on |
executeable | boolean | True if the executable permission is set for the current user |
exists | boolean | True if the destination file exists |
gid | integer | File owner’s group ID |
gr_name | string | File owner’s group name |
inode | integer | Inode number of the path |
isblk | boolean | True if the path is a block device |
ischr | boolean | True if the path is a character device |
islnk | boolean | True if the path is a symbolic link |
isreg | boolean | True if the path is a regular file |
issock | boolean | True if the path is a UNIX socket |
isuid | boolean | True if the current user is the owner |
lnk_source | string | Target of the symlink if the file is a symbolic link |
lnk_target | string | Target of the symlink if the file is a symbolic link |
mimetype | string | File magic data or mime-type |
mode | string | Unix permissions of the file in octal. Ex. “0644” |
mtime | float | Time of last modification |
nlink | integer | Number of hard links to the file |
path | string | Full path |
pw_name | string | Username of the owner |
readable | boolean | True if the invoking user has permission to read the path |
rgrp | boolean | True if the owner’s group has read permission |
roth | boolean | True if others have read permission |
rusr | boolean | True if the owning user has permission to read the path |
size | integer | Size of the file in bytes |
uid | integer | UID of the owner |
version | string | Version of the file according to the filesystem |
wgrp | boolean | True if the owner’s group has write permission |
woth | boolean | True if others have write permission |
writeable | boolean | True if the invoking user has write permission |
wusr | boolean | True if the owner has write permission |
xgrp | boolean | True if the owning user has executable permission |
xoth | boolean | True if others have executable permission |
xusr | boolean | True if the owning user has executable permission |
That’s it for now. I hope this helps. I will be writing more ansible tutorials in the future. In the meantime, you can check out some related articles here:
- How to access hostvars in Ansible.
- Arithmetic Operators in Ansible
- How to copy multiple files in Ansible