For long playbooks, it is useful to be able to run subsets of the tasks in the playbook. To do this, tags can be set on specific resources as a text label. Tagging a resource only requires that the tags keyword be used, followed by a list of tags to apply. When plays are tagged, the –tags option can be used with ansible-playbook to filter the playbook to only execute specific tagged plays.
Tags are available for the following resources:
- In playbooks, each task can be tagged, using the tags keyword:
1 2 3 4 5 6 7 8 |
tasks: - name: Install {{ item }} package yum: name={{ item }} state=installed with_items: - postfix - mariadb-server tags: - packages |
- When a task file is included in a playbook, the task can be tagged, allowing administrators to set a global tag for the include statement:
1 2 |
- include: common.yml tags: [webproxy, webserver] |
Note
When tagging a role or an include statement, all tasks they define are also tagged.
1 2 |
roles: - { role: databases, tags: ['production', 'staging'] } |
Important
When roles or include statements are tagged, the tag is not a way to exclude some of the tagged tasks the included files contain. Tags in this context are a way to apply a global tag to all tasks.
Managing tagged resources
When a tag has been applied to a resource, the ansible-playbook command can be used with the –tags or –skip-tags argument to either execute the tagged resources, or prevent the tagged resources from being included in the play. The following playbook contains two tasks. The first one is tagged with the production tag, the other one does not have any tag associated with it:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
--- - hosts: all tasks: - name: My tagged task yum: name: httpd state: latest tags: production - name: Installs postfix yum: name: postfix state: latest |
To only run the first task, the –tags argument can be used:
1 2 3 4 |
[user@demo ~]$ ansible-playbook main.yml --tags 'production' TASK [My tagged task] *************************************************** ok: [demo.example.com] ... Output omitted ... |
Because the –tags option was specified, the playbook only ran one task, the task tagged with the production tag. To skip the first task and only run the second task, the –skip-tags option can be used:
1 2 3 4 |
[user@demo ~]$ ansible-playbook main.yml --skip-tags 'production' ... Output omitted ... TASK [Installs postfix] ************************************************** ok: [demo.example.com] |
Special tags
Ansible has a special tag that can be assigned in a playbook: always. This tag causes the task to always be executed even if the –skip-tags option is used, unless explicitly skipped with –skip-tags always.
There are three special tags that can be used from the command-line with the –tags option:
1. The tagged keyword is used to run any tagged resource.
2. The untagged keyword does the opposite of the tagged keyword by excluding all tagged resources from the play.
3. The all keyword allows administrators to include all tasks in the play. This is the default behavior of the command line.
Example 1. Selectively Run Specific Tasks In Playbooks Using Tags
The playbook has two tasks. First copy inde html to the managedhost1 and is tagged as webdeploy. Second copy sql script to the managedhost2 and is tagged as dbdeploy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[miro@controlnode playbooks]$ cat playbook7.yml --- - hosts: managedhost1 become: yes tasks: - name: deploy app binary copy: src: /home/miro/apps/index.html dest: /var/www/html/index.html tags: - webdeploy - hosts: managedhost2 become: yes tasks: - name: deploy db script copy: src: /home/miro/apps/script.sql dest: /opt/script.sql tags: - dbdeploy |
You can run only first or only second tasks using a tags:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[miro@controlnode playbooks]$ ansible-playbook playbook7.yml --tags webdeploy PLAY [managedhost1] ******************************************************************************************************************************* TASK [Gathering Facts] **************************************************************************************************************************** ok: [managedhost1] TASK [deploy app binary] ************************************************************************************************************************** changed: [managedhost1] PLAY [managedhost2] ******************************************************************************************************************************* TASK [Gathering Facts] **************************************************************************************************************************** ok: [managedhost2] PLAY RECAP **************************************************************************************************************************************** managedhost1 : ok=2 changed=1 unreachable=0 failed=0 managedhost2 : ok=1 changed=0 unreachable=0 failed=0 |
And second task:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[miro@controlnode playbooks]$ ansible-playbook playbook7.yml --tags dbdeploy PLAY [managedhost1] ******************************************************************************************************************************* TASK [Gathering Facts] **************************************************************************************************************************** ok: [managedhost1] PLAY [managedhost2] ******************************************************************************************************************************* TASK [Gathering Facts] **************************************************************************************************************************** ok: [managedhost2] TASK [deploy db script] *************************************************************************************************************************** changed: [managedhost2] PLAY RECAP **************************************************************************************************************************************** managedhost1 : ok=1 changed=0 unreachable=0 failed=0 managedhost2 : ok=2 changed=1 unreachable=0 failed=0 |
You can even use --skip-tags
parameter to only run plays and tasks whose tags do not match these values.
Example 2.
Create the prepare_db.yml
task file and define a task that installs the required services for the database. Tag the task as dev, and have it notify the start_db handler. Define a second task to retrieve the database configuration file if the dev tag is active in the execution environment, and that restarts that database service. The task will use a conditional to ensure the configuration file path is defined. Define a third task to retrieve a different database configuration file if the prod tag is active in the execution environment, and that restarts the database service. Like the previous task, this task will use a conditional to ensure that the configuration file path is defined. Define a task that sets the Message of The Day for the managed host. Tag the task with the dev task. Repeat the previous step but change both the tag as well as the command (dev to prod).
When completed, the task file should read as follows:
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 34 35 36 37 38 39 40 41 42 43 44 |
--- - name: Install database packages yum: name: "{{ item }}" state: latest with_items: "{{ db_packages }}" tags: - dev notify: - start_db - name: Get small config file get_url: url: "{{ db_config_src_path_small }}" dest: "{{ db_config_path }}" when: db_config_src_path_small is defined notify: - restart_db tags: - dev - name: Get large config file get_url: url: "{{ db_config_src_path_large }}" dest: "{{ db_config_path }}" when: db_config_src_path_large is defined notify: - restart_db tags: - prod - name: Update motd for development copy: content: "This is a development server" dest: /etc/motd tags: - dev - name: Update motd for production copy: content: "This is a production server" dest: /etc/motd tags: - prod |
In the project directory, create the main playbook, playbook.yml
for servers in the databases group. The playbook will define the variables required by the task file upon import. Define the first task that includes the task file; the task file will use a conditional to ensure the managed host is in the group databases. Define the two handlers the task file requires, start_db and restart_db.
When completed, the playbook should read as follows:
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 |
--- - hosts: all vars: db_packages: - mariadb-server - MySQL-python db_config_url: http://materials.example.com/task_control db_config_src_path_small: "{{ db_config_url }}/my.cnf.small" db_config_src_path_large: "{{ db_config_url }}/my.cnf.large" db_config_path: /etc/my.cnf db_service: mariadb tasks: - include: prepare_db.yml when: inventory_hostname in groups['databases'] handlers: - name: start_db service: name: "{{ db_service }}" state: started - name: restart_db service: name: "{{ db_service }}" state: restarted |
Run the playbook, applying the dev tag using the –tags option.
1 2 3 4 5 6 7 |
[student@workstation demo-tags]$ ansible-playbook playbook.yml --tags 'dev' ... Output omitted ... TASK [Get small config file] *************************************************** changed: [servera.lab.example.com] TASK [Update motd for development] ********************************************* changed: [servera.lab.example.com] ... Output omitted ... |
Notice the configuration file that has been retrieved (my.cnf.small
).
Run an ad hoc command to display the /etc/motd
file on servera.
1 2 3 |
[student@workstation dev-tags]$ ansible databases -a 'cat /etc/motd' servera.lab.example.com | SUCCESS | rc=0 >> This is a development server |
Run the playbook again, this time skipping the tasks tagged as dev:
1 2 3 4 5 6 7 |
[student@workstation demo-tags]$ ansible-playbook playbook.yml --skip-tags 'dev' ... Output omitted ... TASK [Get large config file] *************************************************** ok: [servera.lab.example.com] TASK [Update motd for production] ********************************************** changed: [servera.lab.example.com] ... Output omitted ... |
Run an ad hoc command to display the /etc/motd file on servera.
1 2 3 |
[student@workstation dev-tags]$ ansible databases -a 'cat /etc/motd' servera.lab.example.com | SUCCESS | rc=0 >> This is a production server |
Example 3.
In the project directory, create the configure_mail.yml
task file. The task file contains instructions to install the required packages for the mail server, as well as instructions to retrieve the configuration files for the mail server.
Create the first task that uses the yum module to install the postfix package. Notify the start_postfix handler, and tag the task as server using the tags keyword. Add a task that installs the dovecot package using the yum module. It should notify the start_dovecot handler. Tag the task as client. Define a task that uses the get_url module to retrieve the Postfix configuration file.
Notify the restart_postfix handler and tag the task as server.
When completed, the task file should read as follows:
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 |
--- - name: Install postfix yum: name: postfix state: latest tags: - server notify: - start_postfix - name: Install dovecot yum: name: dovecot state: latest tags: - client notify: - start_dovecot - name: Download main.cf configuration get_url: url: http://materials.example.com/task_control/main.cf dest: /etc/postfix/main.cf tags: - server notify: - restart_postfix |
Create a playbook file named playbook.yml
. Define the playbook for all hosts. Define the task that includes the task file configure_mail.yml
using the include module. Add a conditional to only run the task for the hosts in the mailservers group. Define the three handlers the task file requires: start_postfix, start_dovecot, and restart_postfix. Define start_postfix handler to start the mail server. Define the start_dovecot handler to start the mail client. Define the restart_postfix handler that restarts the mail server.
When completed, the playbook should read as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
--- - hosts: all tasks: - name: Include configure_mail.yml include: configure_mail.yml when: inventory_hostname in groups['mailservers'] handlers: - name: start_postfix service: name: postfix state: started - name: start_dovecot service: name: dovecot state: started - name: restart_postfix service: name: postfix state: restarted |
Run the playbook, only applying the server tagged tasks using the –tags option. Notice how only the start_postfix handler gets triggered.
1 2 3 4 5 |
[student@workstation dev-tags]$ ansible-playbook playbook.yml --tags 'server' ... Output omitted ... RUNNING HANDLER [start_postfix] ************************************************ changed: [servera.lab.example.com] ... Output omitted ... |
Run an ad hoc command to ensure the postfix package has been successfully installed.
1 2 3 4 5 |
[student@workstation dev-tags]$ ansible mailservers -a 'yum list installed postfix' servera.lab.example.com | SUCCESS | rc=0 >> Loaded plugins: langpacks, search-disabled-repos Installed Packages postfix.x86_64 2:2.10.1-6.el7 @rhel_dvd |
Run the playbook again, but this time skip the tasks tagged with the server tag. The play will install the dovecot package, because the task is tagged with the client tag, and it will trigger the start_dovecot handler.
1 2 3 4 5 6 7 |
[student@workstation dev-tags]$ ansible-playbook playbook.yml --skip-tags 'server' ... Output omitted ... TASK [Install dovecot] ********************************************************* changed: [servera.lab.example.com] RUNNING HANDLER [start_dovecot] ************************************************ changed: [servera.lab.example.com] ... Output omitted ... |
Run an ad hoc command to ensure the dovecot package has been successfully installed.
1 2 3 4 5 |
[student@workstation dev-tags]$ ansible mailservers -a 'yum list installed dovecot' servera.lab.example.com | SUCCESS | rc=0 >> Loaded plugins: langpacks, search-disabled-repos Installed Packages dovecot.x86_64 1:2.2.10-5.el7 @rhel_dvd |