Ansible: the Power of Tags

Ansible: the Power of Tags

Let's talk about why you should use tags in your Ansible playbooks. For starters, I'll lecture you a bit about tags in the Ansible language and then show you how we use them in a real-life scenario.

Ansible is a great automation and configuration tool, built with simplicity and idempotency in mind. And like all in this world, it comes with a price tag: Ansible language is simple, not to say minimalistic in terms of the control structures. Of course, you immediately refute me and say that you can do all that in Ansible, and you would be correct. Ansible has its ways to compensate for missing constructs and develop some quite sophisticated scenarios.

Plus, Ansible gives you tags, another unique and quite powerful way to control the execution. You can put a label or labels on tasks, roles, and plays. The tag value could be any valid string. Just keep in mind there are few reserved words - always and never and I do not recommend use words tagged and untagged for tags as well. Let's use a small example to illustrate how it looks like:

- name: Start MySQL Server
  tags: 
   - database
   - start
  service:
   name: mysql
   state: started
   
- name: Start HTTPD Server
  tags:
   - http
   - start
  service: 
   name: httpd
   state: started
system-start.yml

Now, let's see how we tags alter the playbook outcome.


# Run all tasks and plays in the book
ansible-playbook system-start.yml

# Start all systems 
ansible-playbook system-start.yml --tags start

# Start HTTP Server only
ansible-playbook system-start.yml --tags http,start

# Same as previous 
ansible-playbook system-start.yml --skip-tags database

Special usage tags:

  • always - Ansible always executes the task, regardless of the specified tags in --tags and --skip-tags arguments.
  • never - Engine will skip the task, ignoring the --tags and --skip-tags arguments.
  • untagged - Not a tag, but with -- tags untagged, Ansible will execute all tasks with no tags.
  • tagged - Opposite to the previous one. With --tags tagged, the engine picks a task if it has any tag.

Special tags (especially never) are pretty handy when you have something that you execute only on a special occasion. For example, I have an Oracle WebLogic configuration play with the tasks like this:

---
- name: Drop RCU Domain 
  hosts: wls-domin-admin
  tags:
   - never
   - rcu-drop
  roles:
   - my_rcu_role
     vars:
       state: absent
       
- name: Create RCU Domain 
  hosts: wls-domin-admin
  tags:
   - rcu-create
  roles:
   - my_rcu_role
     vars:
       state: present
 # Anotehr plays are skipped.
 ...
wls-domain.yml

In the example above, play "Drop RCU Domain" will never be executed until I say so. It assures me that I wouldn't drop all my database artifacts by chance. Now, if I decide to re-create my WebLogic domain, I run this playbook with tags:

# Recreate Database objects
ansible-playbook wls-domain.yml --tags rcu-drop,rcu-create
Run play, marked as "never"

There are more fine details on using tags with roles or imported tasks, but you already have a general idea of how valuable tags for your projects. One more tag-related tip:

  • --list-tags - List all tags from the playbook. Quite useful to understand what you can specify for the play.
  • --list-tasks - the command itself shows you the list of tasks in the playbook, but in combination with --tags or --skip-tags, it gives you a heads up on what Ansible will execute from your playbook.

Control Playbook

One of the most common playbooks we run - is the control playbooks. They are designed to start/stop or restart a specific system, with all sequences and validations we need. To add some flexibility, I created separate plays to manage domain components individually. You don't need to know all the WebLogic domain details to understand this example, but the precedence of commands is important for the result, and you can't bring up the next component without the previous one.

Let me show the playbook structure, and then we discuss how to use it.

---
## WebLogic Domain Control Playbook
## Stop Section
- name: Stop Managed Servers
  hosts: wls_hosts
  tags:
   - stop
   - server-stop
  roles: 
    - role: domain-ctl
      vars:
        process: server
        state: stop

- name: Stop Admin Server
  hosts: wls_host_admin
  tags:
   - stop
   - admin-stop
  roles: 
    - role: domain-ctl
      vars:
        process: admin
        state: stop
        
- name: Stop NodeManager
  hosts: wls_hosts
  tags:
   - stop
   - nm-stop
  roles: 
    - role: domain-ctl
      vars:
        process: node
        state: stop

## Start Section        
- name: Start NodeManager
  hosts: wls_hosts
  tags:
   - start
   - nm-start
  roles: 
    - role: domain-ctl
      vars:
        process: node
        state: start

- name: Start Admin Server
  hosts: wls_host_admin
  tags:
   - start
   - admin-start
  roles: 
    - role: domain-ctl
      vars:
        process: admin
        state: start     

- name: Start Managed Servers
  hosts: wls_hosts
  tags:
   - start
   - server-start
  roles: 
    - role: domain-ctl
      vars:
        process: server
        state: start
...
wls-domain-ctl.yml

Now, let see how I use playbooks

# Restart Domain
ansible-playbook wls-domain-ctl.yml  
# Stop Domain
ansible-playbook wls-domain-ctl.yml --tags stop
# Start Domain.  
ansible-playbook wls-domain-ctl.yml --tags start 
# Start AdminServer only 
ansible-playbook wls-domain-ctl.yml --tags nm-start,admin-start,stop

Properly arranged plays give you the most common control activity for the information system - restart. As a bonus, if the system was down, it will be started if you run playbook as is.

Every play bears a start or stop tag, so if I want to bring down the domain for the maintenance, I use only one tag for it.

If I need to start only the Admin Server, I make sure that the domain went down and specify component-branded tags for the start sequence. This command also illustrates that I can list tags in any order; Ansible will select and execute tasks in the playbook precedence order.