Ansible: Tags are a code smell
I have referred to this article by Michel Blanc a number of times, primarily because of one thing he says:
Do not overdo tags: most of the time, this is YAGNI (You Ain’t Gonna Need It)
What he says is absolutely the truth. Using Ansible tags creates all sorts of uncertainty in your playbooks and roles at run-time.
Your own code
Let’s look at an example. In this very simple playbook, we have four tasks: one with no tags, one with a non-special tag, one with the never
special tag, and one with the always
special tag:
We can try various invocations of ansible-playbook and observe what happens with each:
That’s quite a truth table, even with just four tags! Using tags to control the flow of your Ansible playbooks gets confusing quickly. It’s easy to make our example even more confusing by creating additional tasks with never
and always
tags:
- name: demo another always-tagged task
debug:
msg: I am another always-tagged task
tags:
- always
- another-always
What happens to that truth table, now? Tags in your own code are confusing enough, but let’s consider what happens when you inject dependencies into your playbooks.
Other peoples’ roles
Suppose you import someone else’s Ansible role through Galaxy or by other means, like this one. In my base playbook (we’ll just use the example, above) I never accounted for the other role author’s ‘security,’ ‘install,’ nor ‘configure’ tags when faithfully importing his (or her) role. If I run my own Ansible playbook and specify any of the tags I’m using, then the other author’s tasks simply won’t run.
Tags are a smell
You can’t use tags to *include* tasks without potentially skipping other tasks, and using special tags like ‘never’ and ‘always’ just makes running playbooks worse. Ansible’s command-line tools provide way too many ways to include or skip tags, leading to a difficult-to-diagnose mess.
It would be great to see Ansible Galaxy evolve to the point where its community just disallows tags in contributed content.
What to use instead of tags?
There are other ways to work around using tags:
- Use
when
conditionals and consider passing extra variables for conditional evaluation duringansible-playbook
invocations using the ‘-e’ flag. - Write separate playbooks and invoke those, instead of your main playbook, when doing something simple with Ansible like reloading a service. Note that you can include specific tasks when importing an Ansible using the
tasks_from
argument to an include_role task. - Other ideas? Feel free to hit me up!