Skip to main content

Command Palette

Search for a command to run...

Using tags to express Ansible tasks dependencies

Updated
2 min read
Using tags to express Ansible tasks dependencies

Everything is simple when we write a playbook that does one thing. We have a powerful tool for expressing dependencies: the task order. But sometimes we want more than one target. We want to deploy something, then cleanup something. Or even build something else. For example, we may want a standard CI/CD workflow:

  • Build a Docker image from source.

  • Push the image to a registry.

  • Update a K8s deployment with the new image.

The playbook structure would be:

- name: Build image...
- name: Push to a registry...
- name: Update deployment...

Now, your boss comes and says "don't publish anything before I tell you so". We add tags to run tasks separately:

- name: Build image...
  tags: [build]
- name: Push to registry...
  tags: [publish]
- name: Update deployment...
  tags: [deploy]

Almost there. We can do everything we want, but we need to specify multiple tags. You can create a serious mess this way. So, the third and final approach is to populate a tag to every tag we're dependent on:

- name: Build image...
  tags: [build, publish, deploy]
- name: Push to registry...
  tags: [publish, deploy]
- name: Update deployment...
  tags: [deploy]

Here you go. If you want to deploy all - use tag 'deploy'. For build only - 'build'. That would be it, except for one caveat. We don't want to run unnecessary tasks. Theoretically - it shouldn't be a problem. Ansible modules are meant to be idempotent. It means - we can run a task any number of times we want - the result should be the same. But sometimes we're wasting our time. Then it would be good to have a 'when' clause to check whether running a task is really necessary.

And one bonus thing. If you create a complex playbook with multiple targets, having a default workflow, without tags makes no sense. For example, you don't want to build something then destroy it. In that case - use a default tag guard:

 pre_tasks:
    - name: Default tag guard
      assert:
        that: "'all' not in ansible_run_tags"
        msg: No default tasks here.

Thanks for the audience. I hope I managed to write something which is not obvious for everyone.