Using set_facts and with_items together in Ansible
I'm currently using Ansible 1.7.2. I have the following test playbook:
---
- hosts: localhost
tasks:
- name: set fact 1
set_fact: foo="[ 'zero' ]"
- name: set fact 2
set_fact: foo="{{ foo }} + [ 'one' ]"
- name: set fact 3
set_fact: foo="{{ foo }} + [ 'two', 'three' ]"
- name: set fact 4
set_fact: foo="{{ foo }} + [ '{{ item }}' ]"
with_items:
- four
- five
- six
- debug: var=foo
The first task sets a fact that's a list with one item in it. The subsequent tasks append to that list with more values. The first three tasks work as expected, but the last one doesn't. Here's the output when I run this:
PLAY [localhost] **************************************************************
GATHERING FACTS ***************************************************************
ok: [localhost]
TASK: [set fact 1] ************************************************************
ok: [localhost]
TASK: [set fact 2] ************************************************************
ok: [localhost]
TASK: [set fact 3] ************************************************************
ok: [localhost]
TASK: [set fact 4] ************************************************************
ok: [localhost] => (item=four)
ok: [localhost] => (item=five)
ok: [localhost] => (item=six)
TASK: [debug var=foo] *********************************************************
ok: [localhost] => {
"foo": [
"zero",
"one",
"two",
"three",
"six"
]
}
PLAY RECAP ********************************************************************
localhost : ok=6 changed=0 unreachable=0 failed=0
Given the with_items in task 4 and the fact that the output shows the task properly iterated over the items in that list, I would have expected the result to contain all the numbers zero through six. But that last task seems to only be evaluating set_fact with the last item in the list. Is this possibly a bug in Ansible?
Edit: I also just tested this on ansible 1.8 and the output was identical.
Solution 1:
There is a workaround which may help. You may "register" results for each set_fact iteration and then map that results to list:
---
- hosts: localhost
tasks:
- name: set fact
set_fact: foo_item="{{ item }}"
with_items:
- four
- five
- six
register: foo_result
- name: make a list
set_fact: foo="{{ foo_result.results | map(attribute='ansible_facts.foo_item') | list }}"
- debug: var=foo
Output:
< TASK: debug var=foo >
---------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ok: [localhost] => {
"var": {
"foo": [
"four",
"five",
"six"
]
}
}
Solution 2:
As mentioned in other people's comments, the top solution given here was not working for me in Ansible 2.2, particularly when also using with_items
.
It appears that OP's intended approach does work now with a slight change to the quoting of item
.
- set_fact: something="{{ something + [ item ] }}"
with_items:
- one
- two
- three
And a longer example where I've handled the initial case of the list being undefined and added an optional when
because that was also causing me grief:
- set_fact: something="{{ something|default([]) + [ item ] }}"
with_items:
- one
- two
- three
when: item.name in allowed_things.item_list
Solution 3:
I was hunting around for an answer to this question. I found this helpful. The pattern wasn't apparent in the documentation for with_items.
https://github.com/ansible/ansible/issues/39389
- hosts: localhost
connection: local
gather_facts: no
tasks:
- name: set_fact
set_fact:
foo: "{{ foo }} + [ '{{ item }}' ]"
with_items:
- "one"
- "two"
- "three"
vars:
foo: []
- name: Print the var
debug:
var: foo