1

I got this simple playbook where I am trying to construct a dictionary from a list of key/value pairs using the combine filter. The problem is that it does not seem to work when looping over the pairs (I've tried loop, with_dict, with_items).

- name: test jinja2 combine filter
  hosts: localhost    
    - name: test combine
      vars:
        x: {'three', 3}
      set_fact:
        x: "{{ x | combine(item) }}"
      with_items: [{'one': 1},{'two': 2}]

    # I am expecting to see the two new dicts here,
    # but only the last one in the list is added
    - name: print x
      debug: msg={{ x }}

Expected output:

ok: [localhost] => {
    "msg": {
        "three": 3,
        "one": 1,
        "two": 2
    }
}

My result:

ok: [localhost] => {
    "msg": {
        "three": 3, 
        "two": 2
    }
}

From this post, it seems that there's no out of the box solution for this kind of problem. While it wouldn't be hard to write a custom plugin, I am still wondering if there is a standard solution without writing a plugin.

lainatnavi
  • 1,453
  • 1
  • 14
  • 22
  • I wanted to say thank you for putting together such a well-formed question; it truly is a great MCVE and I appreciate you boiling down your problem to such a clear example – mdaniel Mar 08 '20 at 02:19

3 Answers3

1

Hope you already found a working solution... If not, this can help you:

- name: test jinja2 combine filter
  hosts: localhost   
  vars:
    x: 
      three: 3
    xx: 
      one: 1
      two: 2

  tasks: 
    - debug: 
        var: x
    - debug: 
        var: xx
    - name: test combine
      set_fact:
        z: "{{ x | combine(xx) | to_nice_json }}"
    - debug: 
        var: z

This gives me:

PLAY [test jinja2 combine filter] ***************************

TASK [debug] ***************************
ok: [localhost] => {
    "x": {
        "three": 3
    }
}

TASK [debug] ***************************
ok: [localhost] => {
    "xx": {
        "one": 1,
        "two": 2
    }
}

TASK [test combine] ***************************
ok: [localhost]

TASK [debug] ***************************
ok: [localhost] => {
    "z": {
        "one": 1,
        "three": 3,
        "two": 2
    }
}

PLAY RECAP ***************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
Darko
  • 11
  • 1
0

try this out ansible-playbook -c local <file.yaml

- hosts: localhost
  tasks:
    - name: test combine
      vars:
        x: {'three': 3}
      set_fact:
        x: "{{ x | combine(item) }}"
      with_items: [{'one': 1},{'two': 2}]

    # I am expecting to see the two hashes here...
    # but only the last one in the list is added
    - name: print x
      debug: msg={{ x }}
Rajesh Rajendran
  • 587
  • 5
  • 18
  • Thanks, but how is this supposed to work if it's the same as my post? Did you try it? My ansible version is ansible 2.5.1 on Ubuntu 18.04.4 LTS. – lainatnavi Mar 07 '20 at 12:06
0

So, the bad news is that I can't replicate your experience locally with ansible 2.9 (so it's possible you are experiencing behavior they decided was a bug and fixed), but my hypothesis is that the with_items: behavior is re-executing the vars: statement as a side effect of the loop, thus resetting x through every loop, equivalent to: with_items({vars, set_fact}, [{item},{item}]) instead of {vars, with_items(set_fact, ...)}

Having said that, unless your actual situation prevents it, what you really want is not to iterate the set_fact but to run set_fact on the combined items:

- set_fact:
    x: '{{ x | combine(*new_values) }}'
  vars:
    x: {"three": 3}
    new_values: [{"one": 1}, {"two": 2}]

- debug: var=x
mdaniel
  • 31,240
  • 5
  • 55
  • 58
  • I need to get the new values iterating on a dict. If it is possible, could you pleas provide a full working example? – lainatnavi Mar 08 '20 at 13:10
  • The issue is of course that set_fact resets x to the vars: declaration. How to overcome this? – lainatnavi Mar 08 '20 at 13:14
  • That's wasn't my experience, but I freely admit that I didn't install an ancient version of ansible just to test this question. You can side step that discussion entirely by not using the same name for the output variable as the seed variable. If you are experiencing this fix didn't work for you, then update your question with the experienced results and we'll go from there – mdaniel Mar 08 '20 at 17:08
  • I don't think the version of ansible is the problem here. Does my example work in your ansbible 2.9 installation? – lainatnavi Mar 08 '20 at 18:01
  • I hear you, and yet I wasn't able to reproduce your original problem in ansible 2.9 (the `debug: var=x` at the end contained all 3 keys for me), so I put together what I thought was a safer pattern. When you try my example, does it also not behave correctly for you? That's why I included "update your question with your experience and we'll go from there" -- I can appreciate you might be having a different experience than I am, but I'm not sitting at your desk to see it – mdaniel Mar 10 '20 at 16:05
  • The point here is not combining the values which of course works also in my case if I run your example. The point is that `[{"one": 1}, {"two": 2}]` is dynamic, I don't have a fixed value of the dict, thus I need to iterate over it in order to build the result dict. Please check my updated answer, I added some more info. – lainatnavi Mar 10 '20 at 19:04