Using the Ansible Stat Module on a Loop

Table of Contents

Using the Ansible stat module on a loop

Hi again, it’s been a while since I wrote something on this blog. This time I was working on a Ansible playbook and I get this:

Problem

I want to verify if a file exists. Depending on the registered output I want to perform some other actions. On my specific use case, I want to use in in conjunct with the file module to define the state on my next task.

graph TD; A(Stat over file) --> B{Does the file exists?}; B -->|Yes| C[state: file]; B -->|No| D[state: touch];

Solution

Here is the solution that worked for me, using loops, loop_control,and jinja2 filters.

- name: Stat over the files
  stat:
    path: "{{ my_loop }}"
    loop:
      - /etc/cron.allow
      - /etc/at.allow
    loop_control:
      loop_var: my_loop
    register: my_stat_var

- name: Create a file if not exists
  file:
    path: "{{ item.my_loop }}"
    owner: root
    group: root
    mode: og-rwx
    state: "{{ 'file' if item.stat.exists else 'touch' }}"
  loop: "{{ my_stat_var.results }}"

Basically, I’m using the new loop syntax to iterate over all the files that I want to check.

In order to avoid some warnings about the loop using item I implement the loop_control and loop_var syntax to control the loop behavior and on this specific case, instead of using the word item I will substitute it with the one that I define as my loop_var in this case is called my_loop (Remember this, I will use it on the next task).

I’m registering the result of the stat task on a variable, for this case is my_stat_var

Here is an example of the output, when the 2 files do not exists:

ok: [instance-amazon2] => {
    "my_loop": {
        "changed": false,
        "msg": "All items completed",
        "results": [
            {
                "ansible_loop_var": "my_loop",
                "at_cron_restricted_touch": "/etc/cron.allow",
                "changed": false,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "checksum_algorithm": "sha1",
                        "follow": false,
                        "get_attributes": true,
                        "get_checksum": true,
                        "get_md5": false,
                        "get_mime": true,
                        "path": "/etc/cron.allow"
                    }
                },
                "stat": {
                    "exists": false
                }
            },
            {
                "ansible_loop_var": "my_loop",
                "at_cron_restricted_touch": "/etc/at.allow",
                "changed": false,
                "failed": false,
                "invocation": {
                    "module_args": {
                        "checksum_algorithm": "sha1",
                        "follow": false,
                        "get_attributes": true,
                        "get_checksum": true,
                        "get_md5": false,
                        "get_mime": true,
                        "path": "/etc/at.allow"
                    }
                },
                "stat": {
                    "exists": false
                }
            }
        ]
    }
}

On the next task I access the results of the registered variable my_stat_var using, according to the previous output, I need to use the results variable to access it and gives me 2 arrays (each one for each file)

Also, I extract the path from the same variable my_stat_var.results but as it is part of a loop, I will access it using item.my_loop as on the the second task loop I’m not using a loop_control, also I can access it using item.item but I prefer the first syntax. In case you implement a loop_control on you can access it as my_second_loop.my_loop

Also, I extract if the file exists with the variable item.stat.exists but I’m putting that on a jinja2 filter, that way it will evaluate and set the correct option on the state

# When the file exists
state: file
# When the file does not exists
state: touch

This behavior also brings idempotency to your task, because if the files does not exists on the first iteration it will be created and the next time you run the tasks as the files already exists it will works as a stat and will return the current state of path.

That is all for now. See you soon!

Luis Cacho
Luis Cacho
Security Systems Administrator III

Security Systems Administrator at Rackspace | Kubernetes Enthusiast | Ansible Ninja

Related