ansibleansible-2.x

How to loop over this dictionary in Ansible?


Say I have this dictionary

war_files:
  server1:
  - file1.war
  - file2.war
  server2:
  - file1.war
  - file2.war
  - file3.war

and for now I just want to loop over each item (key), and then over each item in the key (value). I did this

- name: Loop over the dictionary
  debug: msg="Key={{ item.key }} value={{ item.value }}"
  with_dict: "{{ war_files }}"

And I get this. It is of course correct, but is NOT what I want.

ok: [localhost] => (item={'value': [u'file1.war', u'file2.war'], 'key': u'server1'}) => {
    "item": {
        "key": "server1", 
        "value": [
            "file1.war", 
            "file2.war"
        ]
    }, 
    "msg": "Server=server1, WAR=[u'file1.war', u'file2.war']"
}
ok: [localhost] => (item={'value': [u'file1.war', u'file2.war', u'file3.war'], 'key': u'server2'}) => {
    "item": {
        "key": "server2", 
        "value": [
            "file1.war", 
            "file2.war", 
            "file3.war"
        ]
    }, 
    "msg": "Server=server2, WAR=[u'file1.war', u'file2.war', u'file3.war']"
}

I want to get an output that says

"msg": "Server=server1, WAR=file1.war"
"msg": "Server=server1, WAR=file2.war"
"msg": "Server=server2, WAR=file1.war"
"msg": "Server=server2, WAR=file2.war"
"msg": "Server=server2, WAR=file3.war"

IOW, how can I write a task to iterates over the dictionary so it goes through each key, and then the items within each key? In essence, I have a nested array and want to iterate over it?


Solution


  • EDIT: At the time of writing this answer, Ansible 2.6 wasn't out. Please read the answer provided by @tmoschou, as it is much better.


    Well, I couldn't find a very easy way to do it, however, with a little bit of jinja2, we can achieve something of this sort:

    /tmp ❯❯❯ cat example.yml
    ---
    - hosts: 127.0.0.1
      vars:
        war_files:
          server1:
          - file1.war
          - file2.war
          server2:
          - file1.war
          - file2.war
          - file3.war
      tasks:
      - set_fact:
          war_files_list_of_dicts: |
              {% set res = [] -%}
              {% for key in war_files.keys() -%}
                 {% for value in war_files[key] -%}
                  {% set ignored = res.extend([{'Server': key, 'WAR':value}]) -%}
                 {%- endfor %}
              {%- endfor %}
              {{ res }}
    
      - name: let's debug the crap out of this
        debug: var=war_files_list_of_dicts
    
      - name: Servers and their WARs!!!
        debug:
           msg: "Server={{ item.Server }}, WAR={{ item.WAR }}"
        with_items: "{{ war_files_list_of_dicts }}"
    

    And, when the playbook is run:

    /tmp ❯❯❯ ansible-playbook example.yml
     [WARNING]: provided hosts list is empty, only localhost is available
    
    
    PLAY [127.0.0.1] ***************************************************************
    
    TASK [setup] *******************************************************************
    ok: [127.0.0.1]
    
    TASK [set_fact] ****************************************************************
    ok: [127.0.0.1]
    
    TASK [let's debug the crap out of this] ****************************************
    ok: [127.0.0.1] => {
        "war_files_list_of_dicts": [
            {
                "Server": "server1", 
                "WAR": "file1.war"
            }, 
            {
                "Server": "server1", 
                "WAR": "file2.war"
            }, 
            {
                "Server": "server2", 
                "WAR": "file1.war"
            }, 
            {
                "Server": "server2", 
                "WAR": "file2.war"
            }, 
            {
                "Server": "server2", 
                "WAR": "file3.war"
            }
        ]
    }
    
    TASK [Servers and their WARs!!!] ***********************************************
    ok: [127.0.0.1] => (item={'WAR': u'file1.war', 'Server': u'server1'}) => {
        "item": {
            "Server": "server1", 
            "WAR": "file1.war"
        }, 
        "msg": "Server=server1, WAR=file1.war"
    }
    ok: [127.0.0.1] => (item={'WAR': u'file2.war', 'Server': u'server1'}) => {
        "item": {
            "Server": "server1", 
            "WAR": "file2.war"
        }, 
        "msg": "Server=server1, WAR=file2.war"
    }
    ok: [127.0.0.1] => (item={'WAR': u'file1.war', 'Server': u'server2'}) => {
        "item": {
            "Server": "server2", 
            "WAR": "file1.war"
        }, 
        "msg": "Server=server2, WAR=file1.war"
    }
    ok: [127.0.0.1] => (item={'WAR': u'file2.war', 'Server': u'server2'}) => {
        "item": {
            "Server": "server2", 
            "WAR": "file2.war"
        }, 
        "msg": "Server=server2, WAR=file2.war"
    }
    ok: [127.0.0.1] => (item={'WAR': u'file3.war', 'Server': u'server2'}) => {
        "item": {
            "Server": "server2", 
            "WAR": "file3.war"
        }, 
        "msg": "Server=server2, WAR=file3.war"
    }
    
    PLAY RECAP *********************************************************************
    127.0.0.1                  : ok=4    changed=0    unreachable=0    failed=0