Using when Function in Ansible

In Ansible, the when statement conditionally executes tasks based on certain conditions. It allows you to control the execution flow in your playbooks based on the values of variables, facts, or the results of previous tasks.

Ansible supports various types of conditions you can use with when:

  • Boolean values: You can directly use true or false for simple conditions.
  • Variables: You can use variables defined in your playbook or inventory files to control task execution.
  • Facts: You can access facts gathered during Ansible's execution using ansible_facts or specific fact names. For example, when: ansible_os_family == "Debian" checks if the operating system belongs to the Debian family. You can use these facts to conditionally execute tasks.
  • Jinja2 Expressions: You can use Jinja2 expressions to evaluate complex conditions based on variable values, mathematical operations, string comparisons, etc.
  • Registered Variables: You can use the results of previous tasks (stored in registered variables) to determine whether subsequent tasks should be executed.

In this post, I will explain how to use the Ansible when using practical examples.

Basic syntax of when in Ansible

Here's a basic syntax of how to use when in Ansible:

- name: Task Name
  <module_name>:
    <module_options>
  when: <condition>

In this syntax:

  • name: A descriptive name for the task.
  • module_name: The Ansible module you want to execute.
  • module_options: Any options or parameters specific to the module.
  • condition: The condition under which the task should be executed. It's usually written as an expression evaluating to true or false.

Example 1: Use Ansible when with a loop and conditionals

You can use the Ansible when statement with a loop and conditionals to install the Apache web server based on the package manager detected on the target system. Here is an example playbook that shows you how to install Apache or HTTPd based on the package manager:

---
- name: Install Apache or HTTPd based on package manager
  hosts: all
  become: true
  tasks:
    - name: Determine package manager
      set_fact:
        apache_pkg:
          - "{{ 'apache2' if ansible_pkg_mgr == 'apt' else 'httpd' }}"
      when: ansible_pkg_mgr == 'apt' or ansible_pkg_mgr == 'dnf' or ansible_pkg_mgr == 'yum'

    - name: Install Apache or HTTPd
      ansible.builtin.package:
        name: "{{ apache_pkg }}"
        state: present
      ignore_errors: yes

    - name: Print error message if no suitable package manager found
      debug:
        msg: "No suitable package manager found to install Apache or HTTPd."
      when: apache_pkg is undefined

This Ansible playbook is designed to install either Apache or HTTPd based on the package manager available on the target system. Let's break down each part of the playbook:

  • Determine package manager: This task uses the set_fact module to determine the appropriate package name for Apache or HTTPd based on the detected package manager (ansible_pkg_mgr). If the package manager is apt, it sets apache_pkg to apache2; otherwise, it sets it to httpd. This task is conditional (when) on the package manager being one of apt, dnf, or yum.
  • Install Apache or HTTPd: This task uses the ansible.builtin.package module to install the package specified in apache_pkg. It sets the state to present, indicating that the package should be installed. The ignore_errors: yes directive is used to continue execution even if the package installation fails.
  • Print error message if no suitable package manager found: This task prints a debug message if apache_pkg is undefined, indicating that no suitable package manager was detected to install Apache or HTTPd.

Now, run the playbook.

ansible-playbook playbook.yml

You will see the following screenshot.

Example 2: Use Ansible when with a loop

Here's an example of using the Ansible when statement with a loop to create directories for different users based on specified conditions:

- name: Create directories for specific users
  hosts: node1
  tasks:
    - name: Create directories for multiple users
      ansible.builtin.file:
        path: "/home/{{ item }}/data"
        state: directory
      when: item != 'admin'   # Create directories for all users except 'admin'
      loop:
        - user1
        - user2
        - user3
        - admin

In this example, the task creates directories for multiple users but skips the directory creation for the user admin using when condition with the loop.

Now, run the playbook.

ansible-playbook playbook.yml

You will see the following screenshot.

💡 If you are new to Ansible and want to learn it from the scratch, our Ansible tutorial series will be of great help. It's written for RHCE exam but it helps you the same whether you are preparing for the exam or not.

RHCE Ansible EX294 Exam Preparation Course
Learn all the essentials of Ansible with this hands-on tutorial series. It is ideal for RHCE Ansible EX294 exam preparation.

Example 3: Run a command only if a file exists

In this example, we will create a playbook that prints the content of a remote file if it exists and fetches the file to a local directory before displaying its content.

