27
votes

Ansible 1.9.4.

The script should execute some task only on hosts where some variable is defined. It works fine normally, but it doesn't work with the with_items statement.

- debug: var=symlinks
  when: symlinks is defined

- name: Create other symlinks
  file: src={{ item.src }} dest={{ item.dest }} state=link
  with_items: "{{ symlinks }}"
  when: symlinks is defined

But I get:

TASK: [app/symlinks | debug var=symlinks] *********************
skipping: [another-host-yet]

TASK: [app/symlinks | Create other symlinks] ******************
fatal: [another-host-yet] => with_items expects a list or a set

Maybe I am doing something wrong?

2
What is the value of symlinks? - helloV
The problem is that the same host, this variable is not defined - user5943216

2 Answers

36
votes
with_items: "{{ symlinks | default([]) }}"
31
votes

The reason for this behavior is conditions work differently inside loops. If a loop was defined the condition is evaluated for every item while iterating over the items. But the loop itself requires a valid list.

This is also mentioned in the docs:

Note that when combining when with with_items (see Loops), be aware that the when statement is processed separately for each item. This is by design:

tasks:
  - command: echo {{ item }}
    with_items: [ 0, 2, 4, 6, 8, 10 ]
    when: item > 5

I think this is a bad design choice and for this functionality they better should have introduced something like with_when.

As you have already figured out yourself, you can default to an empty list.

with_items: "{{ symlinks  | default([]) }}"

Finally if the list is dynamically loaded from a var, say x, use:

with_items: "{{ symlinks[x|default('')] | default([])}}" 

This will default to an empty list when 'x' is undefined

Accordingly, fall back to an empty dict with default({}):

# service_facts skips, then dict2items fails?
with_dict: "{{ ansible_facts.services|default({})|dict2items|selectattr('key', 'match', '[^@]+@.+\\.service')|list|items2dict }}"