Mark
Mark

Reputation: 630

Assign values to variables using a loop (set_fact or other?)

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

Answers (3)

Vladimir Botka
Vladimir Botka

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

U880D
U880D

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

Related Questions