---
- name: Print content of remote file if it exists
  hosts: node3
  tasks:
    - name: Check if the file exists
      ansible.builtin.stat:
        path: /etc/file
      register: file_stat

    - name: Print content of the file if it exists
      ansible.builtin.fetch:
        src: /etc/file
        dest: /tmp/file
        flat: yes
      when: file_stat.stat.exists

    - name: Display content of the file
      debug:
        msg: "File content: {{ lookup('file', '/tmp/file') }}"
      when: file_stat.stat.exists

Let's break down the playbook:

  • The playbook targets the node3 host.
  • The first task uses the stat module to check if the file /etc/file exists on the target node. The result is registered in the variable file_stat.
  • The second task uses the fetch module to fetch the file /etc/file from the remote host to the local directory /tmp/file. The flat: yes option ensures that the file is fetched without any directory structure. This task only runs if the file exists on the target node (when: file_stat.stat.exists).
  • The third task uses the debug module to display the content of the file fetched to the local directory. This task also runs only if the file exists on the target node (when: file_stat.stat.exists).

Use the following command to run the playbook.

ansible-playbook playbook.yml

You will see the following screenshot.

Example 4: Use Ansible when with facts

In this playbook, we will demonstrate the usage of conditional statements when based on Ansible facts to execute tasks selectively depending on the distribution and major version of the target systems.

---
- name: Ansible When with facts example
  hosts: all
  become: true
  tasks:

    - name: Display distribution and major version
      debug:
        msg: "Distribution: {{ ansible_facts['distribution'] }}, Major Version: {{ ansible_facts['distribution_major_version'] }}"


    - name: Shutdown the remote node if distribution is Ubuntu with a major Version 22.04 
      ansible.builtin.command: /sbin/shutdown -t now
      when: ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_major_version'] == '22'

    - name: Shutdown the remote node if distribution is RedHat 
      ansible.builtin.command: /sbin/shutdown -t now
      when: ansible_facts['os_family'] == "RedHat"

This playbook prints the distribution and major version of the target systems and then selectively shuts down the remote node based on the detected distribution and OS family.

Let's break down each part of the playbook:

  • Display distribution and major version: This task uses the debug module to display the distribution and major version of the target systems. It uses ansible_facts['distribution'] and ansible_facts['distribution_major_version'] to access these facts.
  • Shutdown the remote node if the distribution is Ubuntu with a major Version 22.04: This task uses the ansible.builtin.command module to execute the /sbin/shutdown -t now command if the distribution is Ubuntu and the major version is 22.04. The when condition checks if ansible_facts['distribution'] is equal to Ubuntu and ansible_facts['distribution_major_version'] is equal to 22.
  • Shutdown the remote node if distribution is RedHat: Similar to the previous task, this task executes the /sbin/shutdown -t now command if the target system's OS family is RedHat. It checks this condition using ansible_facts['os_family'].

Now, run the above playbook.

ansible-playbook playbook.yml

You will see the following screenshot:

Example 5: Use Ansible when based on registered values

Ansible's when clause allows you to execute tasks conditionally based on the output of previous tasks.

In this playbook, you can use the stat module in Ansible along with the when condition to check if a remote directory is empty. Here's how to use it with registered values to check if a remote directory is empty:

---
- name: Check the empty directory
  hosts: node3
  tasks:
    - name: Get directory stats
      stat:
        path: /opt
      register: dir_info

    - name: Find files in a directory
      find:
        paths: /opt
        file_type: file
        hidden: true
      register: files_found

    - name: Fail if directory not empty
      fail:
        msg: "Directory /opt is not empty."
      when: dir_info.stat.exists and files_found.matched > 0

    - name: Debug message if directory is empty
      debug:
        msg: "Directory /opt is empty."
      when: dir_info.stat.exists and files_found.matched == 0

In this example:

  • The first task uses stat to get information about the directory, including its existence. It stores the result in dir_info.
  • The second task uses find to search for all files (including hidden) within the directory. It stores the number of matches in files_found.matched.
  • The fail task triggers only if the directory exists (dir_info.stat.exists is True) and there are files found (files_found.matched is greater than 0). This indicates a non-empty directory.
  • The debug task prints a message if the directory is empty (both existence check and no files found).

Let's run this playbook:

ansible-playbook playbook.yml

You will see the following screenshot:

Conclusion

Throughout this article, we've explored various examples showcasing the versatility of the Ansible when function. From basic conditionals based on system facts such as distribution and version to more complex scenarios involving file existence checks and custom conditionals.

By leveraging Ansible facts, variables, and logical operators within the "when" statement, users can create flexible and dynamic playbooks tailored to specific scenarios and environments.

✍️
Author: Hitesh Jethwa has more than 15+ years of experience with Linux system administration and DevOps. He likes to explain complicated topics in easy to understand way.