Stage 4 · Provision
Playbooks & Variables
Loops & Conditionals
loop, when, block/rescue, and register for control flow.
The When Clause
The when clause controls whether a task runs. It evaluates a Jinja2 expression and executes the task only if the expression is true. It is Ansible's primary conditional mechanism.
- name: Install on Debian
apt:
name: nginx
state: present
when: ansible_os_family == "Debian"
- name: Install on RedHat
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
- name: Only on production
debug:
msg: "This is production"
when: environment == "production"when expressions are evaluated against facts and variables. Use == for equality, != for inequality, and Jinja2 operators for complex conditions.
Loop Basics
The loop keyword repeats a task for each item in a list. The current item is available as item in the task.
# Install multiple packages
- name: Install packages
package:
name: "{{ item }}"
state: present
loop:
- nginx
- python3
- git
# Create multiple users
- name: Create users
user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlieloop replaces the older with_items syntax. Both work but loop is the modern, preferred approach.
Complex Loops
# Loop over dictionaries
- name: Create users with groups
user:
name: "{{ item.name }}"
groups: "{{ item.group }}"
state: present
loop:
- { name: alice, group: admin }
- { name: bob, group: developer }
- { name: charlie, group: ops }
# Loop with nested lists
- name: Install packages
package:
name: "{{ item.name }}"
state: "{{ item.state | default('present') }}"
loop:
- { name: nginx, state: present }
- { name: python3-dev, state: present }
- { name: git, state: latest }Access dictionary values with dot notation: item.name. Use default filter for optional keys.
Use loop_control to customize loop behavior: label to control output, pause to add delays, and index_var to track the loop index.
Block/Rescue/Always
block/rescue/always is Ansible's try/catch/finally mechanism. Use it to handle errors gracefully, run cleanup tasks, or apply conditional logic to groups of tasks.
- name: Deploy application
block:
- name: Pull latest code
git:
repo: https://github.com/app/repo.git
dest: /opt/app
version: "{{ version }}"
- name: Install dependencies
pip:
requirements: /opt/app/requirements.txt
- name: Restart app
service:
name: app
state: restarted
rescue:
- name: Rollback on failure
git:
repo: https://github.com/app/repo.git
dest: /opt/app
version: "{{ previous_version }}"
- name: Restart with previous version
service:
name: app
state: restarted
always:
- name: Send notification
uri:
url: "{{ webhook_url }}"
method: POST
body: '{"status": "deploy attempted"}'block runs the tasks. If any fail, rescue runs. always runs regardless of success or failure.
Registering Results
- name: Check if nginx is running
command: systemctl is-active nginx
register: nginx_status
ignore_errors: yes
- name: Start nginx if not running
service:
name: nginx
state: started
when: nginx_status.rc != 0
- name: Show previous command output
debug:
msg: "Nginx status: {{ nginx_status.stdout }}"register captures stdout, stderr, rc (return code), and other task output. Use ignore_errors: yes to continue when a command fails.
When register is used with loop, the result is a list. Access individual results with results[index].stdout or use results | map(attribute='stdout') to extract values.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.