It's a best practice for Ansible to check ansible version in playbook if that playbook uses some unusual features of Ansible. Sometime there is a subtle breakage which render playbook messing up production for real.

There can be other reasons to have a piece of code to run on every launch no matter what execution options are. Run it with any --limit, run it with any --tags, etc.

One part of the puzzle is easy to solve, just add tags: [always] and job is done.

Not really.

  1. You need to be really careful with `hosts` definition. If your --limit is not within your list, your task is ignored. And 'all' is not a solution: --limit localhost will ignore your hosts: all. Buhaha.
  2. Even if you are a smart cookie and say hosts: all, localhost, you'll get a lot of repeated code for big inventories. Most of the time it's not the thing you want.

Complete solution

- hosts: all, localhost
  gather_facts: false
  run_once: true
  tasks:
    - name: Check if numbers are not string
      assert:
        that: 11 > 9
        success_msg: 'Ansible is sane'
        fail_msg: 'Javascript is everywhere'
      tags:
        - always
        - number_check

As you can see, there are few tricks here:

  • run_once help us to reduce verbosity
  • gather_facts: false disable fact gathering (if you don't need facts at this stage it will greatly speeds everything)
  • We use tag always to make it run in all situations, except if user uses --skip-tag=always or --skip-tag=number_check.
  • We add second tag to always to help user to skip only our task. Any workflow with --skip-tag=always is strictly against best practices. Every always task should have a separate tag to allow localized skips without mass destruction weapon (It's said that --skip-tag=always is Ansible equivalent of atomic bomb).

How to use this guard

As you can see, there are quiet a lot of lines in this snippet. If you want to run this snippet for real for all possible cases, you need to put it into every playbook you have in your project.

That's done by placing a 'guard' snippet into something like guard.yaml or failsafe.yaml, and import_playbook it into every other playbook as the first two lines:

- name: Safeguard checks
  import_playbook: failsafe.yaml

Most big projects have site.yaml which import other playbooks. Each of them should have failsafe check on top (if you have those checks in the first place), and this is another reason why you want it to be as fast as possible.