when — Run This Task Only If…
when attaches a Jinja2 expression to a task. If the expression evaluates true,
the task runs; if false, the task is reported as skipped and the playbook continues.
Three Common Patterns
YAML — when patterns
- hosts: all
tasks:
# 1. Match a fact
- name: Install firewalld (RHEL family only)
ansible.builtin.dnf:
name: firewalld
state: present
when: ansible_os_family == "RedHat"
# 2. Check a variable was passed
- name: Apply custom config
ansible.builtin.template:
src: app.conf.j2
dest: /etc/app.conf
when: app_custom_config is defined
# 3. Branch on a list inclusion
- name: Restart web tier services
ansible.builtin.systemd:
name: nginx
state: restarted
when: "'webservers' in group_names"
Operators You Can Use
| Operator | Meaning | Example |
|---|---|---|
== != | equality | when: x == "primary" |
> >= < <= | numeric comparison | when: ansible_memtotal_mb >= 4096 |
and or not | boolean logic | when: a and not b |
in / not in | list / string membership | when: "deploy" in admin_users |
is defined | variable exists | when: tls_cert is defined |
is not defined | variable doesn't exist | when: backup_path is not defined |
is none | variable is null | when: result.stdout is none |
is match("re") | regex match | when: ver is match("^8\\.") |
Conditioning on Prior Task Results
You can chain tasks: register the result of one, then run the next only if the first succeeded, failed, changed, or produced a particular output.
YAML — register + when
- name: Check if MySQL is already initialised
ansible.builtin.stat:
path: /var/lib/mysql/ibdata1
register: ibdata_stat
- name: Initialise data dir
ansible.builtin.command: mysqld --initialize-insecure
when: not ibdata_stat.stat.exists
- name: Check current version
ansible.builtin.command: mysql --version
register: version_check
changed_when: false # this is a read-only check
- name: Upgrade if version is too old
ansible.builtin.dnf:
name: mysql-server
state: latest
when: "'8.0' not in version_check.stdout"
Multi-Line when — and / or
For complex conditions, use a YAML list — every entry is implicitly AND-ed:
YAML — list-form when (implicit AND)
- name: Configure GR primary
ansible.builtin.command: |
mysql -e "SET GLOBAL group_replication_bootstrap_group=ON;"
when:
- mysql_role == "primary"
- inventory_hostname == groups['mysql_primary'][0]
- bootstrap_cluster | bool
For OR semantics, use a single line with explicit or:
YAML — explicit OR
- name: Send alert
ansible.builtin.uri:
url: https://hooks.slack.com/services/...
method: POST
when: deploy_failed or rollback_required
Combining when with Loops
when evaluates per loop iteration. The item variable is in scope, so you can
filter what the loop processes:
YAML — when inside a loop
- name: Open ports for services we use
ansible.posix.firewalld:
port: "{{{{ item.port }}}}/tcp"
state: enabled
permanent: true
loop:
- {{ name: ssh, port: 22, enabled: true }}
- {{ name: http, port: 80, enabled: false }}
- {{ name: https, port: 443, enabled: true }}
- {{ name: mysql, port: 3306, enabled: true }}
when: item.enabled
Common Beginner Traps
| Mistake | Fix |
|---|---|
Wrapping when in {{ }} | Don't — when is already evaluated as Jinja2: when: x == 1 not when: "{{ x == 1 }}" |
| Forgetting to quote a string with a colon | when: "':' in value" — inner colon needs quoting around the whole expression |
| Comparing a string to a bool | "true" == True is False — use my_bool | bool first |
Using = instead of == | Easy typo — = is assignment, == is comparison |