dsollen
dsollen

Reputation: 6459

how to modify multiple values in deep dictionary in ansible

In my ansible I need to make a rest call to get a rather large json object (which comes in as a dict). Then I need to modify a few fields in the json and repost it to update the status of the components I want to change.

I already have the rest response and I'm trying to figure out how to cleanly modify numerous values. My current attempt looks like this (simplified a little):

- name: update locations for remote processor
  set_fact:
    request: "{{ response.json | combine(item)}}"
  loop:
    - { 'component': { 'targetUri': "{{remoteProcessorUri}}"
    - { 'component': { 'targetUris': "{{remoteProcessorUri}}"
    ...

Unfortunately while this does change the request, it replaces the larger component dict with an dict that only contains targetUri, all the other content that was in component was erased where as I want to keep it and only modify targetUri.

I tried a variant where my loop had a location and a value field for each item, but I can't get the location field syntax right to be able to replace response.json with item.location.

So how can I create this to make it easy, and readable, to make various changes across my dict without changing anything other then the specific subfields I call out?

Upvotes: 0

Views: 1247

Answers (2)

dsollen
dsollen

Reputation: 6459

Turns out I had it almost correct. All I needed was to add the recursive=true option to the combine filter. so...

- name: update locations for remote processor
  set_fact:
    request: "{{ response.json | combine(item, recursive=True)}}"
  loop:
    - { 'component': { 'targetUri': "{{remoteProcessorUri}}"
    - { 'component': { 'targetUris': "{{remoteProcessorUri}}"
    ...

Upvotes: 1

mdaniel
mdaniel

Reputation: 33158

Dictionaries are "live" in ansible/jinja2, in that you can modify them in-place using most of the python dict methods, with the only caveat that you have to echo the whole structure back out in a set_fact: to "save" the changes

  tasks:
  - set_fact:
      example1:
        {
          "some": {
            "key": {
              "ansible_python": {
                "executable": "/usr/local/opt/[email protected]/bin/python3.9",
                "has_sslcontext": true,
                "type": "cpython",
                "version": {
                  "major": 3,
                  "micro": 5,
                  "minor": 9,
                  "releaselevel": "final",
                  "serial": 0
                },
                "version_info": [
                  3,
                  9,
                  5,
                  "final",
                  0
                ]
              }
            }
          }
        }
  - set_fact:
      example1: >-
        {%- set _ = example1.some.key.ansible_python.version.update({
          "a_new_key": "yup",
          "mojor": 9999,
          }) -%}
        {{ example1 }}
  - debug:
      var: example1.some.key.ansible_python.version

produces

ok: [localhost] => {
    "example1.some.key.ansible_python.version": {
        "a_new_key": "yup",
        "major": 3,
        "micro": 5,
        "minor": 9,
        "mojor": 9999,
        "releaselevel": "final",
        "serial": 0
    }
}

That {% set _ = ... %} business is because the jinja2 versions bundled with ansible don't seem to support the do operator from jinja2, so the only way to get a method with side-effects is to have it be on the right side of an assignment operator, and just throw away the result

Upvotes: 0

Related Questions