Warning: This article related to ansible 2.5.0. The behaviour of tags in ansible has changed over the last few releases. See part-1 for the previous version relating to ansible 2.4.3.

The code for this article is available on github.

The main motivation of learning about tags and dynamic includes, is that during debugging of a complex configuration, where you have a nested structure of roles and tasks, it’s useful to be able to run one or more tasks in isolation, without having to make lots of changes to the code to achieve that.

The code for this example makes use of a playbook, which includes a role (using include_role, which itself includes some tasks (using include_tasks)., The aim of this endeveaour is to be able to run only tagged tasks in nested task_files. You can checkout the code and follow along. The playbook writes out some files out in your /tmp directory.

The ansible version I am using is as follows;

$ ansible --version
ansible 2.5.0 (detached HEAD 2c2dd1a1b3) last updated 2018/03/27 19:45:15 (GMT +100)
  config file = /home/demouser/.ansible.cfg
  configured module search path = [u'/home/demouser/.local/lib/python2.7/site-packages/ara/plugins/modules']
  ansible python module location = /home/demouser/git/ansible-other/lib/ansible
  executable location = /home/demouser/git/ansible-other/bin/ansible
  python version = 2.7.14 (default, Mar 14 2018, 13:36:31) [GCC 7.3.1 20180303 (Red Hat 7.3.1-5)]

If you checkout the example repo, the code looks like this;

cd ~/git
git clone https://github.com/limepepper/ansible-tags-article-code-2
cd ansible-tags-article-code-2
$ tree -I .git --prune
.
├── meta
│   └── main.yml
├── molecule
│   └── default
│       ├── create.yml
│       ├── destroy.yml
│       ├── Dockerfile.j2
│       ├── INSTALL.rst
│       ├── molecule.yml
│       ├── playbook-1.yml
│       ├── playbook-2.yml
│       ├── playbook-3.yml
│       ├── playbook-4.yml
│       ├── playbook-5.yml
│       ├── playbook-6.yml
│       ├── playbook.yml
│       ├── prepare.yml
│       ├── pytestdebug.log
│       └── tests
│           ├── __pycache__
│           │   └── test_default.cpython-27-PYTEST.pyc
│           ├── test_default.py
│           └── test_default.pyc
├── Rakefile
├── README.md
├── tasks
│   ├── playbook-1-tasks-1.yml
│   ├── playbook-1-tasks-2.yml
│   ├── playbook-2-tasks-1.yml
│   ├── playbook-3-tasks-1.yml
│   ├── playbook-4-tasks-1.yml
│   ├── playbook-5-tasks-1.yml
│   ├── playbook-6-tasks-1.yml
│   └── playbook-6-tasks-2.yml
└── templates
    └── template-in.j2

7 directories, 29 files

So the first thing to do is to run the first playbook with no tags specified and see what the tasks look like;

$ ansible-playbook -v  ./molecule/default/playbook-1.yml -l localhost
Using /home/demouser/.ansible.cfg as config file

PLAY [Converge] **************************************************************

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

TASK [print out the information about this playbook] *************************
ok: [localhost] =>
  msg: |-
    This playbook uses include_role to dynamically include a role
    it also uses tasks_from to pick a specific tasks file.
    The tasks in the tasks file are a mix of tagged, untagged

TASK [include_role] **********************************************************

TASK [ansible-tags-article-code-2 : debug] ***********************************
ok: [localhost] =>
  msg: debug in playbook-1-tasks-1

TASK [ansible-tags-article-code-2 : include_tasks] ***************************
included: ./ansible-tags-article-code-2/tasks/playbook-1-tasks-2.yml for localhost

TASK [ansible-tags-article-code-2 : debug] ***********************************
ok: [localhost] =>
  msg: debug in playbook-1-tasks-2

TASK [ansible-tags-article-code-2 : this task is tagged as 'run-me'] *********
changed: [localhost] => changed=true

TASK [ansible-tags-article-code-2 : this tasks is tagged as 'dont-run-me'] ***
changed: [localhost] => changed=true

TASK [ansible-tags-article-code-2 : this tasks is not tagged] ****************
changed: [localhost] => changed=true

PLAY RECAP *******************************************************************
localhost                  : ok=8    changed=3    unreachable=0    failed=0

And from that we can see that all the tasks are run. Whether they are tagged or not.

The next thing to try, is to pass the tag run-me, and see what happens. Obviously we hope that only the single task tagged run-me will run. But lets not get our hopes up;

$ ansible-playbook -v  ./molecule/default/playbook-1.yml -l localhost --tags run-me
Using /home/demouser/.ansible.cfg as config file

PLAY [Converge] ***********************************************************
TASK [Gathering Facts] ****************************************************
ok: [localhost]
PLAY RECAP ****************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0

So nothing ran.. hardly surprising given the ansible docs say this with reference to tags;

The above information does not apply to include_tasks or other dynamic includes, as the attributes applied to an include, only affect the include itself.

So how about tagging the include_role and include_tasks like so;

    - name: "include_role: ansible-tags-article-code-2"
      include_role:
        name: ansible-tags-article-code-2
        tasks_from: playbook-2-tasks-1.yml
      tags: run-me
$ ansible-playbook -v  ./molecule/default/playbook-2.yml -l localhost --tags run-me
Using /home/demouser/.ansible.cfg as config file

PLAY [Converge] **********************************************************

TASK [Gathering Facts] ***************************************************
ok: [localhost]
TASK [include_role: ansible-tags-article-code-2] *************************
TASK [ansible-tags-article-code-2 : include_tasks] ***********************
included: /home/demouser/Dropbox/bin/ansible/roles/ansible-tags-article-code-2/tasks/playbook-2-tasks-2.yml for localhost
TASK [ansible-tags-article-code-2 : this task is tagged as 'run-me'] *****
changed: [localhost] => changed=true
PLAY RECAP ***************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=0

Great! our tagged task, ran as expected.

In playbook-3 I switched to tags: always so you don’t have to duplicate your tags up to all the include_role and include_tasks modules.

  # file: playbook-3.yml
  - name: "include_role: ansible-tags-article-code-2"
    include_role:
      name: ansible-tags-article-code-2
      tasks_from: playbook-3-tasks-1.yml
    tags: always

  # file: playbook-3-tasks-1.yml
- include_tasks: "playbook-3-tasks-2.yml"
  tags: always

For the next trick, we are going to test that we can run a tagged tasks in a tasks file that was included in a loop like so;

  # file: playbook-4-tasks-1.yml
- include_tasks: "playbook-4-tasks-2.yml"
  vars:
    site_to_process: ""
  with_items:
    - site: www.mywordpresssite-01.com
    - site: www.mywordpresssite-02.com
    - site: www.mywordpresssite-03.com
  tags: always

and great! that seems to work as well;

$ ansible-playbook -v  ./molecule/default/playbook-4.yml -l localhost --tags run-me
Using /home/demouser/.ansible.cfg as config file

PLAY [Converge] ************************************************************

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

TASK [include_role: ansible-tags-article-code-2] ***************************

TASK [ansible-tags-article-code-2 : include_tasks] *************************
included: ./ansible-tags-article-code-2/tasks/playbook-4-tasks-2.yml for localhost
included: ./ansible-tags-article-code-2/tasks/playbook-4-tasks-2.yml for localhost
included: ./ansible-tags-article-code-2/tasks/playbook-4-tasks-2.yml for localhost

TASK [ansible-tags-article-code-2 : this task is tagged as 'run-me' for {u'site': u'www.mywordpresssite-01.com'}] ***
ok: [localhost] => changed=false

TASK [ansible-tags-article-code-2 : this task is tagged as 'run-me' for {u'site': u'www.mywordpresssite-02.com'}] ***
ok: [localhost] => changed=false

TASK [ansible-tags-article-code-2 : this task is tagged as 'run-me' for {u'site': u'www.mywordpresssite-03.com'}] ***
ok: [localhost] => changed=false
PLAY RECAP *****************************************************************
localhost                  : ok=7    changed=0    unreachable=0    failed=0

Final Thing

So one other thing to note, is that you can also indent your tasks into a block like so, which allows marking a series of tasks with tags, without a lot of duplication.

- tags: run-me
  block:

  - name: "this task is tagged as 'run-me' for "
    template:
      src: template-in.j2
      dest: "/tmp/a_playbook-5-tasks-2-tagged-run-me.txt"

  - name: "this task is tagged as 'run-me' for "
    template:
      src: template-in.j2
      dest: "/tmp/a_playbook-5-tasks-2-2-tagged-run-me.txt"

So tags are pretty neat, if you update to the most recent version of ansible, and read the docs.