Ansible: Why you should use the assert module instead of failed_when

Achtung! Dieser Artikel ist älter als ein Jahr. Der Inhalt ist möglicherweise nicht mehr aktuell!

As an ansible user you may be familiar with the failed_when clause of a task. Its condition should resolve to a boolean value and determines if it was successful or not. In my backup playbook I’ve a task creating a tar archive by using the command module. Later on I want to check if an actual POSIX tar archive was created:

- name: Check if a real tar archive was created
  command: "file /mnt/backups/mail.veloc1ty.de/{{ ansible_date_time.date }}.tar"
  register: filecommandoutput
  failed_when: "'POSIX tar archive' not in filecommandoutput.stdout"

This task should fail when POSIX tar archive is not found in the file command output. If the string is not found the task should fail and force me to investigate the problem. However this could also be done in two tasks:

- name: Check if a real tar archive was created
  command: "file /mnt/backups/mail.veloc1ty.de/{{ ansible_date_time.date }}.tar"
  register: filecommandoutput

- name: Assert file command output
  assert:
    that: "'POSIX tar archive' not in filecommandoutput.stdout"

What’s the difference? Nothing! Both tasks fail perfectly if no tar archive is detected. The real advantage comes into play when you need to chain multiple conditions together, because the assert module will tell you which condition actually failed. This can be very helpful for you as a human debugging the playbook.

Maybe another example helps you understand the advantage: Another check I do in my backup playbook is counting the backed up mails. Here is how the tasks would look like with the failed_when clause:

- name: Check tar archive containing actual mails
  shell: "tar -tf /mnt/backups/mail.veloc1ty.de/{{ ansible_date_time.date }}.tar | grep /home/vmail/veloc1ty.de/hello/Maildir/cur/ | wc -l"
  register: taroutput
  failed_when: taroutput.stdout and taroutput.stdout | int and taroutput | int < 1000

The failed_when clause checks that taroutput.stdout

  1. exists
  2. is an integer
  3. is greater than 1000 (“more than 1000 mails are stored in that directory”)

Here is the resulting task output:

TASK [Check tar archive containing actual mails] ************************************
fatal: [XXX.veloc1ty.de]: FAILED! => {"changed": true, "cmd": "tar -tf /mnt/backups/mail.veloc1ty.de/2022-10-24.tar | grep /home/vmail/veloc1ty.de/hello/Maildir/cur/ | wc -l", "delta": "0:00:03.628501", "end": "2022-10-24 21:42:20.539682", "failed_when_result": true, "msg": "", "rc": 0, "start": "2022-10-24 21:42:16.911181", "stderr": "", "stderr_lines": [], "stdout": "15773", "stdout_lines": ["15773"]}

All we know now is that the task failed but we have no clue why. The next step is to add a debug tasks and manually check the condition. This can all be avoided by using the assert module. Here is the same task refactored:

- name: Check tar archive containing actual mails
  shell: "tar -tf /mnt/backups/mail.veloc1ty.de/{{ ansible_date_time.date }}.tar | grep /home/vmail/veloc1ty.de/hello/Maildir/cur/ | wc -l"
  register: taroutput

- name: Assert tar archive mail content
  assert:
    that:
      - taroutput.stdout is defined
      - taroutput.stdout | int
      - taroutput.stdout | int < 1000

The task output of course failed, too:

TASK [Assert tar archive mail content] ************************************
fatal: [XXX.veloc1ty.de]: FAILED! => {
    "assertion": "taroutput.stdout | int < 1000",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

The assert module tells us exactly which assertion failed. Of course I want to check if I’ve more than 1000 mails backed up and not less than 1000. Did you spot the error in the failed_when approach above?
Here is my rule of thumb: If you only have one condition: Use failed_when. If you have multiple conditions include the extra assert task and prevent your future self from adding debug tasks!


Du hast einen Kommentar, einen Wunsch oder eine Verbesserung? Schreib mir doch eine E-Mail! Die Infos dazu stehen hier.

🖇️ = Link zu anderer Webseite
🔐 = Webseite nutzt HTTPS (verschlüsselter Transportweg)
Zurück