Ansible when all item in loop is true

Viewed 206

Let say, I have this directory structure:

# ls /root/ansible_test/
one  two

And the playbooks looks like this:

- name: gathering all dirs
  stat:
    path: /root/ansible_test/{{ item }}/
  register: dir_check
  changed_when: false
  check_mode: no
  loop:
    - "one"
    - "two"
    - "three"

- name: check all of the dirs are created
  set_fact:
    all_dirs_created: true
  when: item.stat.exists == true
  loop: "{{ dir_check.results }}"

- debug:
    msg: "Not all dirs are created!"
  when: all_dirs_created is not defined

My problem is that the "one" and "two" dirs are created, so the fact will be defined because if one dir exists, then the loop will return true. I also tried opposite and checked item.stat.exists == false but if one dir is not created (three) then fact will be created also.

I would like to play the task set_fact only if all of the item in the loop is true or if one of them is false. How do I achieve this in this case?

1 Answers

Q: set_fact only if all of the items in the loop are true or if one of them is false

A: Count the items. For example

    - set_fact:
        dirs_missing: "{{ _all|int - _exist|int }}"
      vars:
        _all: "{{ dir_check.results|length }}"
        _exist: "{{ dir_check.results|
                    map(attribute='stat.exists')|
                    select|length }}"

gives (in your case)

  dirs_missing: '1'

Now, you can set whatever you want, e.g.

    - name: check all of the dirs are created
      set_fact:
        all_dirs_created: true
      when: dirs_missing|int == 0

    - debug:
        msg: "Not all dirs are created!
              (Exactly {{ dirs_missing }} missing.)"
      when: all_dirs_created is not defined

gives

TASK [check all of the dirs are created] ********************************
skipping: [localhost]

TASK [debug] ************************************************************
ok: [localhost] => 
  msg: Not all dirs are created! (Exactly 1 missing.)

You can simplify the code by using the filter counter from the latest collection Community.General. See Counting elements in a sequence, e.g.

    - name: check all of the dirs are created
      set_fact:
        all_dirs_created: true
      when: _counts[false] is not defined
      vars:
        _counts: "{{ dir_check.results|
                     map(attribute='stat.exists')|
                     community.general.counter }}"

The solutions above counted the items of the list to enable the evaluation of the option if one of them is false. The code can be simplified further if the counting of the items is not necessary. For example, test if all items are true

    - name: check all of the dirs are created
      set_fact:
        all_dirs_created: true
      when: dir_check.results|map(attribute='stat.exists') is all

However, then you have to test the existence of the variable all_dirs_created. More practical is setting both values. Ultimately, the expected functionality of your last two tasks can be reduced to the code below

    - name: check all of the dirs are created
      set_fact:
        all_dirs_created: "{{ dir_check.results|
                              map(attribute='stat.exists') is all }}"
    - debug:
        msg: Not all dirs are created!
      when: not all_dirs_created
Related