Ansible include_tasks with loop failed to check when condition

Viewed 1375

I want to include tasks based on the condition set inside the task.

tasks:
- name: SET INIT STATUS
  set_facts:
    passed: true

- name: INCLUDE TASKS WITH LOOP
  include_tasks: update_status.yml
  loop: [1, 2, 3]
  when: passed

update_status.yml:

- name: OUTPUT THE STATUS
  debug: msg="ITEM: {{item}} has status {{passed}}"
- name: UPDATE the Status
  set_fact:
    passed: false

What I expected that it will only run once for item 1 because after it runs, it will change the status to false and then it failed the condition check. But it actually run 3 time so looks like it does not check the status during the loop again.

Here is the output

TASK: [SET INIT STATUS]
ok: [localhost]

TASK: [INCLUDE TASKS WITH LOOP]
included: simple.yml for localhost
included: simple.yml for localhost
included: simple.yml for localhost

TASK: [OUTPUT THE STATUS]
ok: [localhost] => {
    "msg": ITEM 1 has status True
}

TASK: [UPDATE the Status]
ok: [localhost]

TASK: [OUTPUT THE STATUS]
ok: [localhost] => {
    "msg": ITEM 2 has status False
}

TASK: [UPDATE the Status]
ok: [localhost]

TASK: [OUTPUT THE STATUS]
ok: [localhost] => {
    "msg": ITEM 3 has status False
}

TASK: [UPDATE the Status]
ok: [localhost]
3 Answers

After trying different approach, i was able to achieve what i want by putting the loop inside a block.

- name: LOOP WITH INCLUDE_TASK INSIDE BLOCK
  block:
    - name: LOOP WITH INCLUDE_TASK
      include_tasks: simple.yml
      loop: [1, 2, 3]
  when: passed

And here is the result

TASK [SET passed] ******************************************************************************************
ok: [localhost]

TASK [LOOP WITH INCLUDE_TASK] ******************************************************************************************
included: /home/richard/samples/simple.yml for localhost
included: /home/richard/samples/simple.yml for localhost
included: /home/richard/samples/simple.yml for localhost

TASK [SHOW THE STATUS] ******************************************************************************************
ok: [localhost] => {
    "msg": "Item 1 has status True"
}

TASK [CHANGE THE STATUS] ******************************************************************************************
ok: [localhost]

TASK [SHOW THE STATUS] ******************************************************************************************
skipping: [localhost]

TASK [CHANGE THE STATUS] ******************************************************************************************
skipping: [localhost]

TASK [SHOW THE STATUS] ******************************************************************************************
skipping: [localhost]

TASK [CHANGE THE STATUS] ******************************************************************************************
skipping: [localhost]

I think you're hitting a fun Jinja side effect. Try this for your conditional.

when: passed|bool

This will ensure its being evaluated as a bones expression.

Q: "Loop failed to check when condition."

A: It's necessary to understand 2 points to explain what's going on here.

1) The condition when: passed, when applied to include_tasks, will be expanded to when: true and will be inherited by all included tasks. This will make the tasks inside update_status.yml look effectively

- name: OUTPUT THE STATUS
  debug: msg="ITEM: {{item}} has status {{passed}}"
  when: true

- name: UPDATE the Status
  set_fact:
    passed: false
  when: true

2) At the same time, the condition when: passed, applied to the loop, decides whether the loop will start or not. Once the loop is started it will not take the condition into consideration anymore and will run all iterations.

This explains the results of the tasks.

Notes:

  • ATM (Ansible version 2.9) it is not possible to break a loop in Ansible.

  • It's a good idea to explicitly cast Boolean bare variables in conditionals when: passed|bool. See Bare variables in conditionals.


There are 2 syntax errors in the code:

  • "ERROR! couldn't resolve module/action 'set_facts'". There is no module set_facts.

Correct syntax

- name: SET INIT STATUS
  set_fact:
    passed: true
  • "Syntax Error while loading YAML. mapping values are not allowed in this context ... - name: OUTPUT THE STATUS\n debug: msg=\"ITEM: {{item}} has status {{passed}}\"

Correct syntax

- name: OUTPUT THE STATUS
  debug:
    msg: "ITEM: {{ item }} has status {{ passed }}"
Related