Ansible’s changed_when and failed_when are conditional statements used in playbooks to control when a task should report a change or failure. These conditions are crucial for accurate reporting and handling exceptional scenarios during automation.
This guide explores these directives, providing practical examples to illustrate their use.
Table of Contents
What is changed_when and failed_when?
- changed_when: This condition determines whether Ansible should report a task as changed. By default, a task reports as changed if it performs an action. However, there are cases where you may want to override this default behavior based on specific conditions.
- failed_when: This condition controls whether a task should be marked as failed. By default, Ansible marks a task as failed if it returns a non-zero exit code or meets a failure condition. The failed_when directive allows you to specify custom conditions for failure.
Example 1: Using changed_when to Avoid False Changes
Consider a scenario where we check if a file exists and create it if it doesn’t. We don’t want Ansible to report a change if the file already exists.
Here is an example playbook the changed_when directive ensures that the task is only marked as changed if the file didn’t exist beforehand.
- name: Check and create file if not exists
hosts: localhost
tasks:
- name: Check if file exists
stat:
path: /tmp/example_file.txt
register: file_stat
- name: Create file if it does not exist
file:
path: /tmp/example_file.txt
state: touch
changed_when: not file_stat.stat.exists
In this example:
- stat module checks if the file /tmp/example_file.txt exists, storing the result in file_stat.
- The file module creates the file only if it doesn’t exist. The changed_when directive uses the condition not file_stat.stat.exists to ensure that Ansible reports a change only if the file was created.
Example 2: Using failed_when to Handle Command Failures
Suppose we have a task to check the disk usage of a directory, and we want to fail the playbook if the usage exceeds a certain threshold.
This playbook checks the disk usage of /var and fails if /dev/sda1 has reached 80% capacity.
- name: Check disk usage
hosts: localhost
tasks:
- name: Get disk usage of /var
command: df -h /var
register: disk_usage
changed_when: false
failed_when: "'/dev/sda1' in disk_usage.stdout and '80%' in disk_usage.stdout"
In this example:
- command module runs df -h /var to check the disk usage of the /var directory.
- result is stored in disk_usage.
- changed_when: false ensures the task never reports a change since we’re only checking the disk usage.
- failed_when directive marks the task as failed if the disk usage exceeds 80% (‘80%’ in disk_usage.stdout). This condition checks if the specific filesystem (/dev/sda1) has high usage.
Example 3: Using Both changed_when and failed_when
Imagine a task that checks if a service is running and starts it if it is not. Additionally, we want to fail the task if the service fails to start.
This playbook checks if my_service is running and attempts to start it if it is not, marking the task as changed and failing the task if starting the service fails.
- name: Ensure my_service is running
hosts: localhost
tasks:
- name: Check if my_service is running
shell: systemctl is-active my_service
register: service_status
ignore_errors: yes
- name: Start my_service if not running
shell: systemctl start my_service
when: service_status.stdout != 'active'
changed_when: service_status.stdout != 'active'
failed_when: service_status.stdout != 'active' and service_status.rc != 0
In this example:
- First task checks if my_service is running using systemctl is-active.
- Register directive saves the output in service_status.
- Second task starts my_service if it is not running.
- The changed_when directive reports a change only if the service was not running before.
- The failed_when directive marks the task as failed if the service was not active and the return code is not zero, indicating a failure to start the service.
Example 4: Using failed_when to Handle Conditional Errors
Suppose we have a task to check for a process running on a specific port. We want to fail the task if the process is not running.
This playbook checks if a process is running on port 80 and fails with a specific message if no process is found.
- name: Check if process is running on port 80
hosts: localhost
tasks:
- name: Get process ID using port 80
shell: netstat -tuln | grep ':80'
register: process_check
ignore_errors: yes
- name: Fail if no process is found on port 80
fail:
msg: "No process found running on port 80."
failed_when: process_check.stdout == ''
In this example:
- netstat command checks for any process listening on port 80, storing the output in process_check.
- ignore_errors: yes allows the playbook to continue even if the command fails (e.g., if the port is not in use).
- failed_when directive triggers a failure if process_check.stdout is empty, indicating no process was found on port 80.
Example 5: Using changed_when with Multiple Conditions
Imagine we have a playbook that checks the status of a web server and wants to report a change only if the server was not previously running and is started by the playbook.
This playbook checks the status of the Apache web server and starts it if it is not active, marking the task as changed if the server was either inactive or failed.
- name: Check and start web server
hosts: localhost
tasks:
- name: Check web server status
command: systemctl is-active apache2
register: webserver_status
ignore_errors: yes
- name: Start web server
command: systemctl start apache2
when: webserver_status.stdout != 'active'
changed_when: "'inactive' in webserver_status.stdout or 'failed' in webserver_status.stdout"
In this example:
- systemctl is-active apache2 command checks if the Apache web server is running, with the result stored in webserver_status.
- systemctl start apache2 command starts the web server only if it was not active.
- changed_when directive reports a change if the web server was either ‘inactive’ or ‘failed’ before the playbook ran.
Example 6: Using changed_when for Idempotency
In scenarios where a command might be rerun without causing changes, changed_when can ensure accurate reporting. For example, resetting a configuration only if it is not already set correctly.
This playbook checks the current configuration in /etc/myapp/config and sets the correct configuration if the setting correct_setting=true is not found, marking the task as changed if the configuration needed to be updated.
- name: Ensure correct configuration is set
hosts: localhost
tasks:
- name: Check current configuration
command: cat /etc/myapp/config
register: current_config
- name: Set correct configuration
command: echo "correct_setting=true" > /etc/myapp/config
when: "current_config.stdout.find('correct_setting=true') == -1"
changed_when: "current_config.stdout.find('correct_setting=true') == -1"
In this example:
- cat command checks the current configuration, with the output stored in current_config.
- command echo “correct_setting=true” > /etc/myapp/config sets the configuration only if the correct setting is not already present.
- changed_when directive reports a change only if the setting was not already present.
Conclusion
The changed_when and failed_when directives are powerful tools in Ansible for controlling task outcomes and reporting. They allow for more granular control over playbook execution, ensuring accurate reporting and handling of exceptional situations. By using these modules into your playbooks, you can create more robust and reliable automation scripts.
FAQs
1. Can I use changed_when with registered variables?
Yes, changed_when can be used with registered variables to conditionally mark a task as changed based on the output of previous tasks.
2. How can failed_when help in handling errors?
failed_when allows you to define specific failure conditions, preventing unnecessary task failures due to non-critical errors or warnings.
3. Can changed_when and failed_when be used together in the same task?
Yes, both changed_when and failed_when can be used in a single task to control how a task's result is interpreted and whether it should be marked as changed or failed.