Ansible Part 5 - Loops, Handlers and Tags

As we move ahead, playbooks start getting long. You're copying the same task block with different values. You're restarting services manually after config changes. You're running the entire playbook just to test one small task you changed. This part fixes it all.
Loops eliminate repetition. Handlers make service restarts automatic and intelligent. Tags let you run just the specific parts of a playbook .
Loops
Imagine you need to create three users on a managed host. Without loops, you'd write this:
tasks:
- name: adding user ankit
user:
name: dummy1
state: present
- name: adding user sandeep
user:
name: dummy2
state: present
- name: adding user manish
user:
name: dummy3
state: present
Three tasks, same module, same arguments, only the name changes. Now imagine doing this for 20 users.
The loop keyword solves this. You write the task once and give it a list to iterate over:
tasks:
- name: adding multiple users
user:
name: "{{ item }}"
state: present
loop:
- dummy1
- dummy2
- dummy3
item is a special keyword - on each iteration, Ansible replaces it with the current value from the list.
Loops with Multiple Value per item
The simple list works when you only need one value per iteration. But what if each user needs a name, a comment, and a UID? You use a list of dictionaries and access the fields with item.field_name:
---
- name: Using loops in playbook
hosts: redhat
tasks:
- name: creating multiple users
user:
name: "{{ item.user_name }}"
comment: "{{ item.comment }}"
uid: "{{ item.uid }}"
state: present
loop:
- user_name: dummy1
comment: USER1
uid: 10000
- user_name: dummy2
comment: USER2
uid: 20000
- user_name: dummy3
comment: USER3
uid: 30000
Each item in the loop is now a dictionary. item.user_name, item.comment, item.uid pull the specific fields out for each iteration.
You can also reference loops with an external variable file
Handlers
Imagine you've 50 web-servers, you deploy a new httpd.conf with some change, for that to take effect, you need to restart the service.
But there is a catch here, if you add restart.httpd at the end of your playbook, it runs every single time, even if nothing has changed. Unnecessary restarts cause unnecessary downtime.
Actual requirement -> Restart only if conf file changed.
A handler is a task that only runs when it's explicitly triggered by another task - and only if that task reported changed. If the triggering task reports ok (nothing changed), the handler never fires.
---
- name: installing httpd package and creating index file
hosts: redhat
tasks:
- name: installing httpd package
yum:
name: httpd
state: present
notify: restarting httpd
- name: starting http service
service:
name: httpd
state: started
enabled: true
- name: creating index.html file
lineinfile:
path: /var/www/html/index.html
line: "This is testing webserver"
create: yes
handlers:
- name: restarting httpd
service:
name: httpd
state: restarted
Things to notice:
notify: restarting httpd- this is the trigger. It points to a handler by name. When this task reportschanged, it queues that handler to run.handlers:- defined at the same level astasks:, not inside it. The handler itself is just a regular task - a module with arguments.
That last point matters. Say you have five tasks, the third one triggers a handler, and the fifth one fails. The handler won't run. Your service won't restart. This is intentional - Ansible assumes a half-broken state isn't worth triggering restarts over.
Tags - Run Only What You Need
As playbooks grow, they've a lot of tasks. Tags let you label tasks and then run only the labeled ones.
Adding tags
---
- name: using tags in my playbook
hosts: redhat
tasks:
- name: creating group
group:
name: developer
state: present
tags: group
- name: creating user
user:
name: taguser0
state: present
tags: user
- name: installing httpd package
yum:
name: httpd
state: present
tags: yum
- name: creating one empty file
file:
path: /tmp/file.txt
state: touch
owner: taguser0
group: root
mode: "666"
tags: file
Running tagged tasks
# Run only the yum task
ansible-playbook tags.yml --tags yum
# Run both the yum and file tasks
ansible-playbook tags.yml --tags yum,file
# Run everything EXCEPT the yum task
ansible-playbook tags.yml --skip-tags yum
Here, adding yum tag skips the rest of the tasks.
One task can have multiple tags
- name: installing httpd package
yum:
name: httpd
state: present
tags:
- yum
- webserver
- packages
Tags at play level
You can tag an entire play instead of individual tasks:
- name: webserver setup
hosts: webservers
tags: web
tasks:
- name: install httpd
yum:
name: httpd
state: present
- name: start httpd
service:
name: httpd
state: started
--tags web now runs the entire play. Useful when your playbook has multiple plays.
Next we'll dive into Jinja2 Templates, something beyond static files and roles, which will tell you how to structure your Ansible project. Till then stay tuned, Keep Balling!





