# 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:

```yaml
- 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:

```yaml
- 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:

```yaml
- 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:

```yaml
 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.
