problem with with_items + when in ansible 2.2

Hi,

I found this bug, which is closed, but I believe I’m still seeing the same problem:

Ansible 2.x errors on the task with the with_items loop with when: some_var is defined condition when it is used in a role, but not in a play

https://github.com/ansible/ansible/issues/14383

In my case I have a very simple task in a role:

  • name: install sw requested for this box

apt:
name: “{{item}}”
state: present
install_recommends: yes
with_items: “{{additional_sw}}”
when: additional_sw is defined

this fails with an error:

fatal: [localhost]: FAILED! => {“failed”: true, “msg”: “‘additional_sw’ is undefined”}

which is true, but the point of the when: is exactly to avoid that. To make it even weirder, I tried to move the when: to a block wrapping that task, and still get the same error.

Eventually I defined a default value to an empty list in role/defaults/main.yml:

additional_sw:

this worked but I’d still prefer to resolve the above problem.

any idea what’s going on?

thanks,

Spike

I found this bug, which is closed, but I believe I'm still seeing the same
problem:

Ansible 2.x errors on the task with the `with_items` loop with `when:
some_var is defined` condition when it is used in a role, but not in a play
https://github.com/ansible/ansible/issues/14383

In my case I have a very simple task in a role:

    - name: install sw requested for this box
      apt:
        name: "{{item}}"
        state: present
        install_recommends: yes
      with_items: "{{additional_sw}}"
      when: additional_sw is defined

this fails with an error:

fatal: [localhost]: FAILED! => {"failed": true, "msg": "'additional_sw' is
undefined"}

which is true, but the point of the *when:* is exactly to avoid that. To
make it even weirder, I tried to move the *when:* to a *block* wrapping
that task, and still get the same error.

You have the point of when wrong, this is by design.
When is applied to every iteration of elements in with_items.

You can read more about it here
https://docs.ansible.com/ansible/playbooks_conditionals.html#loops-and-conditionals

Eventually I defined a default value to an empty list in
role/defaults/main.yml:

additional_sw:

this worked but I'd still prefer to resolve the above problem.

any idea what's going on?

Just remove the when, an change the with_items to
with_items: "{{ additional_sw | default() }}"

This translate to, if additional_sw variable is not defined use a empty list, the result is that the task will be skipped.

thanks for the explanation Kai, makes sense, it effectively does the same thing I’ve done by setting a default value, but indeed I didn’t understand how when worked in conjunction with the with_items.

Here’s however something else related that’s happening and I’m not understanding: why does this happens also if I move the task in a block and apply the when to that?

- block:

- name: install sw requested for this box
apt:
name: “{{item}}”
state: present
install_recommends: yes
with_items: “{{additional_sw}}”
when: additional_sw is defined

shouldn’t the task only be considered if the block is executed? why in this case i’m still getting the same error? and in fact, I also see the problem elsewhere with an include, which is even stranger:

#tasks/main.yml

- include: interfaces.yml
when: server_interfaces is defined
tags:
- netwrk

#tasks/interfaces.yml

- name: generate new interfaces file
template:
src: interfaces.j2
dest: /etc/network/interfaces
owner: root
group: adm
mode: 0640
notify:
- restart network

- name: setup rt_tables
lineinfile:
dest: /etc/iproute2/rt_tables
regexp: "^{{item.id}} "
line: “{{item.id}} {{item.tname}}”
with_items: “{{server_rt_tables}}”
when: server_rt_tables is defined

If I don’t define server_interfaces I would expect the interfaces.yml to be skipped. Is that not correct? Instead what I’m seeing is the “generate new interfaces” task being skipped while the setup rt_tables throws the same error. It seems like the tasks are “interpreted” even tho they are supposed to be skipped, which doesn’t make sense to me.

any thoughts?

thanks,

Spike

thanks for the explanation Kai, makes sense, it effectively does the same
thing I've done by setting a default value, but indeed I didn't understand
how when worked in conjunction with the with_items.

Here's however something else related that's happening and I'm not
understanding: why does this happens also if I move the task in a block and
apply the when to that?

You should really read the documentation, it's documented.
https://docs.ansible.com/ansible/playbooks_blocks.html

* - block:*
* - name: install sw requested for this box*
* apt:*
* name: "{{item}}"*
* state: present*
* install_recommends: yes*
* with_items: "{{additional_sw}}"*
* when: additional_sw is defined*

shouldn't the task only be considered if the block is executed? why in this
case i'm still getting the same error? and in fact, I also see the problem
elsewhere with an include, which is even stranger:

All the tasks in a block inherit the when, so it's the same as adding a when on all the task.

*#tasks/main.yml*
* - include: interfaces.yml*
* when: server_interfaces is defined*
* tags:*
* - netwrk*

*#tasks/interfaces.yml*

* - name: generate new interfaces file*
* template:*
* src: interfaces.j2*
* dest: /etc/network/interfaces*
* owner: root*
* group: adm*
* mode: 0640*
* notify:*
* - restart network*

* - name: setup rt_tables*
* lineinfile:*
* dest: /etc/iproute2/rt_tables*
* regexp: "^{{item.id <http://item.id>}} "*
* line: "{{item.id <http://item.id>}} {{item.tname}}"*
* with_items: "{{server_rt_tables}}"*
* when: server_rt_tables is defined*

If I don't define server_interfaces I would expect the interfaces.yml to be
skipped. Is that not correct? Instead what I'm seeing is the "generate new
interfaces" task being skipped while the setup rt_tables throws the same
error. It seems like the tasks are "interpreted" even tho they are supposed
to be skipped, which doesn't make sense to me.

You should read the entire page that I linked to in my previous mail
https://docs.ansible.com/ansible/playbooks_conditionals.html

All the task in the include inherits the when.
So all task is executed with the when applied, it's same as manually adding when to every single task.

any thoughts?

When alway get applied to the tasks, not the include or the block.
And the Ansible documentation is pretty good, I do recommend reading it, you will pick up many useful things :slight_smile:

Kai,

wow, school’ed :). I read the docs way back when I picked up when and blocks for the first time and used them for months without a problem thinking I understood what was going on. So when these issues arose I didn’t think of checking again. Obviously I had misunderstood it the first time around. Thank you again for the clarifications and reminders.

Spike

wow, school'ed :).

Sorry about that :slight_smile:

I read the docs way back when I picked up *when* and
*blocks* for the first time and used them for months without a problem
thinking I understood what was going on. So when these issues arose I
didn't think of checking again. Obviously I had misunderstood it the first
time around. Thank you again for the clarifications and reminders.

I forgot that in 2.0 and 2.1 this gave a deprecation warning with a message that this feature will be removed in future releases and that it will be a fatal error.
So you're right, it did work until 2.2.