In Ansible, conditionals offer control in playbooks, allowing tasks, plays, or even entire roles to be executed or skipped based on certain conditions. Here's an overview of the most used conditionals and their uses:
1. when
The most commonly used conditional. It determines if a task or play should be executed.
You have to write an expression that evaluates to a boolean (true/false).
Example:
You want some tasks to be executed only on some specific servers.
---
- hosts: all
become: true
tasks:
- name: update repository index Ubuntu
apt:
update_cache: yes
when: ansible_distribution == "Ubuntu"
- name: update repository index Centos
yum:
update_cache: yes
when: ansible_distribution == "CentOS"
Here while executing the task. Ansible will first check the when condition. If it's true only then it will execute the main task.
You can see it's skipping if the condition is not true.
From where we are getting this "ansible_os_family" variable?
while running every Ansible playbook. By default, Ansible runs the gather_facts task. that task gathers all information about servers. which gives us access to these variables.
2. changed_when
Modifies the behavior of a task to report a "changed" status.
You have to write an expression that evaluates to a boolean (true/false).
Example:
Updating the repository is a basic task Suppose you don't want to consider that as a change then you can write it like below
---
- hosts: all
become: true
tasks:
- name: update repository index
apt:
update_cache: yes
when: ansible_distribution == "Ubuntu"
changed_when: false
- name: update repository index
yum:
update_cache: yes
when: ansible_distribution == "CentOS"
changed_when: false
It will give you a changed=0
3. failed_when
- Modifies the behavior of a task to report a "failed" status based on custom conditions.
Example: You want to check disk usage and want to mark it as a failure if usage is more than 90%.
---
- hosts: all
vars:
disk_usage_threshold: 90
tasks:
- name: Check root filesystem disk usage
command: df -h /
register: df_output
changed_when: false
failed_when: >
(ansible_distribution == "CentOS" and (df_output.stdout_lines[-1].split()[4]|replace('%',''))|int > disk_usage_threshold) or
(ansible_distribution == "Ubuntu" and (df_output.stdout_lines[1].split()[4]|replace('%',''))|int > disk_usage_threshold)
The output will be like the below
as my disks are not filled let me fill the disk of one server.
Now you can see that for one server script has failed showing disk is filled 94%.
4. until
- Used for retrying a task until a certain condition is met or a certain number of retries is reached.
Example: Retry a task until a file exists.
---
- hosts: all
tasks:
- name: Check if file exists
stat:
path: "/tmp/file.txt"
register: file_status
until: file_status.stat.exists
retries: 5
delay: 3
You can see playbook is trying to check if file exist at that location or not. while it's running i logged into one server and created file. you can see it's showing file found for one server. it kept trying for another server.
5. assert
- Used for failing a play if certain conditions are not met.
Example: Fail if the free memory is less than 10 GB.
---
- hosts: all
tasks:
- name: Get filesystem details for /
ansible.builtin.setup:
filter: ansible_mounts
- name: Assert minimum disk space requirements
assert:
that:
- item.size_available >= 10000000000 # 10 GB in bytes
fail_msg: "Not enough disk space on {{ item.mount }}. At least 10GB required."
success_msg: "{{ item.mount }} has sufficient space."
with_items: "{{ ansible_mounts }}"
when: item.mount == "/"
Output
You can see in the above image it failed for one server having insufficient space and was successful for another.
6. block and rescue
block
groups tasks together.rescue
defines tasks to be executed if any task in theblock
fails.It's like try and catch in javascript, java etc.
Example: Attempt to install a package and handle failures.
---
- hosts: all
become: yes
vars:
package_name: example_package
tasks:
- name: Attempt to install a package
block:
- name: Install the "{{ package_name }}"
package:
name: "{{ package_name }}"
state: present
rescue:
- name: Handle the failure
debug:
msg: "Failed to install {{ package_name }} . Check if the package name is correct or if there's a repo issue."
always:
- name: This always gets executed
debug:
msg: "This message will always be displayed, regardless of success or failure in the block above."
As you can see above script will fail to install a package called example_package as no such package exists. still, we are not shown any failure(failed=0). You can see the task in rescue is executed correctly.
if we put package_name=wget. which is actually a real package name. task will succeed. It will look like below.
7. Conditional imports and includes
- Conditionally import playbooks or include tasks/roles based on certain conditions.
Example: install web server depending on user input
web_setup.yml
---
- hosts: all
become: true
vars_prompt:
- name: webserver_type
prompt: "Which web server do you want to install? (apache/nginx)"
private: no
tasks:
- name: Include tasks based on web server choice
include_tasks: "install_{{ webserver_type }}.yml"
when: webserver_type in ['apache', 'nginx']
install_apache.yml
---
- name: Install Apache
package:
name: apache2
state: present
when: ansible_distribution == "Ubuntu"
- name: Install httpd
package:
name: httpd
state: present
when: ansible_distribution == "CentOS"
install_nginx.yml
---
- name: Install Nginx
ansible.builtin.package:
name: nginx
state: present
so depending on user input it will select playbook and run it.
for apache as input you can see it ran install_apache.yml
For nginx as input, you can see it ran install_nginx.yml
In conclusion, Ansible's conditionals provide a powerful way to bring logic and decision-making into playbooks, ensuring that infrastructure automation is both flexible and precise. Like any tool, the key is understanding the available options and applying them judiciously.
Happy automating! 🤖
Top comments (0)