In a playbook or in a role sometimes we want to run different tasks based on different conditions. In most cases it depends on a fact (detail about the managed host) or some data collected during the playbook run. Ansible conditionals are there to make it possible to run different tasks based on different conditions, or skip executing tasks entirely.
I’ve already wrote it before that loops and conditionals are a double edged sword. They can make simple tasks complex, and complex tasks impossible. This is why we have to use just the necessary amount of loops and conditionals in our code if we want to write futureproof playbooks and roles.
We can create simple conditional statements with the when
keyword.
Conditional statements after tasks or blocks will look at the condition and decide what to do with the task or block.
If the certain condition is met then a true value is returned, and the task will be executed. If the condition isn’t met with the expectations, it returns a false value and the task execution will be skipped.
Most of our conditions will be based on facts gathered on a managed node and variables registered while running Ansible playbooks.
Fact based conditionals
If we want to use facts in our Ansible roles (and playbooks), we have to use the fact names listed by the ansible.builtin.debug
module, like:
"distribution": "Debian",
"distribution_file_variety": "Debian",
"distribution_major_version": "11",
"distribution_release": "bullseye",
"distribution_version": "11",
We can use these variable names in our playbooks and roles.
Here is a simple task to display the facts available on a managed node from a playbook:
- name: Show facts available on the system
ansible.builtin.debug:
var: ansible_facts
Ansible uses the Jinja2 templating engine for testing conditional statements.
Ansible uses Jinja2 tests and filters in conditionals. Ansible supports all the standard tests and filters, and adds some unique ones as well.
Ansible Docs
There are many options to control execution flow in Ansible. You can find more examples of supported conditionals at https://jinja.palletsprojects.com/en/latest/templates/#comparisons.
Ansible Docs
Often you want to execute or skip a task based on facts. Facts are attributes of individual hosts, including IP address, operating system, the status of a filesystem, and many more. With conditionals based on facts:
Ansible Docs
- You can install a certain package only when the operating system is a particular version.
- You can skip configuring a firewall on hosts with internal IP addresses.
- You can perform cleanup tasks only when a filesystem is getting full.
So according to the Ansible documentation we can make sure to only run a task when a specific condition is met. Our play-test.yml
playbook from the previous article is still on our filesystem, and we can use it to test some conditionals on the localhost (on our Ansible control node). That’s also a Debian Bullseye server by the way.
When we want to use facts in conditional statements we have list them with the ansible.builtin.debug
module:
- name: Show facts available on the system
ansible.builtin.debug:
var: ansible_facts
This task will show us all the possible fact variables for the when
keyword.
Now we can extend the play-test.yml
playbook and we can just play with the possibilities and practice:
---
- name: Test playbook for looping
hosts: ansible
tasks:
# - name: Show facts available on the system
# ansible.builtin.debug:
# var: ansible_facts
- name: Iteration through a list
ansible.builtin.debug:
msg: "{{ item }}"
loop:
- test1
- test2
when: ansible_facts['distribution'] == 'Debian'
- name: Iteration through a list of dictionaries
ansible.builtin.debug:
msg: "{{ item.first_id }} and {{ item.second_id }}"
loop:
- { first_id: test1, second_id: secret1 }
- { first_id: test2, second_id: secret2 }
when: ansible_facts['distribution'] == 'CentOS'
- name: Iteration through a dictionary
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ users | dict2items }}"
vars:
users:
user1: tamas
user2: tom
when: ansible_facts['hostname'] != 'LaptopDebtop'
When we run this playbook we will see how Ansible behaves when it hits conditional statements in tasks, and the conditions are not met:
TASK [Iteration through a list of dictionaries] ************************************************************************************************************
skipping: [ansible] => (item={'first_id': 'test1', 'second_id': 'secret1'})
skipping: [ansible] => (item={'first_id': 'test2', 'second_id': 'secret2'})
skipping: [ansible]
When a condition is not met, Ansible skips the task and continues with executing the other tasks in the playbook.
We can use logical operators like and/or to write multiple conditions:
- name: Iteration through a dictionary
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ users | dict2items }}"
vars:
users:
user1: tamas
user2: tom
when: ansible_facts['hostname'] != 'LaptopDebtop' or
ansible_facts['distribution'] == 'Debian'
Conditions on registered variables during run time
Playbooks can save (register) variables during run time with the register
keyword. A registered variable contains the task attributes and the output the task generated.
We can see the content of the output in variable.stdout
and variable.stderr
. Our example will explain it better below.
Let’s capture a variable in our test playbook and look into its contents!
- name: Register a variable
ansible.builtin.shell: cat /etc/hosts
register: hosts_file
- name: Use a variable
ansible.builtin.debug:
var: hosts_file
- name: Condition based on a registered variable
ansible.builtin.debug:
var: hosts_file
when: hosts_file.stderr
The first task will capture the data about the task, and it will capture the output of the shell command. Then the second task simply shows us the captured data from the first task. The third task uses the registered variable to determine if we had any error message. If the first task failed then the third task will run. If the first task was successful, then the third task gets skipped.
We should avoid using the shell module in our code. We used it here only for practicing with registered variables!
Conditional statements reference: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html#conditionals
Now we can start automating our personal practice lab!
If you have anything to share then please visit my Tom’s IT Cafe Discord Server!