How Do Ansible Tags Work?

how do ansible tags workOne of the most confusing Ansible features is the tags, and in this blog, I will try to clarify how they work.

A tag is an attribute that you can set to an Ansible structure (plays, roles, tasks), and then when you run a playbook you can use –tags or –skip-tags to execute a subset of tasks.

Let’s look at this basic playbook example:

Looking at the play we can see two tags – install and configure. You can also list all tags in a play using –list-tags

You can list all tasks and tags with –list-tasks

Now to see the effect of tags we can –list-tasks applying tags filters to know which tasks will run. 

Ansible Tags Inheritance

So far, so good. Now let’s check how tags inheritance works. When you add a tag to a play or a static import (role or task), the included tasks inherit the tag. Let’s see an example:

This is the same play as before but I just added the pmm tag (Percona Monitoring and Management) to play structure. And we can see the inheritance: 

All the tasks now have the pmm tag.

Also, with roles:

When we set a tag on a role structure definition, all the tasks in those roles will inherit the tags. Which, in this case, means that all the tasks in the pmm role will have the pmm tag when we run this playbook.

As I mentioned before, inheritance only works with static imports (import_*). For dynamic includes (include_*), you would need to explicitly apply the tags.

Be careful with this if you also have include_tasks inside your roles! Those dynamic included tasks won’t inherit the tag and then won’t run. A common example would be to include tasks based on the OS family.

In this example, we had to re-apply tags for the dynamic included tasks. You could also be using static imports, but in that case, you would need to import two files and use when:  

In this case, imported tasks will inherit tags from the top, but also all tasks on both files will be processed inheriting the when conditional from the import statement. This means that the when condition is not applied to the import_tasks itself, but to all the tasks imported from it. So for large roles, I don’t recommend this, as still all tasks would need to be processed and the output would become hard to analyze.

Special Ansible Tags

As you may see in the latest playbook example, the percona_repo role has three tags: one for itself (percona_repo) and one for each role that installs packages (toolkit, pmm). So what if I keep adding roles for other package management that would require the percona_repo to be present?  In that case, I will need to keep adding more tags to the percona_repo role, which is not efficient. Of course, you can use dependencies on the role’s meta, but for the context of this blog, I will just use tags. For these cases, Ansible has two special tags: always and never

always tagged tasks will run unless you skip it with –skip-tags=always.

never tagged tasks won’t run unless a tag on it is specified to run –tags=never.

Let’s extend the initial sample and add always and never tags.

There is now a new task “Uninstall PMM2 client” with the never tag, and “Install PMM2 client” with always.

We can see the never task was skipped as expected.

Running with configure tag, the install task is also executed as we added the always tag.

Be careful with the inheritance effect. As we said:

  • Never tagged tasks run only if one the tags are specified to run
  • Tags are inherited from parent blocks

In this play, we see that the pmm tag is inherited from the play to the tags, so what happens if we run with the pmm tag?

The task tagged as never is executed as-is, inheriting the pmm tag. So you will need to be careful when you use role tasks that are inheriting tags.

Conclusion

Tags are a powerful feature in Ansible, as it allows you to run a subset of tasks, and are very helpful for large plays. But it is really important to understand how inheritance works, and the difference between dynamic and static imports.

Share this post

Leave a Reply