Reputation: 630
Using an Ansible task, I'm trying to create variables and associated values from a returned loop object.
Ref: https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html
- name: Get values
someModule:
input_from_loop: "{{ item }}"
loop:
- "foo"
- "bar"
register: get_result
Gives
get_result:
changed: false
msg: All items completed
results:
- ansible_loop_var: item
item: foo
alpha:
beta:
content: someFooValue
- ansible_loop_var: item
item: bar
alpha:
beta:
content: someBarValue
With this get_result
object, I'm trying to create variables and associated values such that:
Pseudocode:
- name: Assign values
set_fact:
"{{ item.item }}":"{{ item.alpha.beta.content }}"
loop: get_result.results
To result in:
foo:someFooValue
bar:someBarValue
I can't seem to get around the error
Implicit map keys need to be followed by map values"
I do not want to create a new object, just variables with values for later use (in existing tasks).
I've tried a few approaches to no avail.
[Or can it happen on each iteration of the initial loop calling the module?]
Upvotes: 3
Views: 3249
Reputation: 68044
For example
_query: '[].{key: item, value: alpha.beta.content}'
result: "{{ get_result.results|json_query(_query)|items2dict }}"
gives the expected result
result:
bar: someBarValue
foo: someFooValue
Example of a complete playbook (for testing)
- hosts: localhost
vars:
get_result:
changed: false
msg: All items completed
results:
- ansible_loop_var: item
item: foo
alpha:
beta:
content: someFooValue
- ansible_loop_var: item
item: bar
alpha:
beta:
content: someBarValue
_query: '[].{key: item, value: alpha.beta.content}'
result: "{{ get_result.results|json_query(_query)|items2dict }}"
tasks:
- debug:
var: result
Upvotes: 1
Reputation: 12070
Regarding the question in the headline
How to assign values to variables using a loop (set_fact)?
and the mentioned "pseudocode" I like to note that such approach might work, as the following test
---
- hosts: localhost
become: false
gather_facts: false
vars:
varKey: "TEST"
varVal: "testValue"
tasks:
- name: Create variable dynamic
set_fact:
"{{ varKey }}_{{ item }}": "{{ varVal }}_{{ item }}"
loop: [1, 2, 3]
- name: Show variable
debug:
var: TEST_{{ item }}
loop: ["1", "2", "3"]
results into an output of
TASK [Create variable dynamic] *********
ok: [localhost] => (item=1)
ok: [localhost] => (item=2)
ok: [localhost] => (item=3)
TASK [Show variable] *********
ok: [localhost] => (item=1) =>
TEST_1: testValue_1
ansible_loop_var: item
item: '1'
ok: [localhost] => (item=2) =>
TEST_2: testValue_2
ansible_loop_var: item
item: '2'
ok: [localhost] => (item=3) =>
TEST_3: testValue_3
ansible_loop_var: item
item: '3'
and also
- name: Create variable dynamic
set_fact:
"{{ item }}": "{{ item }}"
loop: [A1, A2, A3]
- name: Show variable
debug:
var: "{{ item }}"
loop: [A1, A2, A3]
into
TASK [Create variable dynamic] **********
ok: [localhost] => (item=A1)
ok: [localhost] => (item=A2)
ok: [localhost] => (item=A3)
TASK [Show variable] **********
ok: [localhost] => (item=A1) =>
A1: A1
ansible_loop_var: item
item: A1
ok: [localhost] => (item=A2) =>
A2: A2
ansible_loop_var: item
item: A2
ok: [localhost] => (item=A3) =>
A3: A3
ansible_loop_var: item
item: A3
Upvotes: 1
Reputation: 39149
Now that I am rereading your your requirements, I am wondering if you are not looking for variables at the top level of your host that would be named
foo
and contain someFooValue
bar
and contain someBarValue
If this is the case, then most of that is above does still apply, the registering of the fact only change:
- set_fact: { "{{ item.0 }}": "{{ item.1 }}" }
loop: "{{
get_result.results | map(attribute='item') | zip(
get_result.results | map(attribute='alpha.beta.content')
)
}}"
Which gives you the two expected variables with the corresponding values.
ok: [localhost] =>
foo: someFooValue
ok: [localhost] =>
bar: someBarValue
What you can do in those kind of cases is to break down the dictionary in multiple lists, all containing one of the field you are interested into, with the map
filter, then reconstruct a list of list with the help of the zip
filter. And finally, join everything together.
So, with a task like:
- set_fact:
formatted_fact: "{{
get_result.results | map(attribute='item') | zip(
get_result.results | map(attribute='alpha.beta.content')
) | map('join', ':') | join('\n')
}}"
You get your expected output:
formatted_fact: |-
foo:someFooValue
bar:someBarValue
Given those couple of tasks, we have the two possibilities:
- set_fact:
formatted_fact: "{{
get_result.results | map(attribute='item') | zip(
get_result.results | map(attribute='alpha.beta.content')
) | map('join', ':') | join('\n')
}}"
vars:
get_result:
changed: false
msg: All items completed
results:
- ansible_loop_var: item
item: foo
alpha:
beta:
content: someFooValue
- ansible_loop_var: item
item: bar
alpha:
beta:
content: someBarValue
- debug:
var: formatted_fact
- set_fact: { "{{ item.0 }}": "{{ item.1 }}" }
loop: "{{
get_result.results | map(attribute='item') | zip(
get_result.results | map(attribute='alpha.beta.content')
)
}}"
vars:
get_result:
changed: false
msg: All items completed
results:
- ansible_loop_var: item
item: foo
alpha:
beta:
content: someFooValue
- ansible_loop_var: item
item: bar
alpha:
beta:
content: someBarValue
- debug:
var: foo
- debug:
var: bar
They would yield:
TASK [set_fact] **************************************************************
ok: [localhost]
TASK [debug] *****************************************************************
ok: [localhost] =>
formatted_fact: |-
foo:someFooValue
bar:someBarValue
TASK [set_fact] **************************************************************
ok: [localhost] => (item=['foo', 'someFooValue'])
ok: [localhost] => (item=['bar', 'someBarValue'])
TASK [debug] *****************************************************************
ok: [localhost] =>
foo: someFooValue
TASK [debug] *****************************************************************
ok: [localhost] =>
bar: someBarValue
Upvotes: 1