Ansible Task Execution Control - Conditional Statements

Subscribe to our newsletter and never miss any upcoming articles

Listen to this article

There are few things on which the play's end result depends such as

  • Predefined variable value
  • Some remote system's fact
  • The result of the previous task or play
  • On the value of another variable
  • Sometimes on the hosts criteria defined in the inventory file

There are various ways we can control execution flow in Ansible.

As part of this tutorial I will be covering 3 most important conditional statements.

  • when
  • failed_when
  • changed_when

When to use when ?

The when clause contains a raw Jinja2 expression without double curly braces.

We use when statement in the cases like skipping a particular task or step on one of the hosts based on some criteria. For example skipping some config changes if the operating system is a particular version.

Let us understand this by an example. Below is the playbook which will restart the apache service on 3 worker nodes. Out of them 2 are CentOS systems and one is Ubuntu and Apache service name is different on both the flavors.

My inventory file:

master ansible_host=lco-ansible-master.example.com ansible_user=ansible_user ansible_ssh_port=22
worker1 ansible_host=lco-worker1.example.com ansible_user=ansible_user ansible_ssh_port=22
worker2 ansible_host=lco-worker2.example.com ansible_user=ansible_user ansible_ssh_port=22
worker3 ansible_host=ubuntu-server2.example.com ansible_user=ansible_user ansible_ssh_port=22 ansible_python_interpreter=/usr/bin/python3

[workers]
worker1
worker2
worker3

And here is the playbook.

---
- name: Restart Apache Web-Server
  hosts: workers
  tasks:
    - name: Start Apache service on CentOS or RedHat system
      service: name=httpd  state=restarted enabled=yes
      become: yes
      when: ansible_os_family == "RedHat"

    - name: Start Apache service on Ubuntu Server
      service: name=apache2 state=restarted enabled=yes
      become: yes
      when: ansible_os_family == "Debian"

As you can see above we are putting when condition to determine the OS family and then accordingly instructing Ansible to start the service.

Let us execute the playbook.

conditionals $ ansible-playbook -i ../myinventory when.yml

PLAY [Restart Apache Web-Server] *******************************************************************************

TASK [Gathering Facts] *****************************************************************************************
ok: [worker2]
ok: [worker1]
ok: [worker3]

TASK [Start Apache service on CentOS or RedHat system] *********************************************************
skipping: [worker3]
changed: [worker2]
changed: [worker1]

TASK [Start Apache service on Ubuntu Server] *******************************************************************
skipping: [worker1]
skipping: [worker2]
changed: [worker3]

PLAY RECAP *****************************************************************************************************
worker1                    : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
worker2                    : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
worker3                    : ok=2    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

You can see from the above output that Start Apache service on Ubuntu Server task was skipped when it was running on CentOS servers and vice versa because of the when condition.

When to use failed_when ?

We can decide when to mark a play as failed using failed_when conditional statement. Multiple failed_when conditions can be joined with an implicit and operator. The task will fail when all conditions are met. Similarly we can use an or operator to trigger failure when even one condition is met.

Here is our example playbook.

---
 - name: Restarting httpd service
   hosts: worker1
   become: true
   gather_facts: false
   vars:
     service: httpd
   tasks:
     - shell: systemctl status httpd.service | grep running
       args:
         warn: false
       register: result
       failed_when: "'running' in result.stdout"
     - service:
        name: "{{ service }}"
        state: restarted

Here we are checking for the httpd service status. If the service is found running then our task will be marked as fail otherwise it will restart the service.

Let us execute the playbook.

Case 1 - when service was in running state.

conditionals $ ansible-playbook -i ../myinventory failed_when.yml

PLAY [Restarting httpd service] ********************************************************************************

TASK [shell] ***************************************************************************************************
fatal: [worker1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": true, "cmd": "systemctl status httpd.service | grep running", "delta": "0:00:00.009251", "end": "2021-01-27 18:42:37.552126", "failed_when_result": true, "msg": "non-zero return code", "rc": 1, "start": "2021-01-27 18:42:37.542875", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

PLAY RECAP *****************************************************************************************************
worker1                    : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

The task failed since the service was in running state on the client node.

No let us stop the service on client machine.

$ conditionals $ ansible -i ../myinventory worker1 -m shell -a "systemctl stop httpd.service" -b -kK
SSH password:
BECOME password[defaults to SSH password]:
worker1 | CHANGED | rc=0 >>

And run the playbook again.

conditionals $ ansible-playbook -i ../myinventory failed_when.yml

PLAY [Restarting httpd service] ********************************************************************************

TASK [shell] ***************************************************************************************************
changed: [worker1]

TASK [service] *************************************************************************************************
changed: [worker1]

PLAY RECAP *****************************************************************************************************
worker1                    : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

This time the playbook successfully executed the task and restarted the service as it was found not running.

When to use changed_when ?

When we use shell/command or other modules they reports “changed” status to true or 1 based on whether it thinks it affected machine state.

By verifying the return code or std output we sometimes know that our play did not make any changes on remote system. In those scenarios we just want to override that changed result so that it did not trigger any further action based on that (For e.g. any handlers to get triggered).

We will cover handlers in our upcoming articles.

syntax to use changed_when: (use it just below your register variable)

changed_when: false

I hope by now you will be comfortable with these three conditional statements.

That's all for now. Hope you like the article. Stay Tuned for more.

Thank you. Happy learning!

No Comments Yet