Sometimes you have playbooks or roles which you only want to execute parts of it and not all. This is where you can use ansible tags.
With Ansible Tags you can execute or skip selected tasks. I will try to summarize and not repeat the official documentation, cause it’s quite well explained and documented there.
How does it work There are two things you have to do
Add tags to your tasks, either individually or to a block, play, role, or import (which will inherited) Select or skip tags when you run your playbook. As mentioned in Ansible Tags uses the tag
-element to each task , like this
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
If you don’t want to add tags for each task you can take advantage of inheritance . This means tags which are defined on a level of block , playbook , roles or imports , are then applied to all lower level elements. In the example below, we define tag ntp
in the block
element. All tasks belonging to that block
implicitly also are tagged with ntp
:
tasks :
- block :
tags : ntp
- 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 : yes
- name : Install NFS utils
ansible.builtin.yum :
name :
- nfs-utils
- nfs-util-lib
state : present
tags : filesharing
Also worth to mention are the special, reserved tags always
, never
and debug
. Tasks or play tagged with always
will always run except you skip it with --skip-tags always
. The opposite is with the never
tag - tasks and plays will only run if you explicitly allow with --tag never
.s At last, the debug
tag is also a tag which will only run with --tag debug
.
The usage of tags is quite easy. 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
. Use the following command line arguments:
--tags all
- run all tasks, ignore tags (default behavior)--tags [tag1, tag2]
- run only tasks with the tags tag1
and tag2
--skip-tags [tag3, tag4
- run all tasks except those with the tags tag3
and tag4
--tags tagged
- run only tasks with at least one tag--tags untagged
- run only tasks with no tags--list-tags
- generate a list of available tags--list-tasks
- when used with --tags tagname
or --skip-tags tagname
, generate a preview of tagged tasksMy use-case - an example I had some playbooks which could perform the initial setup (bootstrapping) of my Hetzner Cloud environment. However, I have chosen to move the generic functionality into a “helper”-role which is structured as follows:
hcloud
├── defaults
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
└── tasks
├── hcloud_firewall.yml
├── hcloud_networks.yml
├── hcloud_project.yml
├── hcloud_server.yml
└── main.yml
This role Setup and configure Hetzner Cloud
add ssh keys add firewalls add networks create a server and assign it the respective firewalls and networks It requires a variable hcloud_env
- a dictionary which contains a list of named “environments” as follows:
hcloud_env :
development :
api_token : XXXXXX
firewalls :
firewall-ssh-only :
- direction : "in"
port : "22"
protocol : "tcp"
source_ips :
- 0.0.0.0 /0
- ::/0
firewall-no-access :
networks :
network-private-dev :
ip_range : "10.10.0.0/16"
subnets :
- ip_range : "10.10.0.0/24"
network_zone : "eu-central"
type : "cloud"
production :
api_token : XXXXXX
firewalls :
firewall-ssh-only :
- direction : "in"
port : "22"
protocol : "tcp"
source_ips :
- 0.0.0.0 /0
- ::/0
firewall-no-access :
networks :
network-private-prd :
ip_range : "10.10.0.0/16"
subnets :
- ip_range : "10.10.0.0/24"
network_zone : "eu-central"
type : "cloud"
The first part of main.yml
iterates over the projects and does creates the related elements:
- name : Setup Hetzner Cloud project
include_tasks : hcloud_project.yml
loop : "{{ hcloud_env|dict2items }}"
loop_control :
loop_var : hcloud_project
tags :
- hcloud
- network
- hcloud-bootstrap
- name : Create a server instance in the Hetzner cloud
include_tasks : hcloud_server.yml
tags :
- hcloud
- hcloud-server
So as you can see I have tagged the two steps with different tags. This allows me to achieve the following
do the initial setup of the cloud (which has to be done once and not every time I add a new server). So the include_tasks: hcloud_project.yml
will run when I use the tag hcloud-bootstrap
create servers - the include_tasks: hcloud_server.yml
will run when I use the tag hcloud-server
Ok, I believe this is a bit an abnormal case, but still it’s very useful for me and shows you what you can achieve with tags.
Initial setup using tag hcloud-bootstrap
So this is my playbook hcloud_bootstrap.yml
which uses the role hcloud
mentioned above:
- hosts : localhost
become : no
gather_facts : false
roles :
- role : hcloud
I run this play with --tags hcloud-bootstrap
:
ansible-playbook bootstrap-hcloud.yml --tags hcloud-bootstrap
This will apply role hcloud
but skip everything which is not tagged with hcloud-bootstrap
:
PLAY [ localhost] ****************************************************************************************************************************************************************************************************
TASK [ hcloud : Setup Hetzner Cloud projects] ************************************************************************************************************************************************************************
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_project.yml for localhost = > ( item={ 'key' : 'development' , 'value' : { 'api_token' : 'xxx' , 'firewalls' : { 'firewall-ssh-only' : [{ 'direction' : 'in' , 'port' : '22' , 'protocol' : 'tcp' , 'source_ips' : [ '0.0.0.0/0' , '::/0' ]}] , 'firewall-no-access' : None} , 'networks' : { 'network-private-dev' : { 'ip_range' : '10.10.0.0/16' , 'subnets' : [{ 'ip_range' : '10.10.0.0/24' , 'network_zone' : 'eu-central' , 'type' : 'cloud' }]}}}})
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_project.yml for localhost = > ( item={ 'key' : 'production' , 'value' : { 'api_token' : 'xxx' , 'firewalls' : { 'firewall-ssh-only' : [{ 'direction' : 'in' , 'port' : '22' , 'protocol' : 'tcp' , 'source_ips' : [ '0.0.0.0/0' , '::/0' ]}] , 'firewall-no-access' : None} , 'networks' : { 'network-private-prd' : { 'ip_range' : '10.10.0.0/16' , 'subnets' : [{ 'ip_range' : '10.10.0.0/24' , 'network_zone' : 'eu-central' , 'type' : 'cloud' }]}}}})
TASK [ hcloud : development: Set api token] **************************************************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : development: Add ssh-keys] ***************************************************************************************************************************************************************************
ok: [ localhost] = > ( item={ 'key' : 'ssh-key1' , 'value' : 'ssh-rsa xxx' })
ok: [ localhost] = > ( item={ 'key' : 'ssh-key2' , 'value' : 'ssh-rsa xxx' })
ok: [ localhost] = > ( item={ 'key' : 'ssh-key3' , 'value' : 'ssh-rsa xxx' })
TASK [ hcloud : development: Create firewalls] ***********************************************************************************************************************************************************************
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_firewall.yml for localhost = > ( item={ 'key' : 'firewall-ssh-only' , 'value' : [{ 'direction' : 'in' , 'port' : '22' , 'protocol' : 'tcp' , 'source_ips' : [ '0.0.0.0/0' , '::/0' ]}]})
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_firewall.yml for localhost = > ( item={ 'key' : 'firewall-no-access' , 'value' : None})
TASK [ hcloud : development: Firewall firewall-ssh-only] *************************************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : development: Firewall 'firewall-ssh-only' with no rules] *********************************************************************************************************************************************
skipping: [ localhost]
TASK [ hcloud : development: Firewall firewall-no-access] ************************************************************************************************************************************************************
skipping: [ localhost]
TASK [ hcloud : development: Firewall 'firewall-no-access' with no rules] ********************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : development: Create networks and corresponding subnets] **********************************************************************************************************************************************
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_networks.yml for localhost = > ( item={ 'key' : 'network-private-dev' , 'value' : { 'ip_range' : '10.10.0.0/16' , 'subnets' : [{ 'ip_range' : '10.10.0.0/24' , 'network_zone' : 'eu-central' , 'type' : 'cloud' }]}})
TASK [ hcloud : development: Create a network 'network-private-dev' ] *************************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : {{ hcloud_project.key }} : Create a subnetwork '{{ subnet.ip_range }}' for '{{ network.key }}' ] *******************************************************************************************************
ok: [ localhost] = > ( item={ 'ip_range' : '10.10.0.0/24' , 'network_zone' : 'eu-central' , 'type' : 'cloud' })
TASK [ hcloud : production: Set api token] ***************************************************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : production: Add ssh-keys] ****************************************************************************************************************************************************************************
ok: [ localhost] = > ( item={ 'key' : 'ssh-key1' , 'value' : 'ssh-rsa xxx' })
ok: [ localhost] = > ( item={ 'key' : 'ssh-key2' , 'value' : 'ssh-rsa xxx' })
ok: [ localhost] = > ( item={ 'key' : 'ssh-key3' , 'value' : 'ssh-rsa xxx' })
TASK [ hcloud : production: Create firewalls] ************************************************************************************************************************************************************************
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_firewall.yml for localhost = > ( item={ 'key' : 'firewall-ssh-only' , 'value' : [{ 'direction' : 'in' , 'port' : '22' , 'protocol' : 'tcp' , 'source_ips' : [ '0.0.0.0/0' , '::/0' ]}]})
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_firewall.yml for localhost = > ( item={ 'key' : 'firewall-no-access' , 'value' : None})
TASK [ hcloud : production: Firewall firewall-ssh-only] **************************************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : production: Firewall 'firewall-ssh-only' with no rules] **********************************************************************************************************************************************
skipping: [ localhost]
TASK [ hcloud : production: Firewall firewall-no-access] *************************************************************************************************************************************************************
skipping: [ localhost]
TASK [ hcloud : production: Firewall 'firewall-no-access' with no rules] *********************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : production: Create networks and corresponding subnets] ***********************************************************************************************************************************************
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_networks.yml for localhost = > ( item={ 'key' : 'network-private-prd' , 'value' : { 'ip_range' : '10.10.0.0/16' , 'subnets' : [{ 'ip_range' : '10.10.0.0/24' , 'network_zone' : 'eu-central' , 'type' : 'cloud' }]}})
TASK [ hcloud : production: Create a network 'network-private-prd' ] **************************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : {{ hcloud_project.key }} : Create a subnetwork '{{ subnet.ip_range }}' for '{{ network.key }}' ] *******************************************************************************************************
ok: [ localhost] = > ( item={ 'ip_range' : '10.10.0.0/24' , 'network_zone' : 'eu-central' , 'type' : 'cloud' })
PLAY RECAP **********************************************************************************************************************************************************************************************************
localhost : ok= 20 changed= 0 unreachable= 0 failed= 0 skipped= 4 rescued= 0 ignored= 0
Create servers using tag hcloud-server
I use the same role hcloud
in a different playbook hcloud_bootstrap_server.yml
# This playbook shall only run against new servers thus you have to specify a coma separated list of server names {{ server_names }}
- hosts : localhost
become : no
gather_facts : false
vars :
hcloud_server_project : "development"
hcloud_server_image : "debian-10"
hcloud_server_type : "cx21"
hcloud_server_network : "network-private-dev"
hcloud_firewalls :
- "firewall-ssh-only"
- "firewall-no-access"
hcloud_server_sshkeys :
- ssh-key1
- ssh-key2
hcloud_server_labels :
dev : ""
roles :
- role : hcloud
# before connecting ensure to wait the hosts are ready
- hosts : "{{ server_names }}"
gather_facts : false
vars :
ansible_host : "{{ server_ipv4 }}"
tags :
- hcloud-server
pre_tasks :
- name : Waits until server is ready
wait_for :
host : "{{ server_ipv4 }}"
port : 22
delay : 2
delegate_to : localhost
tags :
- hcloud-server
- hosts : "{{ server_names }}"
vars :
ansible_host : "{{ server_ipv4 }}"
ansible_ssh_user : "{{ bootstrap_remote_user }}"
tags :
- hcloud-server
roles :
- role : base-system
vars :
ansible_host : "{{ server_ipv4 }}"
tags :
- hcloud-server
- role : papanito.cloudflared
tags :
- hcloud-server
Which I run with --tags hcloud-server
plus -e server_names=x1,x2
, which are the server names for the servers to be created:
ANSIBLE_HOST_KEY_CHECKING= False ansible-playbook bootstrap-hcloud_servers.yml -e server_names= "dev0001" --tags hcloud-server -e 'hcloud_server_labels={"dev":"","k8s":"master"}'
Which will apply role hcloud
but skip everything which is not tagged with hcloud-server
:
PLAY [ localhost] ****************************************************************************************************************************************************************************************************
TASK [ hcloud : Create a server instance in the Hetzner cloud] *******************************************************************************************************************************************************
included: /home/aedu/Workspaces/example.com/infrastructure/roles/hcloud/tasks/hcloud_server.yml for localhost
TASK [ hcloud : Set api token for server creation] *******************************************************************************************************************************************************************
ok: [ localhost]
TASK [ hcloud : {{ hcloud_server_project }} : Create a server '{{ server_name }}' ] ************************************************************************************************************************************
changed: [ localhost] = > ( item= dev0001)
TASK [ hcloud : {{ hcloud_server_project }} : Create a server network for '{{ server_name }}' ] ************************************************************************************************************************
changed: [ localhost] = > ( item= dev0001)
PLAY [ dev0001] ******************************************************************************************************************************************************************************************************
TASK [ Waits until server is ready] **********************************************************************************************************************************************************************************
ok: [ dev0001]
PLAY [ dev0001] ******************************************************************************************************************************************************************************************************
TASK [ Gathering Facts] **********************************************************************************************************************************************************************************************
ok: [ dev0001]
TASK [ base-system : base-system | users | Ensure group "ansible" exists] ********************************************************************************************************************************************
changed: [ dev0001]
...
PLAY RECAP **********************************************************************************************************************************************************************************************************
dev0001 : ok= 29 changed= 14 unreachable= 0 failed= 0 skipped= 16 rescued= 0 ignored= 0
localhost : ok= 5 changed= 2 unreachable= 0 failed= 0 skipped= 0 rescued= 0 ignored= 0
I hope this makes sense to you and may be helpful to understand tags a bit better.