1

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 ^"}

0

1 Answer 1

0

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Would I need to use json_query if I had a list of dicts: [{ "cert": "abc123......", "id": 10, "key": "abc123....." }]
@Chuck it all depends. If the list only contains one element and you want the first one, you can go cert: "{{ (cert_output.stdout | from_json).0.cert }}", in order to target the first element of the list. If you have multiple, then I guess you would like cert to be a list too? That's possible with a filter e.g. cert: "{{ (cert_output.stdout | from_json) | map(attribute='cert') }}"
Actually was able to do it using select/map. THx

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.