1

I had search in a few forums including this one, but none of them seems to be able to provide the solution for me. In the playbook, I am doing a call via uri module that registers a variable: result.

result.json looks like this

{
"global_api_concurrency_limit": 199,
"client_api_rate_limit": 100,
"client_api_concurrency_limit": 40,
"connection_timeout": 30,
"redirect_host": "",
"protocol_versions": [
{"enabled": true, "name": "TLSv1.1"},
{"enabled": true, "name": "TLSv1.2"}
]
}

I checked via result.json | type_debug and says its a dictionary.

I want to update property to "enabled": false where "name": "TLSv1.1".

Noting that false is not quoted in the JSON. So my idea is to either create a new dict or update the same dict. But I am stuck how I can update only that value.

I attempted the below tasks, but it obviously didn't work.

  - name: Call NSXT
    vars:
      api: "/api/v1/cluster/api-service"
    ansible.builtin.uri:
      url: "https://server01{{ api }}"
      user: user1
      password: pwd1
      method: GET
      force_basic_auth: true
      validate_certs: false
      headers:
        Content-Type: application/json
    register: result

  - set_fact:
      newdic: "{{ (newdic | d([])) | combine(new_item, recursive=True) }}"
    with_dict: "{{ result.json }}"
    vars:
      new_item: {'enabled': false}
    when: item.protocol_versions.name == 'TLSv1.1'

Any advise how to improve this? (Note: I don't want to install jmespath for JSON queries, because of company restrictions)

I couldn't figure out how I can only update one value with that nested dictionary?

U880D
  • 8,601
  • 6
  • 24
  • 40
  • According your description I wonder if you could use [`update_fact` module – Update currently set facts](https://docs.ansible.com/ansible/latest/collections/ansible/utils/update_fact_module.html)? – U880D Jul 20 '23 at 09:53

2 Answers2

1

According your description I wonder if you could use the update_fact module – Update currently set facts? It is intended for updating variables and one of the documented Examples seems to be similar to yours.

Based on your provided sample data, a minimal example playbook

---
- hosts: localhost
  become: false
  gather_facts: false

  tasks:

  - include_vars:
      file: result.json
      name: result

  - ansible.utils.update_fact:
      updates:
        - path: result.protocol_versions[0]
          value: {"enabled": false, "name": "TLSv1.1"}
    register: updated

  - debug:
      var: updated

will result into an output of

TASK [debug] *************************
ok: [localhost] =>
  updated:
    changed: true
    failed: false
    result:
      client_api_concurrency_limit: 40
      client_api_rate_limit: 100
      connection_timeout: 30
      global_api_concurrency_limit: 199
      protocol_versions:
      - enabled: false
        name: TLSv1.1
      - enabled: true
        name: TLSv1.2
      redirect_host: ''

If it is not clear if the result.protocol_versions will contain the correct versions, of if the order is not clear, it is also possible to define the desired state of the configuration.

  - ansible.utils.update_fact:
      updates:
        - path: result.protocol_versions
          value: [{"enabled": false, "name": "TLSv1.1"}, {"enabled": true, "name": "TLSv1.2"}]
    register: updated

Please take note that even if using update_fact module – Update currently set facts

Variables are not modified in place, instead they are returned by the module.

and that is the expected behavior.

U880D
  • 8,601
  • 6
  • 24
  • 40
  • Thanks, but the danger is that this may not be portable. The code should not expect TLSv1.1 or TLSv1.2 only nor expect them to be ordered in the current way (so [0] position may not work) in future it is possible to have TLSv1.3 and this won't work. What I am looking for is code that will just look for TLSv1.1 and modify that and leave the rest alone. That is, I don't need to konw about future versions. – Kelvin Wong Jul 21 '23 at 03:42
  • "_The code should not expect TLSv1.1 or TLSv1.2 only nor expect them to be ordered in the current way_", But isn't that what the second example shows? – U880D Jul 21 '23 at 03:59
  • Correct, but its not generic enough. If my result now also has {"enabled": true, "name": "TLSv1.3"}. The update will remove this row. What I want is a generic way without knowing any other values just to update enabled to false from TLSv1.1. – Kelvin Wong Jul 21 '23 at 06:42
  • Right, as far as I understand such is called configuration management and desired state configuration and one would just define the desired state `[{"enabled": false, "name": "TLSv1.1"}, {"enabled": true, "name": "TLSv1.2"}, {"enabled": true, "name": "TLSv1.3"}]`. – U880D Jul 21 '23 at 06:45
  • I agree, but I think my exercise is also to learn how to use ansible/jija/python logic to update a single value based on a nested dictionary such as this, also such logic can be useful else where to update variables or dictionaries not just for config mgmt. There don't seem to be a simple solution yet. I have explored with_dict, with_subelements, etc, but I could not figure out how they work for what I want to go – Kelvin Wong Jul 21 '23 at 08:10
0

Looks like I found the answer, this will update just the row with name=TLSv1.1

  - ansible.utils.update_fact:
      updates:
        - path: "result.json.protocol_versions[{{item}}].enabled"
          value: false
    loop: "{{ range(result.json.protocol_versions|length) }}"
    when: result.json.protocol_versions[{{item}}].name == "TLSv1.1"
    register: updated