Posted on April 14, 2021 by Adrian Wyssmann ‐ 8 min read
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.
There are two things you have to do
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:
- configurationIf 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: filesharingAlso 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 tasksI 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.ymlThis role Setup and configure Hetzner Cloud
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-serverSo as you can see I have tagged the two steps with different tags. This allows me to achieve the following
include_tasks: hcloud_project.yml will run when I use the tag hcloud-bootstrapinclude_tasks: hcloud_server.yml will run when I use the tag hcloud-serverOk, 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.
hcloud-bootstrapSo this is my playbook hcloud_bootstrap.yml which uses the role hcloud mentioned above:
- hosts: localhost
become: no
gather_facts: false
roles:
- role: hcloudI run this play with --tags hcloud-bootstrap:
ansible-playbook bootstrap-hcloud.yml --tags hcloud-bootstrapThis 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 hcloud-serverI 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-serverWhich 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.