Search code examples
ansibleyamljinja2ansible-template

How can I replace a value of dictionary based on a key condition in values.yaml via Ansible playbook?


I have a values.yaml file and I want to update the particular value of a dictionary based on its key.

For Example

input.yaml

env:
- name: "namespace1"
  value: "abc"
- name: "namespace2"
  value: "ijk"

Expected Result to be updated in the same input.yaml

env:
- name: "namespace1"
  value: "xyz"
- name: "namespace2"
  value: "ijk"

only if the key is namespace1, its value should be as xyz from abc

Updated input.yaml

operator:
  operator:
    replicaCount: 1
    env:
      - name: namespace1
        value: abc
    resources:
      requests:
        cpu: 200m
        memory: 256Mi
        ephemeral-storage: 500Mi
  console:
    replicaCount: 1

Solution

  • Here is a self explanatory playbook with intermediate step debugging

    Example file structure:

    $ tree
    .
    ├── input.yml
    └── test.yml
    
    0 directories, 2 files
    

    input.yaml before running playbook

    env:
    -   name: namespace1
        value: abc
    -   name: namespace2
        value: ijk
    

    test.yml playbook:

    ---
    - name: Make sure var file contains what we expect
      hosts: localhost
      gather_facts: false
    
      vars:
        inputf: input.yml
        env_as_dict: "{{ env | items2dict(key_name='name') }}"
        env_as_dict_transformed: "{{ env_as_dict | combine({'namespace1': 'xyz'}) }}"
        env_as_transformed_list: "{{ env_as_dict_transformed | dict2items(key_name='name') }}"
    
      vars_files:
        - "{{ inputf }}"
    
      tasks:
        - name: Convert original env var from a list of key/value pairs to a dict
          ansible.builtin.debug:
            var: env_as_dict
    
        - name: Combine resulting dict with expected value
          ansible.builtin.debug:
            var: env_as_dict_transformed
    
        - name: Convert the transformed dict back to a list
          ansible.builtin.debug:
            var: env_as_transformed_list
    
        - name: Write the content back to original file
          # Note: this works because we target localhost only
          # `delegate_to: localhost` and `run_once: true`
          # for a play targeting multiple non local hosts.
          vars:
            fcontent:
              env: "{{ env_as_transformed_list }}"
          ansible.builtin.copy:
            dest: "{{ inputf }}"
            content: "{{ fcontent | to_nice_yaml }}"
    

    which gives as a result

    $ ansible-playbook test.yml 
    
    PLAY [Make sure var file contains what we expect] ***********************************************************************************
    
    TASK [Convert original env var from a list of key/value pairs to a dict] ************************************************************
    ok: [localhost] => {
        "env_as_dict": {
            "namespace1": "abc",
            "namespace2": "ijk"
        }
    }
    
    TASK [Combine resulting dict with expected value] ***********************************************************************************
    ok: [localhost] => {
        "env_as_dict_transformed": {
            "namespace1": "xyz",
            "namespace2": "ijk"
        }
    }
    
    TASK [Convert the transformed dict back to a list] **********************************************************************************
    ok: [localhost] => {
        "env_as_transformed_list": [
            {
                "name": "namespace1",
                "value": "xyz"
            },
            {
                "name": "namespace2",
                "value": "ijk"
            }
        ]
    }
    
    TASK [Write the content back to original file] **************************************************************************************
    changed: [localhost]
    
    PLAY RECAP **************************************************************************************************************************
    localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    

    And the content of input.yml after running the playbook is

    env:
    -   name: namespace1
        value: xyz
    -   name: namespace2
        value: ijk
    

    Note that running the playbook again will report OK on the last task as the content of the file will already be aligned with what is expected.