Search code examples
ansiblejinja2

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?]


Solution

  • 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