Improve Ansible tasks by debugging them

Say you're working on the below task.

- name: Check if webroot exists
  stat:
    path: /var/www/public_html

When running your playbook, the task will return 'ok' whether the directory exists or not.

TASK [Check if webroot exists] *********************************  
ok: [203.0.113.2]  

That's not exactly what we want. How can we do something about it and make our task more accurate? Enter the debug module.

First, you need to register an arbitrary variable to identify your task.

- name: Check if webroot exists
  stat:
    path: /var/www/public_html
  register: webroot

Now, we can add the below code under the task:

- debug: msg={{ webroot }}

This will invoke the debug module and will return the result of the task for you to better understand what's going on.

TASK [debug] *****************************************************  
ok: [203.0.113.2] => {  
    "msg": {
        "changed": false,
        "stat": {
            "exists": false
        }
    }
}

Above, we can see it returned false, which means our directory doesn't exist. Good. Now, let's improve our task to reflect that.

We essentially have three options. Either we want the playbook to continue, either we want it to fail...but continue, or we want it to fail immediately. It's up to you to decide what should happen now. Is the task required for you to proceed in the playbook? Then make it fail (Or fix it on the fly. More below). Is it not critical to the execution of the playbook? Then let it run. Ansible offers the changed_when or failed_when parameters to achieve just that. It also offers a few more fine-grained options (e.g. max_fail_percentage, any_errors_fatal) for advanced usage. Please refer to Ansible's error handling for all details.

Below, we'll be forcing the task to fail immediately if the directory doesn't exist.

- name: Check if webroot exists
  stat:
    path: /var/www/public_html
  register: webroot
  failed_when: webroot.stat.exists == false

Note the failed_when construct: we're drilling down through the previous debug output to narrow-down our check to the true/false boolean only.

If we run the playbook again, it'll stop immediately upon failure.

TASK [Check if webroot exists] *************************************************  
fatal: [203.0.113.2]: FAILED! => {"changed": false, "failed": true, "failed_when_result": true, "stat": {"exists": false}}  

The playbook recap will also show a failure.

PLAY RECAP *******************************************************  
203.0.113.2      : ok=76    changed=3    unreachable=0    failed=1  

Alternatively, we can make it fail but continue with the remaining tasks. This needs us to add a new ignore_errors parameter.

- name: Check if webroot exists
  stat:
    path: /var/www/public_html
  register: webroot
  failed_when: webroot.stat.exists == false
  ignore_errors: true

We have a failure but the playbook continues to run.

TASK [Check if webroot exists] *************************************************  
fatal: [203.0.113.2]: FAILED! => {"changed": false, "failed": true, "failed_when_result": true, "stat": {"exists": false}}  
...ignoring

Sure it'll show a big red line to indicate failure, but if you have tens of tasks, you might not see it. The playbook recap will also not outline a failure, which is why I wouldn't necessarily recommend this option.

What about fixing the issue on the fly instead? Because after all that's what we want, and nothing else.

- name: Check if webroot exists
  stat:
    path: /var/www/public_html
  register: webroot

- name: Create webroot directory
  file:
    path: /var/www/public_html
    state: directory
    mode: 0755
  when: webroot.stat.exists == false

What we're doing here is we're first checking if the directory exists and we're not making any assumption about it. The task will return 'ok' because it has performed its check successfully no matter the result. What's more interesting is we're then creating the missing directory if and only if it doesn't already exist. And to do that we're relying on the output the debug module gave us previously. Nifty.

Here's what your playbook output will now look like on the first run:

TASK [Check if webroot exists] *************************************************  
ok: [203.0.113.2]                                                                                                                               

TASK [Create webroot directory] *********************************  
changed: [203.0.113.2]  

And on subsequent runs:

TASK [Check if webroot exists] *************************************************  
ok: [203.0.113.2]

TASK [Create webroot directory] ************************************************  
skipping: [203.0.113.2]  

Any task for which the condition will not be met will be automatically skipped. This considerably speeds up playbook runs.

Improving your tasks to make them more relevant and reliable starts with understanding the underlying debug code generated by your Ansible modules. Use it!

Aurelien Navarre

Read more posts by this author.

Lyon, France