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 ansible2.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.