Search

Top 60 Oracle Blogs

Recent comments

Ansible tips’n’tricks: run select parts of a playbook using tags

I have recently re-discovered an Ansible feature I haven’t used in a while: tagging. Ansible allows you to define tags at various places of your playbook. On its own that wouldn’t be terribly useful, except that you can pass tags to ansible-playbook causing the interpreter to selectively run tasks tagged appropriately.

My example uses Ansible 2.9.6+dfsg-1 as it was provided by Ubuntu 20.04 LTS.

Tagging Ansible tasks

Here is the code of my somewhat over-simplified playbook for this blog post:

 - hosts: testvm
   tasks:
  - block:
    - name: tag1 step 1
      debug:
        msg: I am part of tag1
    - name: tag1 step 2
      debug:
        msg: And so am I
    tags: tag1

  - block:
    - name: tag2 complete
      debug:
        msg: I am tag 2
    tags: tag2

  - name: tag3 step 1
    debug:
      msg: this is tag3 step 1
    tags: tag3

  - name: tag3 step 2 
    debug:
      msg: this is tag3 step 2
    tags: tag3

  - name: common task for tag2 and tag3
    debug:
      msg: I am common to tag2 and tag3
    tags:
    - tag2
    - tag3

  - name: this needs to be executed regardless
    debug:
      msg: I am always run, irrespective of the tag
    tags: always

  - name: untagged task
    debug:
      msg: I am an untagged task  

To keep the post readable I’ll focus on tagging tasks. Another (future) post will discuss the use of tags for includes and other Ansible entities.

Looking at the Ansible playbook you can see all but 1 tasks are tagged. Tags 1 and 2 apply to blocks, whereas tag 3 is applied to multiple tasks. The special always tag has been applied to the penultimate task. Finally, you can see a task with 2 tags as well.

Execute the play without specifying tags

If I run this playbook the usual way, it will execute all steps, as shown in the output:

$ ansible-playbook -vi inventory.ini cond_exec.yml
Using /etc/ansible/ansible.cfg as config file

PLAY [testvm] ******************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************
ok: [localhost]

TASK [tag1 step 1] *************************************************************************************************
ok: [localhost] => {
    "msg": "I am part of tag1"
}

TASK [tag1 step 2] *************************************************************************************************
ok: [localhost] => {
    "msg": "And so am I"
}

TASK [tag2 complete] ***********************************************************************************************
ok: [localhost] => {
    "msg": "I am tag 2"
}

TASK [tag3 step 1] *************************************************************************************************
ok: [localhost] => {
    "msg": "this is tag3 step 1"
}

TASK [tag3 step 2] *************************************************************************************************
ok: [localhost] => {
    "msg": "this is tag3 step 2"
}

TASK [common task for tag2 and tag3] *******************************************************************************
ok: [localhost] => {
    "msg": "I am common to tag2 and tag3"
}

TASK [this needs to be executed regardless] ************************************************************************
ok: [localhost] => {
    "msg": "I am always run, irrespective of the tag"
}

TASK [untagged task] ***********************************************************************************************
ok: [localhost] => {
    "msg": "I am an untagged task"
}

PLAY RECAP *********************************************************************************************************
localhost                  : ok=9    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

If you have executed playbooks before, this should look familiar.

Passing a tag to ansible-playbook

Let’s assume I want to re-run the playbook, limited to those tasks tagged tag2. To do so, I have to provide the tag to ansible-playbook, like so:

$ ansible-playbook -vi inventory.ini cond_exec.yml --tags=tag2
Using /etc/ansible/ansible.cfg as config file

PLAY [testvm] ******************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************
ok: [localhost]

TASK [tag2 complete] ***********************************************************************************************
ok: [localhost] => {
    "msg": "I am tag 2"
}

TASK [common task for tag2 and tag3] *******************************************************************************
ok: [localhost] => {
    "msg": "I am common to tag2 and tag3"
}

TASK [this needs to be executed regardless] ************************************************************************
ok: [localhost] => {
    "msg": "I am always run, irrespective of the tag"
}

PLAY RECAP *********************************************************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

Looking at the execution you can see any task tagged tag2 is executed, including the common task for tag2 and tag3. But there is another task’s output: an additional task is executed thanks to the special tag: always. It has an equivalent, never, although I’m still trying to find a use case for it.

Notice how the un-tagged task isn’t executed.

Providing more than 1 tag to ansible-playbook

I’m not limited to running a single tag, I can do the same for multiple tags. Let’s run the play for tags tag2 and tag3:

$ ansible-playbook -vi inventory.ini cond_exec.yml --tags=tag2,tag3
Using /etc/ansible/ansible.cfg as config file

PLAY [testvm] ******************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************
ok: [localhost]

TASK [tag2 complete] ***********************************************************************************************
ok: [localhost] => {
    "msg": "I am tag 2"
}

TASK [tag3 step 1] *************************************************************************************************
ok: [localhost] => {
    "msg": "this is tag3 step 1"
}

TASK [tag3 step 2] *************************************************************************************************
ok: [localhost] => {
    "msg": "this is tag3 step 2"
}

TASK [common task for tag2 and tag3] *******************************************************************************
ok: [localhost] => {
    "msg": "I am common to tag2 and tag3"
}

TASK [this needs to be executed regardless] ************************************************************************
ok: [localhost] => {
    "msg": "I am always run, irrespective of the tag"
}

PLAY RECAP *********************************************************************************************************
localhost                  : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

Tasks tagged tag2 and tag3 are run, as is the task flagged to always run. This wasn’t particularly surprising considering the output of the previous execution.

Skipping a tag

Ansible offers the option to skip one or more tags. It’s not the inverse operation to specifying which tags to run. Here is proof: I’m asking Ansible to skip tag 1, so it should produce the same output as before. Well, it doesn’t quite manage to do so:

$ ansible-playbook -vi inventory.ini cond_exec.yml --skip-tags=tag1
Using /etc/ansible/ansible.cfg as config file

PLAY [testvm] ******************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************
ok: [localhost]

TASK [tag2 complete] ***********************************************************************************************
ok: [localhost] => {
    "msg": "I am tag 2"
}

TASK [tag3 step 1] *************************************************************************************************
ok: [localhost] => {
    "msg": "this is tag3 step 1"
}

TASK [tag3 step 2] *************************************************************************************************
ok: [localhost] => {
    "msg": "this is tag3 step 2"
}

TASK [common task for tag2 and tag3] *******************************************************************************
ok: [localhost] => {
    "msg": "I am common to tag2 and tag3"
}

TASK [this needs to be executed regardless] ************************************************************************
ok: [localhost] => {
    "msg": "I am always run, irrespective of the tag"
}

TASK [untagged task] ***********************************************************************************************
ok: [localhost] => {
    "msg": "I am an untagged task"
}

PLAY RECAP *********************************************************************************************************
localhost                  : ok=7    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

Although the block tagged tag1 didn’t run, the un-tagged task did! So if you want absolute control, you should not use the --skip-tags option as it might produce undesired results with mixed (tagged and un-tagged) playbooks. Unless of course you apply tags to all the elements in your playbook. Which requires more discipline than I can muster.

Summary

Tags are an interesting, optional feature of the language specification allowing you to run only select parts of playbooks. I’ll try to write another post detailing the use of tags with dynamic includes.

Happy automating!