Order of evaluation of included tasks and loop

I have a playbook that conditionally includes tasks within a loop. In my use-case, the execution of the tasks being included may influence the evaluation of the condition for the next iteration, however, Ansible seems to include the tasks X times at one moment, so the condition for the second iteration gets evaluated before execution of the first iteration of included tasks, and thus yields the wrong result.

A minimal test case follows here:

roles/test_case/main.yml:

`

  • include_tasks: inc.yml
    when: test_var|default(0) < 5
    loop: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

`

roles/test_case/inc.yml:

`

  • set_fact:
    test_var: “{{ (test_var|default(0)|int + 1)|string }}”

`

Output:

`

$ ansible-role --hosts localhost -vvv test_case
ansible-playbook 2.5.4
config file = /etc/ansible/ansible.cfg
configured module search path = [u’/home/harry/.ansible/plugins/modules’, u’/usr/share/ansible/plugins/modules’]
ansible python module location = /usr/lib/python2.7/dist-packages/ansible
executable location = /usr/bin/ansible-playbook
python version = 2.7.12 (default, Dec 4 2017, 14:50:18) [GCC 5.4.0 20160609]
Using /etc/ansible/ansible.cfg as config file
Parsed /etc/ansible/inventory/hosts inventory source with ini plugin
Parsed /etc/ansible/inventory/openstack.yaml inventory source with openstack plugin

PLAYBOOK: tmpJMfRh7 *************************************************************************************************************************************************************************************************************************
1 plays in /home/harry/playbook/tmpJMfRh7

PLAY [localhost] ****************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/tmpJMfRh7:4
Using module file /usr/lib/python2.7/dist-packages/ansible/modules/system/setup.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: harry
<127.0.0.1> EXEC /bin/sh -c ‘echo ~harry && sleep 0’
<127.0.0.1> EXEC /bin/sh -c ‘( umask 77 && mkdir -p “echo /home/harry/.ansible/tmp/ansible-tmp-1528793952.57-263974998165124” && echo ansible-tmp-1528793952.57-263974998165124=“echo /home/harry/.ansible/tmp/ansible-tmp-1528793952.57-263974998165124” ) && sleep 0’
<127.0.0.1> PUT /home/harry/.ansible/tmp/ansible-local-24007PeqOdf/tmpVt9tuW TO /home/harry/.ansible/tmp/ansible-tmp-1528793952.57-263974998165124/setup.py
<127.0.0.1> EXEC /bin/sh -c ‘chmod u+x /home/harry/.ansible/tmp/ansible-tmp-1528793952.57-263974998165124/ /home/harry/.ansible/tmp/ansible-tmp-1528793952.57-263974998165124/setup.py && sleep 0’
<127.0.0.1> EXEC /bin/sh -c ‘/usr/bin/python /home/harry/.ansible/tmp/ansible-tmp-1528793952.57-263974998165124/setup.py && sleep 0’
<127.0.0.1> EXEC /bin/sh -c ‘rm -f -r /home/harry/.ansible/tmp/ansible-tmp-1528793952.57-263974998165124/ > /dev/null 2>&1 && sleep 0’
ok: [localhost]
META: ran handlers

TASK [test_case : include_tasks] ************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/main.yml:1
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost
included: /home/harry/playbook/roles/test_case/tasks/inc.yml for localhost

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “1”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “2”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “3”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “4”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “5”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “6”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “7”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “8”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “9”
},
“changed”: false
}

TASK [test_case : set_fact] *****************************************************************************************************************************************************************************************************************
task path: /home/harry/playbook/roles/test_case/tasks/inc.yml:1
ok: [localhost] => {
“ansible_facts”: {
“test_var”: “10”
},
“changed”: false
}
META: ran handlers
META: ran handlers

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
localhost : ok=21 changed=0 unreachable=0 failed=0

`

Expected: 5 iterations instead of 10.

Is this an Ansible bug, or is this how it is intended to work? And in the latter case, what would be the alternative?

This is not a bug.

The when statement only applies to the include_tasks, but all iterations of loop are processed before any of the tasks inside run.

Effectively we see an include_tasks with 10 iterations and then add the tasks for each iteration into the task queue, and process them.

So by the time that your set_fact has executed, all of the include_tasks tasks have been executed.

A minimal test case follows here:

roles/test_case/main.yml:
- include_tasks: inc.yml
  when: test_var|default(0) < 5
  loop: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

Matt explained why this doesn't work.
Your when would not have work anyway because test_var is a string not a int.

roles/test_case/inc.yml:
- set_fact:
    test_var: "{{ (test_var|default(0)|int + 1)|string }}"

string is redundant, it will be a string no matter what.

Expected: 5 iterations instead of 10.

Is this an Ansible bug, or is this how it is intended to work? And in the
latter case, what would be the alternative?

Move the when inside the inc.yml, you'll still get 10 task but the last 5 is skipped.