Ansible changed_when and failed_when with Examples

Ansible changed_when and failed_when

Have you ever had an Ansible task report “changed” even though it didn’t really change anything? Or worse, mark a task as “failed” when the error was harmless?

That’s where changed_when and failed_when come into play. These directives give you full control over how Ansible interprets task results—so your automation is accurate, clean, and smart.

This guide explores how to use changed_when and failed_when with practical examples.

What Are changed_when and failed_when?

DirectivePurpose
changed_whenControls whether a task is marked as changed, regardless of module behavior.
failed_whenControls whether a task is marked as failed, even if no error occurred.

By default:

  • A task is considered “changed” if it performs any action.

  • A task fails if it returns a non-zero exit code or an error.

These directives override that behavior using conditions based on registered variables.

Example 1: Avoid False Positives When Creating Files

You want to create a file only if it doesn’t exist and prevent Ansible from falsely reporting it as “changed” when it’s already there.

In this 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: Fail When Disk Usage Hits Threshold

You want to fail a task if disk usage on /dev/sda1 exceeds 80%, but not always treat it as a failure.

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:

  • Prevents the task from reporting as “changed”.
  • Triggers failure only when /dev/sda1 hits 80% usage.

Example 3: Start a Service If Inactive, Fail If Restart Fails

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:

  • Service starts only if it was not active.
  • Reports as “changed” only when needed.
  • Fails if systemctl returns an error during the start process.

Example 4: Fail if No Process is Listening on Port 80

You want to ensure a service is listening on port 80, and fail explicitly if not.

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: Report Change Only on Apache Restart

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 is not active.
  • changed_when directive reports a change if the web server was either ‘inactive’ or ‘failed’ before the playbook ran.

Example 6: Use changed_when for Idempotent Config Changes

You only want to change a config if a specific setting is missing—and not every time the playbook runs.

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 needs 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.

 Troubleshooting Tips

ProblemFix
Task always marked as changedAdd changed_when: false or use conditionally
Task fails with harmless errorsUse failed_when: <condition> or ignore_errors
Want both conditions togetherYou can combine changed_when + failed_when
Need debug infoUse debug: to print register outputs

Conclusion

Ansible’s changed_when and failed_when give you powerful control over task outcomes, helping you:

  • Avoid false change reports
  • Prevent unnecessary failures
  • Build idempotent, intelligent automation workflows

Use them wisely to improve playbook clarity, accuracy, and error handling.

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.

About Hitesh Jethva

Experienced Technical writer, DevOps professional with a demonstrated history of working in the information technology and services industry. Skilled in Game server hosting, AWS, Jenkins, Ansible, Docker, Kubernetes, Web server, Security, Proxy, Iptables, Linux System Administration, Domain Name System (DNS), and Technical Writing.

View all posts by Hitesh Jethva