ansiblejmespathjson-query

json_query not parsing registered variable containing JSON output with escaped double-quotes in Ansible


I have a cert creation tool that outputs certs like so:

{
    "cert": "abc123......",
    "id": 10,
    "key": "abc123....."
}

The actual cert and key attributes are base64 encoded but that's irrelevant to the problem here.

When I run this from ansible and register the output as a variable, it is stored with the double-quotes escaped:

{
    "cert_output": {
        "changed": true,
        "delta": "0:00:00.673537",
        "end": "2024-05-14 13:34:45.492559",
        "failed": false,
        "msg": "",
        "rc": 0,
        "start": "2024-05-14 13:34:44.819022",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "{\"id\":233370662,\"cert\":\"abc123...\",\"key\":\"abc123...\"}",
    }
}

I do not find the way to parse out individual keys.
The two flavour of set_fact below are not working as intended:

- set_fact:
    cert: "{{ cert_output.stdout|from_json|json_query('.cert') }}"
    key: "{{ cert_output.stdout|from_json|json_query('.key') }}"

- set_fact:
    cert: "{{ cert_output.stdout|json_query('.cert') }}"
    key: "{{ cert_output.stdout|json_query('.key') }}"

Ansible error:

FAILED! => {"msg": "JMESPathError in json_query filter plugin:\ninvalid token: Parse error at column 0, token "." (DOT), for expression:\n".cert"\n ^"}


Solution

  • You do not need to use JMESPath for this use case, Ansible is very much JSON capable. As soon as you have a JSON, and not the string representation of a JSON, thanks to your correct usage of the from_json filter, then you can go back querying the dictionaries attributes with the dot notation:

    - set_fact:
        cert: "{{ (cert_output.stdout | from_json).cert }}"
        key: "{{ (cert_output.stdout | from_json).key }}"
    

    Which results in

    ok: [localhost] => changed=false 
      ansible_facts:
        cert: abc123...
        key: abc123...
    

    Note: the above output was generated running the playbook with the option -v, which, amongst other useful information, shows the result of a set_fact task.


    As for your JMESPath queries, accessing a JSON identifier on the "root" node of a JSON is done simply by addressing its key, without any dot, so:

    - set_fact:
        cert: "{{ cert_output.stdout | from_json | json_query('cert') }}"
        key: "{{ cert_output.stdout | from_json | json_query('key') }}"
    

    Would result in the same as above.