'Ansible change all dict key values only by filters

I have an ansible dict like

ports:
  webui: 7200
  webadmin: 7209
  core_api: 7201
  stock_api: 7204
  import_export: 7207

And i want to transform all keys in it, like

ports:
  staging-webui: 7200
  staging-webadmin: 7209
  staging-core_api: 7201
  staging-stock_api: 7204
  staging-import_export: 7207

I do that in 'vars' section, so i can't use 'set_fact' with 'with_items' to iterate over dict. Is it possible to it only by filters?

I found working answer,

env: staging # comes from outside

regex_env: "\"key\": \"{{ env }}-\\1\""
app_upstreams: "{{ ports | dict2items | map('to_json') | map('regex_replace', '\"key\":\\s\"(.*)\"', lookup('vars', 'regex_env')) | map('from_json') }}"

but it looks really ugly, is there more good-looking solution?



Solution 1:[1]

For example, the playbook below

- hosts: localhost
  vars:
    env: staging_
    ports:
      webui: 7200
      webadmin: 7209
      core_api: 7201
      stock_api: 7204
      import_export: 7207
    env_ports_keys: "{{ [env]|product(ports.keys()|list)|map('join') }}"
    env_ports_vals: "{{ ports.values()|list }}"
    env_ports: "{{ dict(env_ports_keys|zip(env_ports_vals)) }}"
  tasks:
    - debug:
        var: env_ports

gives (abridged)

  env_ports:
    staging_core_api: 7201
    staging_import_export: 7207
    staging_stock_api: 7204
    staging_webadmin: 7209
    staging_webui: 7200

To simplify the code, you can create a custom filter

shell> cat filter_plugins/dict_utils.py
def prefix_key(d, prefix='prefix'):
    temp = {}
    for key in d.keys():
        temp[prefix + key] = d[key]
    return temp


class FilterModule(object):
    ''' utility filters for operating on dictionary '''
    def filters(self):
        return {
            'prefix_key': prefix_key,
        }

The playbook below gives the same result

- hosts: localhost
  vars:
    env: staging_
    ports:
      webui: 7200
      webadmin: 7209
      core_api: 7201
      stock_api: 7204
      import_export: 7207
    env_ports: "{{ ports|prefix_key(prefix=env) }}"
  tasks:
    - debug:
        var: env_ports

Solution 2:[2]

I was just trying to solve this issue for myself. I came up with a solution that does not require the use of a filter plugin or a jinja template. Just pure ansible playbook tasks. I did this on the newest version of Ansible, so I can not confirm it will work on older versions.

It works by creating a second dictionary that has the renamed keys and then overwriting the original dictionary with the renamed one. Here is a complete playbook you can run as a proof of concept.

---
- hosts: localhost
  connection: local
  vars:
    prefix: staging
    ports:
      webui: 7200
      webadmin: 7209
      core_api: 7201
      stock_api: 7204
      import_export: 7207
    renamed_ports: {}

  tasks:
  - name: set renamed ports
    ansible.builtin.set_fact:
      renamed_ports: "{{ renamed_ports | combine({ prefix + '-' + item.key: item.value }) }}"
    with_dict: "{{ ports }}"

  - name: rewrite original ports
    ansible.builtin.set_fact:
      ports: "{{ renamed_ports }}"

  - name: erase renamed ports if you want to
    ansible.builtin.set_fact:
      renamed_ports:

  - name: print ports
    ansible.builtin.debug:
      var: ports
...

Here is the output of the print ports task:

TASK [print ports] ****************************************************************************
ok: [localhost] => {
    "ports": {
        "staging-core_api": 7201,
        "staging-import_export": 7207,
        "staging-stock_api": 7204,
        "staging-webadmin": 7209,
        "staging-webui": 7200
    }
}

Solution 3:[3]

You can achieve this using jinja2 template module and create a yml file with all the variables.

playbook -->

---
- hosts: localhost
  vars:
    ports:
      webui: 7200
      webadmin: 7209
      core_api: 7201
      stock_api: 7204
      import_export: 7207
  tasks:
    - name: debug module
      debug:
        var: "{{ item }}"
      with_items:
        - ports

    - name: template
      template:
        src: templates/template.yml
        dest: templates/dest.yml

jinja2 template -->

ports:
{% for key, value in ports.iteritems() %}
  staging-{{ key }}: {{ value }}
{% endfor %}

output -->

ports:
  staging-core_api: 7201
  staging-import_export: 7207
  staging-stock_api: 7204
  staging-webui: 7200
  staging-webadmin: 7209

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Apreche
Solution 3 error404