Ansible tags are another great feature which can help you execute respective tasks from the playbook. By default all the tasks from the playbook are executed but with tags we can control this behaviour and execute only the tasks with the matching tags.
Understanding Tags
- Tags are used at resource level to give a name to a specific resource
- The
--tags
option is used withansible-playbook
to run only resources with a specific tag - When a task file is included in a playbook, it can be tagged in the include statement
- When
ansible-playbook --tags "tagname"
is used, only resources marked with those tags will run. That would mean that if a resource at the same level doesn’t have a tag then it won’t run - Use
--skip-tags 'tagname'
to exclude resources with a specific tag - The special tag can be used to make sure a resource is always executed, unless specifically excluded with
--skip-tags
- The
--tags
option can take 3 specific tags as argument- tagged runs any tagged resource
- untagged excludes all tagged resources
- all runs all tasks (which is also the default behaviour when no tags have been specified)
NOTE:
You should take care of the indentation, tags should take the same amount of whitespace as used by the module definition i.e. module name
or name
under the tasks section (but without the hyphen).
ALSO READ: How to provision AWS EC2 Instances using Ansible
Example-1: Add tags to all the tasks
In this example I will prepare my sample playbook ansible-tags-1.yml
with four tasks which will just print a message on the console. I will add a different tag with each task so we can use these individual tags to execute the mapping task
--- - name: Ansible Tags hosts: localhost gather_facts: false tasks: - debug: msg: "This is first task" tags: first - debug: msg: "This is second task" tags: second - debug: msg: "This is third task" tags: third - debug: msg: "This is fourth task" tags: fourth
Now if I execute this playbook without any additional command line argument then all the tasks will be executed by default:
[ansible@controller ~]$ ansible-playbook ansible-tags-1.yml PLAY [Ansible Tags] ************************************************************************************************ TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is first task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is second task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is third task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is fourth task" } PLAY RECAP ********************************************************************************************************* localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
But we can control this behaviour by using tags
, assuming we only wish to execute the first task so we can use «--tags first
» with the same command:
[ansible@controller ~]$ ansible-playbook ansible-tags-1.yml --tags first PLAY [Ansible Tags] ************************************************************************************************ TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is first task" } PLAY RECAP ********************************************************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
So all other tasks are excluded from the play. We can add more tags to the list, for example to call task1 and task3 we will use:
[ansible@controller ~]$ ansible-playbook ansible-tags-1.yml --tags first,third PLAY [Ansible Tags] ************************************************************************************************ TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is first task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is third task" } PLAY RECAP ********************************************************************************************************* localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
So we just need to give the mapping tag name to execute the respective task
ALSO READ: Working with Ansible Handlers
Example-2: Exclude tasks using tags
Now in the above example we were executing the tasks by using the mapping tag name. But if you have 100s of tags and you only wish to exclude few of the tasks then it is not a good practice to provide all those tags through the command line.
In such case we can use --skip-tags
to call all the other tags except the provided tags with --skip-tags
. For example here I am executing all the tags except for the task mapped with tag fourth
[ansible@controller ~]$ ansible-playbook ansible-tags-1.yml --skip-tags fourth PLAY [Ansible Tags] ************************************************************************************************ TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is first task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is second task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is third task" } PLAY RECAP ********************************************************************************************************* localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Example-3: Using same tag for multiple tasks
We can also use the same tag name for multiple tasks so that with a single tag name we can execute multiple tasks. I have updated my playbook to assign another tag name for my first and third task.
--- - name: Ansible Tags hosts: localhost gather_facts: false tasks: - debug: msg: "This is first task" tags: - first - general - debug: msg: "This is second task" tags: second - debug: msg: "This is third task" tags: - third - general - debug: msg: "This is fourth task" tags: fourth
I have created an additional tag «general
» for first and third task so now I can only use this tag if I want to execute first and third task instead of using two different tags all the time:
ALSO READ: Getting started with Ansible
Let us verify this configuration
[ansible@controller ~]$ ansible-playbook ansible-tags-1.yml --tags general PLAY [Ansible Tags] ************************************************************************************************ TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is first task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is third task" } PLAY RECAP ********************************************************************************************************* localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Example-4: Disable one or more tasks using tags
We know that by default all the tasks from a play are executed but for some debugging purpose if you wish to disable a task then we can achieve this using tags. You can just assign the respective task «never
» tag and that task will not be executed by default unless you specifically call the tag name of that task.
For example I have added never
tag to my fourth task
--- - name: Ansible Tags hosts: localhost gather_facts: false tasks: - debug: msg: "This is first task" tags: - first - general - debug: msg: "This is second task" tags: second - debug: msg: "This is third task" tags: - third - general - debug: msg: "This is fourth task" tags: - fourth - never
Let me execute the playbook without any command line arguments:
[ansible@controller ~]$ ansible-playbook ansible-tags-1.yml PLAY [Ansible Tags] ************************************************************************************************ TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is first task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is second task" } TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is third task" } PLAY RECAP ********************************************************************************************************* localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
So the fourth task was not executed here. But we can execute the fourth task by using --tags fourth
[ansible@controller ~]$ ansible-playbook ansible-tags-1.yml --tags fourth PLAY [Ansible Tags] ************************************************************************************************ TASK [debug] ******************************************************************************************************* ok: [localhost] => { "msg": "This is fourth task" } PLAY RECAP ********************************************************************************************************* localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
In such case the never
tag control is overwritten by the command line argument with a higher precedence.
ALSO READ: Ansible Installation Guide on RHEL/CentOS 8
What’s Next
Next in our Ansible Tutorial we will learn about ansible blocks and rescue which can be used for error and recovery situations in real time environments
Didn’t find what you were looking for? Perform a quick search across GoLinuxCloud
If you have a large playbook, it may be useful to run only specific parts of it instead of running the entire playbook. You can do this with Ansible tags. Using tags to execute or skip selected tasks is a two-step process:
Add tags to your tasks, either individually or with tag inheritance from a block, play, role, or import.
Select or skip tags when you run your playbook.
-
Adding tags with the tags keyword
-
Adding tags to individual tasks
-
Adding tags to includes
-
Tag inheritance: adding tags to multiple tasks
-
Adding tags to blocks
-
Adding tags to plays
-
Adding tags to roles
-
Adding tags to imports
-
Tag inheritance for includes: blocks and the
apply
keyword
-
-
-
Special tags: always and never
-
Selecting or skipping tags when you run a playbook
-
Previewing the results of using tags
-
Selectively running tagged tasks in re-usable files
-
Configuring tags globally
-
Adding tags with the tags keyword
You can add tags to a single task or include. You can also add tags to multiple tasks by defining them at the level of a block, play, role, or import. The keyword tags
addresses all these use cases. The tags
keyword always defines tags and adds them to tasks; it does not select or skip tasks for execution. You can only select or skip tasks based on tags at the command line when you run a playbook. See Selecting or skipping tags when you run a playbook for more details.
Adding tags to individual tasks
At the simplest level, you can apply one or more tags to an individual task. You can add tags to tasks in playbooks, in task files, or within a role. Here is an example that tags two tasks with different tags:
tasks: - name: Install the servers ansible.builtin.yum: name: - httpd - memcached state: present tags: - packages - webservers - name: Configure the service ansible.builtin.template: src: templates/src.j2 dest: /etc/foo.conf tags: - configuration
You can apply the same tag to more than one individual task. This example tags several tasks with the same tag, “ntp”:
--- # file: roles/common/tasks/main.yml - name: Install ntp ansible.builtin.yum: name: ntp state: present tags: ntp - name: Configure ntp ansible.builtin.template: src: ntp.conf.j2 dest: /etc/ntp.conf notify: - restart ntpd tags: ntp - name: Enable and run ntpd ansible.builtin.service: name: ntpd state: started enabled: true tags: ntp - name: Install NFS utils ansible.builtin.yum: name: - nfs-utils - nfs-util-lib state: present tags: filesharing
If you ran these four tasks in a playbook with --tags ntp
, Ansible would run the three tasks tagged ntp
and skip the one task that does not have that tag.
Adding tags to includes
You can apply tags to dynamic includes in a playbook. As with tags on an individual task, tags on an include_*
task apply only to the include itself, not to any tasks within the included file or role. If you add mytag
to a dynamic include, then run that playbook with --tags mytag
, Ansible runs the include itself, runs any tasks within the included file or role tagged with mytag
, and skips any tasks within the included file or role without that tag. See Selectively running tagged tasks in re-usable files for more details.
You add tags to includes the same way you add tags to any other task:
--- # file: roles/common/tasks/main.yml - name: Dynamic re-use of database tasks include_tasks: db.yml tags: db
You can add a tag only to the dynamic include of a role. In this example, the foo
tag will not apply to tasks inside the bar
role:
--- - hosts: webservers tasks: - name: Include the bar role include_role: name: bar tags: - foo
With plays, blocks, the role
keyword, and static imports, Ansible applies tag inheritance, adding the tags you define to every task inside the play, block, role, or imported file. However, tag inheritance does not apply to dynamic re-use with include_role
and include_tasks
. With dynamic re-use (includes), the tags you define apply only to the include itself. If you need tag inheritance, use a static import. If you cannot use an import because the rest of your playbook uses includes, see Tag inheritance for includes: blocks and the apply keyword for ways to work around this behavior.
Tag inheritance: adding tags to multiple tasks
If you want to apply the same tag or tags to multiple tasks without adding a tags
line to every task, you can define the tags at the level of your play or block, or when you add a role or import a file. Ansible applies the tags down the dependency chain to all child tasks. With roles and imports, Ansible appends the tags set by the roles
section or import to any tags set on individual tasks or blocks within the role or imported file. This is called tag inheritance. Tag inheritance is convenient, because you do not have to tag every task. However, the tags still apply to the tasks individually.
Adding tags to blocks
If you want to apply a tag to many, but not all, of the tasks in your play, use a block and define the tags at that level. For example, we could edit the NTP example shown above to use a block:
# myrole/tasks/main.yml - name: ntp tasks tags: ntp block: - name: Install ntp ansible.builtin.yum: name: ntp state: present - name: Configure ntp ansible.builtin.template: src: ntp.conf.j2 dest: /etc/ntp.conf notify: - restart ntpd - name: Enable and run ntpd ansible.builtin.service: name: ntpd state: started enabled: true - name: Install NFS utils ansible.builtin.yum: name: - nfs-utils - nfs-util-lib state: present tags: filesharing
Adding tags to plays
If all the tasks in a play should get the same tag, you can add the tag at the level of the play. For example, if you had a play with only the NTP tasks, you could tag the entire play:
- hosts: all tags: ntp tasks: - name: Install ntp ansible.builtin.yum: name: ntp state: present - name: Configure ntp ansible.builtin.template: src: ntp.conf.j2 dest: /etc/ntp.conf notify: - restart ntpd - name: Enable and run ntpd ansible.builtin.service: name: ntpd state: started enabled: true - hosts: fileservers tags: filesharing tasks: ...
Adding tags to roles
There are three ways to add tags to roles:
Add the same tag or tags to all tasks in the role by setting tags under
roles
. See examples in this section.Add the same tag or tags to all tasks in the role by setting tags on a static
import_role
in your playbook. See examples in Adding tags to imports.Add a tag or tags to individual tasks or blocks within the role itself. This is the only approach that allows you to select or skip some tasks within the role. To select or skip tasks within the role, you must have tags set on individual tasks or blocks, use the dynamic
include_role
in your playbook, and add the same tag or tags to the include. When you use this approach, and then run your playbook with--tags foo
, Ansible runs the include itself plus any tasks in the role that also have the tagfoo
. See Adding tags to includes for details.
When you incorporate a role in your playbook statically with the roles
keyword, Ansible adds any tags you define to all the tasks in the role. For example:
roles: - role: webserver vars: port: 5000 tags: [ web, foo ]
or:
--- - hosts: webservers roles: - role: foo tags: - bar - baz # using YAML shorthand, this is equivalent to: # - { role: foo, tags: ["bar", "baz"] }
Adding tags to imports
You can also apply a tag or tags to all the tasks imported by the static import_role
and import_tasks
statements:
--- - hosts: webservers tasks: - name: Import the foo role import_role: name: foo tags: - bar - baz - name: Import tasks from foo.yml import_tasks: foo.yml tags: [ web, foo ]
Tag inheritance for includes: blocks and the apply
keyword
By default, Ansible does not apply tag inheritance to dynamic re-use with include_role
and include_tasks
. If you add tags to an include, they apply only to the include itself, not to any tasks in the included file or role. This allows you to execute selected tasks within a role or task file — see Selectively running tagged tasks in re-usable files when you run your playbook.
If you want tag inheritance, you probably want to use imports. However, using both includes and imports in a single playbook can lead to difficult-to-diagnose bugs. For this reason, if your playbook uses include_*
to re-use roles or tasks, and you need tag inheritance on one include, Ansible offers two workarounds. You can use the apply
keyword:
- name: Apply the db tag to the include and to all tasks in db.yaml include_tasks: file: db.yml # adds 'db' tag to tasks within db.yml apply: tags: db # adds 'db' tag to this 'include_tasks' itself tags: db
Or you can use a block:
- block: - name: Include tasks from db.yml include_tasks: db.yml tags: db
Special tags: always and never
Ansible reserves two tag names for special behavior: always and never. If you assign the always
tag to a task or play, Ansible will always run that task or play, unless you specifically skip it (--skip-tags always
).
For example:
tasks: - name: Print a message ansible.builtin.debug: msg: "Always runs" tags: - always - name: Print a message ansible.builtin.debug: msg: "runs when you use tag1" tags: - tag1
Warning
-
Fact gathering is tagged with ‘always’ by default. It is only skipped if
you apply a tag and then use a different tag in--tags
or the same
tag in--skip-tags
.
Warning
-
The role argument specification validation task is tagged with ‘always’ by default. This validation
will be skipped if you use--skip-tags always
.
New in version 2.5.
If you assign the never
tag to a task or play, Ansible will skip that task or play unless you specifically request it (--tags never
).
For example:
tasks: - name: Run the rarely-used debug task ansible.builtin.debug: msg: '{{ showmevar }}' tags: [ never, debug ]
The rarely-used debug task in the example above only runs when you specifically request the debug
or never
tags.
Selecting or skipping tags when you run a playbook
Once you have added tags to your tasks, includes, blocks, plays, roles, and imports, you can selectively execute or skip tasks based on their tags when you run ansible-playbook. Ansible runs or skips all tasks with tags that match the tags you pass at the command line. If you have added a tag at the block or play level, with roles
, or with an import, that tag applies to every task within the block, play, role, or imported role or file. If you have a role with lots of tags and you want to call subsets of the role at different times, either use it with dynamic includes, or split the role into multiple roles.
ansible-playbook offers five tag-related command-line options:
-
--tags all
— run all tasks, ignore tags (default behavior) -
--tags [tag1, tag2]
— run only tasks with either the tagtag1
or the tagtag2
-
--skip-tags [tag3, tag4]
— run all tasks except those with either the tagtag3
or the tagtag4
-
--tags tagged
— run only tasks with at least one tag -
--tags untagged
— run only tasks with no tags
For example, to run only tasks and blocks tagged configuration
and packages
in a very long playbook:
ansible-playbook example.yml --tags "configuration,packages"
To run all tasks except those tagged packages
:
ansible-playbook example.yml --skip-tags "packages"
Previewing the results of using tags
When you run a role or playbook, you might not know or remember which tasks have which tags, or which tags exist at all. Ansible offers two command-line flags for ansible-playbook that help you manage tagged playbooks:
-
--list-tags
— generate a list of available tags -
--list-tasks
— when used with--tags tagname
or--skip-tags tagname
, generate a preview of tagged tasks
For example, if you do not know whether the tag for configuration tasks is config
or conf
in a playbook, role, or tasks file, you can display all available tags without running any tasks:
ansible-playbook example.yml --list-tags
If you do not know which tasks have the tags configuration
and packages
, you can pass those tags and add --list-tasks
. Ansible lists the tasks but does not execute any of them.
ansible-playbook example.yml --tags "configuration,packages" --list-tasks
These command-line flags have one limitation: they cannot show tags or tasks within dynamically included files or roles. See Comparing includes and imports: dynamic and static re-use for more information on differences between static imports and dynamic includes.
Selectively running tagged tasks in re-usable files
If you have a role or a tasks file with tags defined at the task or block level, you can selectively run or skip those tagged tasks in a playbook if you use a dynamic include instead of a static import. You must use the same tag on the included tasks and on the include statement itself. For example you might create a file with some tagged and some untagged tasks:
# mixed.yml tasks: - name: Run the task with no tags ansible.builtin.debug: msg: this task has no tags - name: Run the tagged task ansible.builtin.debug: msg: this task is tagged with mytag tags: mytag - block: - name: Run the first block task with mytag ... - name: Run the second block task with mytag ... tags: - mytag
And you might include the tasks file above in a playbook:
# myplaybook.yml - hosts: all tasks: - name: Run tasks from mixed.yml include_tasks: name: mixed.yml tags: mytag
When you run the playbook with ansible-playbook -i hosts myplaybook.yml --tags "mytag"
, Ansible skips the task with no tags, runs the tagged individual task, and runs the two tasks in the block.
Configuring tags globally
If you run or skip certain tags by default, you can use the TAGS_RUN and TAGS_SKIP options in Ansible configuration to set those defaults.
Sometimes, you might want to run specific tasks instead of running an entire playbook file. This helps to reduce the total playbook execution time, especially when dealing with a large playbook file.
So, what are tags?
Tags in playbooks are pieces of metadata that are attached to tasks in a playbook file. They are referred to when running playbooks and allow you to selectively target certain tasks at runtime. Basically, the instruct Ansible to execute (or not to execute) specific tasks within the playbook file.
Usually, tasks that are skipped have been carried out and hence there is no need to carry them out once again. This way, tags avoid repetition and optimize playbook execution time. They are handy when you want to run certain tasks on demand.
In this guide, we will delve into Ansible Tags and demonstrate how you can attach tags to tasks to determine playbook execution.
Execute a specific task in a playbook
Let us take an example of a playbook file that executes three tasks as shown. The tags are specified using the tags label at the end of each task.
--- - name: Ansible Tags example hosts: localhost gather_facts: False tasks: - name: Hello World tag example debug: msg: "Hello World!" tags: - hello - name: Welcome to Ansible Tags tag example debug: msg: "How are you?" tags: - welcome - name: Enjoy tag example debug: msg: "Enjoy!" tags: - enjoy
In this playbook, we have three tags: hello, welcome, and enjoy.
$ sudo ansible-playbook /etc/ansible/ansible-01-tags.yml --list-tags
As mentioned earlier, you can use tags to control the execution of Ansible playbooks. To specify which task to execute use the -t or –tags flag.
In the command below, we are instructing Ansible to execute the first task only which has been tagged as hello.
$ sudo ansible-playbook /etc/ansible/ansible-01-tags.yml --tags hello
Skip specific tags in a Playbook
You can also instruct Ansible to skip specific tags using the –skip-tags flag. In doing so, Ansible will run the rest of the tasks in the Playbook file with the exception of the task provided.
In this example, Ansible ignores the last task which is tagged enjoy and executes the rest of the tasks defined in the playbook.
$ sudo ansible-playbook /etc/ansible/ansible-01-tags.yml --skip-tags enjoy
Ensure that a task always (or never) runs
Even as you use tags to determine which tasks to be executed in a playbook file, sometimes, you find that there are tasks that you need to execute. Take, for example, a playbook that installs Apache on the remote webserver.
The playbook has 3 tasks. It first updates the package lists on the remote host, installs Apache, and restarts it. However, before installing Apache, it is required to update the package lists or refresh the repositories.
Since refreshing the repositories is a prerequisite, We will tag this task with the always tag.
--- - name: install Apache webserver hosts: webserver tasks: - name: Update and upgrade apt packages apt: update_cache: yes tags: - always - name: install Apache on Ubuntu apt: name: apache2 state: latest tags: - install_apache - name: Restart Apache Webserver service: name: apache2 state: restarted tags: - restart_apache
Without any tags, the playbook runs as expected.
$ sudo ansible-playbook /etc/ansible/ansible-02-tags.yml
If we specify to run a specific task other than the one that is mandatory (which in this case is the first task) Ansible will also execute the task that bears the ‘always’ tag.
In this example, we have instructed Ansible to restart Apache which bears the ‘restart_apache’ tag. However, the ‘update apt repository’ task still runs since it has the ‘always’ tag.
$ sudo ansible-playbook /etc/ansible/ansible-02-tags.yml --tags restart_apache
In the same vein, you can tell Ansible never to run a task, and for this, we use the ‘never‘ tag. This is the exact opposite of the ‘always’ tag.
Back to our playbook. Suppose you don’t want to restart Apache after installing. To achieve this, simply pass the never tag as shown.
- name: Restart Apache Webserver service: name: apache2 state: restarted tags: - restart_apache - never
This can also be tagged as follows using the double square brackets:
tags: [ restart_apache, never ]
When the playbook is executed without any tags, the task will be omitted.
$ sudo ansible-playbook /etc/ansible/ansible-02-tags.yml
From the output, you can see that restart apache task has been skipped.
NOTE:
A task that is tagged ‘never‘ will still run when it is explicitly called in the ansible-playbook command. For example, the task that restarts Apache will still be executed since it has explicitly been defined in the command.
$ sudo ansible-playbook /etc/ansible/ansible-02-tags.yml --tags restart_apache
Wrapping up
Ansible tags are handy and provide the flexibility needed in running Ansible playbooks. You can execute or skip executing specific tasks in a playbook. This provides better control of your playbooks during playbook runtime.
Read Also: How to Use Ansible Vault to Secure Sensitive Data
One 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
— — hosts: localhost tasks: — name: Install PMM2 client yum: name: pmm-client2 state: present tags: — install — name: create pmm-admin flags source template: src: pmm-admin-flags.txt.j2 dest: /etc/pmm/pmm-admin-flags.txt tags: — configure — name: setup pmm-agent command: «pmm-agent setup —force @/etc/pmm/pmm-agent-flags.txt» tags: — configure ... |
Looking at the play we can see two tags – install and configure. You can also list all tags in a play using –list-tags
% ansible—playbook tags.yaml —list—tags playbook: tags.yaml play #1 (local): local TAGS: [] TASK TAGS: [configure, install] |
You can list all tasks and tags with –list-tasks
% ansible—playbook tags.yaml —list—tasks playbook: tags.yaml play #1 (local): local TAGS: [] tasks: Install PMM2 client TAGS: [install] create pmm—admin flags source TAGS: [configure] setup pmm—agent TAGS: [configure] |
Now to see the effect of tags we can –list-tasks applying tags filters to know which tasks will run.
% ansible—playbook tags.yaml —list—tasks —tags=install playbook: tags.yaml play #1 (local): local TAGS: [] tasks: Install PMM2 client TAGS: [install] |
% ansible—playbook tags.yaml —list—tasks —tags=configure playbook: tags.yaml play #1 (local): local TAGS: [] tasks: create pmm—admin flags source TAGS: [configure] setup pmm—agent TAGS: [configure] |
% ansible—playbook tags.yaml —list—tasks —skip—tags=install playbook: tags.yaml play #1 (local): local TAGS: [] tasks: create pmm—admin flags source TAGS: [configure] setup pmm—agent TAGS: [configure] |
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:
— — hosts: local tags: — pmm tasks: — name: Install PMM2 client |
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:
% ansible—playbook tags.yaml —list—tasks playbook: tags.yaml play #1 (local): local TAGS: [pmm] tasks: Install PMM2 client TAGS: [install, pmm] create pmm—admin flags source TAGS: [configure, pmm] setup pmm—agent TAGS: [configure, pmm] |
All the tasks now have the pmm tag.
Also, with roles:
— hosts: dbservers,!rds become: yes roles: — role: percona_repo tags: — percona_repo — toolkit — pmm — role: percona-toolkit tags: — toolkit — role: pmm tags: — pmm |
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.
— name: Static import pmm role import_role: pmm tags: — pmm — name: Dynamic include pmm role include_role: pmm apply: tags: — pmm tags: — pmm |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
% cat roles/pmm/tasks/main.yaml — — name: include {{ ansible_os_family }} tasks include_tasks: «{{ ansible_os_family }}.yaml» apply: tags: — pmm % cat roles/pmm/tasks/Debian.yaml — — name: Install pmm—client with apt apt: name: pmm—client2 state: present % cat roles/pmm/tasks/RedHat.yaml — — name: Install pmm—client with yum yum: name: pmm—client2 state: present |
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:
— name: import tasks for RedHat import_tasks: RedHat.yaml when: ansible_os_family == ‘RedHat’ — name: import tasks for Debian import_tasks: Debian.yaml when: ansible_os_family == ‘Debian’ |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
— — hosts: localhost tags: — pmm tasks: — name: Uninstall PMM2 client yum: name: pmm-client2 state: absent tags: — never — uninstall — name: Install PMM2 client yum: name: pmm-client2 state: present tags: — install — always — name: create pmm-admin flags source template: src: pmm-admin-flags.txt.j2 dest: /etc/pmm/pmm-admin-flags.txt tags: — configure — name: setup pmm-agent command: «pmm-agent setup —force @/etc/pmm/pmm-agent-flags.txt» tags: — configure ... |
There is now a new task “Uninstall PMM2 client” with the never tag, and “Install PMM2 client” with always.
% ansible—playbook tags.yaml —list—tasks playbook: tags.yaml play #1 (localhost): localhost TAGS: [pmm] tasks: Install PMM2 client TAGS: [always, install, pmm] create pmm—admin flags source TAGS: [configure, pmm] setup pmm—agent TAGS: [configure, pmm] |
We can see the never task was skipped as expected.
% ansible—playbook tags.yaml —list—tasks —tags=configure playbook: tags.yaml play #1 (localhost): localhost TAGS: [pmm] tasks: Install PMM2 client TAGS: [always, install, pmm] create pmm—admin flags source TAGS: [configure, pmm] setup pmm—agent TAGS: [configure, pmm] |
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?
% ansible—playbook tags.yaml —list—tasks —tags=pmm playbook: tags.yaml play #1 (localhost): localhost TAGS: [pmm] tasks: Uninstall PMM2 client TAGS: [never, pmm, uninstall] Install PMM2 client TAGS: [always, install, pmm] create pmm—admin flags source TAGS: [configure, pmm] setup pmm—agent TAGS: [configure, pmm] |
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.