Search code examples
ansiblejinja2

Flatten doubly-nested Ansible dict


How would I get from sessions to sessions_flat in a call to set_fact within Ansible?

sessions:
  - name: "clientALPHA"
    vpns:
      - name: "tun10"
        password: 56813252
        port: 1194
      - name: "tun20"
        password: 90871307
        port: 1195
  - name: "clientBRAVO"
    vpns:
      - name: "tun10"
        password: 76530913
        port: 1196


sessions_flat:
  - oid: "clientALPHA-tun10"
    password: 56813252
    port: 1194
  - oid: "clientALPHA-tun20"
    password: 90871307
    port: 1195
  - oid: "clientBRAVO-tun10"
    password: 76530913
    port: 1196

Solution

  • You could use a loop with the subelements filter, like this:

    - set_fact:
        sessions_flat: >-
          {{
            sessions_flat + [{
              "port": item.1.port,
              "password": item.1.password
            }|combine({"oid": "%s-%s" % (item.0.name, item.1.name)})]
          }}
      vars:
        sessions_flat: []
      loop: "{{ sessions|subelements('vpns')}}"
    

    If you're using the community edition of jmespath, you could use a let expression:

    - set_fact:
        sessions_flat: >-
          {{
          sessions | json_query('[].[let $top = @ in $top.vpns[]|[].{"oid": join(`"-"`, [$top.name, name]), "port": port, "password": password}][][]')
          }}
    

    Both of the above tasks result in:

    {
      "sessions_flat": [
          {
              "oid": "clientALPHA-tun10",
              "password": 56813252,
              "port": 1194
          },
          {
              "oid": "clientALPHA-tun20",
              "password": 90871307,
              "port": 1195
          },
          {
              "oid": "clientBRAVO-tun10",
              "password": 76530913,
              "port": 1196
          }
      ]
    }