Search code examples
listdictionaryansibleansible-inventory

How to add items to an existing dictionary in Ansible?


I have a dictionary which is loaded by the following play:

- name: Get variables from ../main.yml and save them into dict
  include_vars:
    file: "../main.yml"
    name: dict

"dict" contains the following:

"dict": {
"environments": {
    "MYENV": {
        "key1": "value1",
        "key2": "value2"
    },
    "MYENV2": {
        "key1": "value1",
        "key2": "value2"
    },
    "MYENV3": {
        "key1": "value1",
        "key2": "value2"
    }
}}

Question: How can I loop through this dictionary in Ansible and add a 3rd key "key3" with accompanied value to each entry in "environments"?

The desired situation would be a new_dict which contains the following:

"new_dict": {
"environments": {
    "MYENV": {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3"
    },
    "MYENV2": {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3"
    },
    "MYENV3": {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3"
    }
}}

With "value3" being a string built like "MYENV" + "value1" + "value2".


Solution

  • The decomposition of the directory is needed. Then {key3: value3} is added and the directory is combined again. The tasks below

      vars:
        add_this:
          key3: value3
      tasks:
        - set_fact: # collect dictionary keys
            keys: "{{ dict.environments|
                      dict2items|
                      json_query('[].key') }}"
        - set_fact: # collect dictionary values and add item
            values: "{{ dict.environments|
                        dict2items|
                        json_query('[].value')|
                        map('combine', add_this)|list }}"
        - set_fact: # create dict environments
            environments: "{{ environments|
                              default({})|
                              combine({item.0: item.1}) }}"
          loop: "{{ keys|zip(values)|list }}"
        - set_fact: # create dictionary new_dict
            new_dict: "{{ new_dict|
                          default({})|
                          combine({'environments': environments}) }}"
        - debug:
            var: new_dict
    

    give

    "new_dict": {
        "environments": {
            "MYENV": {
                "key1": "value1", 
                "key2": "value2", 
                "key3": "value3"
            }, 
            "MYENV2": {
                "key1": "value1", 
                "key2": "value2", 
                "key3": "value3"
            }, 
            "MYENV3": {
                "key1": "value1", 
                "key2": "value2", 
                "key3": "value3"
            }
        }
    }
    

    Given the fact that all values are different, how can I dynamically create "value3" for each of the environments by e.g. appending the values of "key1" and "key2"?

    With a couple of filters

    $ cat filter_plugins/filter1.py
    def custom_1(h):
        return {'key3': h.values()}
    
    def dict_merge(x, y, recursive=False):
        if recursive:
            z = dict(list(x.items()) + list(y.items()))
        else:
            z = x.copy()
            z.update(y)
        return z
    
    def dict_keys(d):
        return list(d)
    
    
    class FilterModule(object):
    
        def filters(self):
            return {
                'custom_1' : custom_1,
                'dict_keys' : dict_keys,
                'dict_merge' : dict_merge
            }
    

    the tasks below

      tasks:
        - set_fact: 
            env: "{{ env|default({})|
                     combine({item: dict.environments[item]|
                                    dict_merge((dict.environments[item]|custom_1), True)
                                    }) }}"
          loop: "{{ dict.environments|dict_keys }}"
        - set_fact:
            new_dict: "{{ {}|combine({'environments': env}) }}"
        - debug:
            var: new_dict
    

    give

    "new_dict": {
        "environments": {
            "MYENV": {
                "key1": "value1", 
                "key2": "value2", 
                "key3": [
                    "value2", 
                    "value1"
                ]
            }, 
            "MYENV2": {
                "key1": "value1", 
                "key2": "value2", 
                "key3": [
                    "value2", 
                    "value1"
                ]
            }, 
            "MYENV3": {
                "key1": "value1", 
                "key2": "value2", 
                "key3": [
                    "value2", 
                    "value1"
                ]
            }
        }
    }
    

    Fit the custom_1 filter to your needs.