Search code examples
ansiblejinja2ansible-2.xansible-template

Replace the key value in a list with the relevant matching values from another list in Ansible


I am trying to replace the key values in a dict inside a list(here only for the values inside Objectids) with the matching values from another list. Somehow i am only able to replace the 1st value but not iterating the whole list. Here the same key in the input.json will have multiple matching values from the finallist.json and all those values needs to be matched and added to the get the final expected output.

Input.json

[
    {
        "name": "DNS_One",
        "objectIds": [
            "DNS_One",
            "DNS_One-HO",
            "NTP"
        ]
    },
    {
        "name": "DNS_Two",
        "objectIds": [
            "NTP"
        ]
    }
]

finallist.json

[
    {
        "id": "123456",
        "net_name": "DNS_One"
    },
    {
        "id": "789101112",
        "net_name": "DNS_One"
    },
    {
        "id": "131415161718",
        "net_name": "DNS_One-HO"
    },
    {
        "id": "23897845",
        "net_name": "NTP"
    },
    {
        "id": "9879879546",
        "net_name": "NTP"
    }
]

Playbook

- name: Id mapping
      vars: 
       objectid: >-
        {{ 
          finallist
          | selectattr('net_name', 'in', item.1)
          | map(attribute = 'id')
          | first
          | default([])
          }}
      set_fact:
        obj: >-
         {{
            obj | default([]) + 
            [item.0 | combine({'objectIds': [objectid]})]
         }}
      with_subelements:
           - "{{ input }}"
           - objectIds
      ignore_errors: yes

Expected Output

[
    {
        "name": "DNS_One",
        "objectIds": [
            "123456","789101112"
            "131415161718",
            "23897845","9879879546"
        ]
    },
    {
        "name": "DNS_Two",
        "objectIds": [
            "23897845","9879879546"
        ]
    }
]

Solution

  • Read the data and create the variables finallist and input

        - set_fact:
            finallist: "{{ lookup('file', 'finallist.json')|from_yaml }}"
        - set_fact:
            input: "{{ lookup('file', 'input.json')|from_yaml }}"
    

    gives

    finallist:
      - id: '123456'
        net_name: DNS_One
      - id: '789101112'
        net_name: DNS_One
      - id: '131415161718'
        net_name: DNS_One-HO
      - id: '23897845'
        net_name: NTP
      - id: '9879879546'
        net_name: NTP
    
    input:
      - name: DNS_One
        objectIds:
        - DNS_One
        - DNS_One-HO
        - NTP
      - name: DNS_Two
        objectIds:
        - NTP
    

    In finallist, group the items by net_name and convert the list to a dictionary

        - set_fact:
            finaldict: "{{ finaldict|d({})|
                           combine({item.0: item.1|map(attribute='id')}) }}"
          loop: "{{ finallist|groupby('net_name') }}"
    

    gives

      finaldict:
        DNS_One:
        - '123456'
        - '789101112'
        DNS_One-HO:
        - '131415161718'
        NTP:
        - '23897845'
        - '9879879546'
    

    Iterate input and substitute the IDs

        - set_fact:
            new: "{{ new|d([]) + [item|combine({'objectIds': _ids})] }}"
          loop: "{{ input }}"
          vars:
            _ids: "{{ item.objectIds|map('extract', finaldict)|list }}"
    

    gives the expected result

      new:
        - name: DNS_One
          objectIds:
          - ['123456', '789101112']
          - ['131415161718']
          - ['23897845', '9879879546']
        - name: DNS_Two
          objectIds:
          - ['23897845', '9879879546']
    

    You can flatten the lists if you want to

           _ids: "{{ item.objectIds|map('extract', finaldict)|flatten }}"
    

    gives

      new:
        - name: DNS_One
          objectIds: ['123456', '789101112', '131415161718', '23897845', '9879879546']
        - name: DNS_Two
          objectIds: ['23897845', '9879879546']