Work with JSON output of a Command#
Problem#
A complex service provides a command to get its status. The command has an option --json to switch to a machine readable format.
How can we read the value of BackendState and run further tasks based on it?
tailscale status --peers=false --json
{
"Version": "1.74.1-tccd6bf2f4-g8fce4ce11",
"TUN": true,
"BackendState": "Running",
"HaveNodeKey": true,
"AuthURL": "",
"TailscaleIPs": [
"100.100.xx.xx"
],
"Self": {
"ID": "...",
"...": "..."
}
}
Solution#
Install the Python dependency jmespath, then use the combination of the filters from_json and json_query get the value of a key.
- name: Tailscale status
ansible.builtin.command: tailscale status --peers=false --json
register: _tailscale__status
changed_when: false
- name: Tailscale up
ansible.builtin.command: >
tailscale up
--auth-key={{ tailscale__auth_token }}
--advertise-routes={{ tailscale__subnets | join(',') }}
when: _tailscale__status.stdout | ansible.builtin.from_json | community.general.json_query('BackendState') == 'NeedsLogin'
Explanation#
The output of the command is returned in json format but Ansible doesn't know that and returns it as String in stdout. That is why we need to turn it into a Dict using from_json first.
Only then can we pick a value out of it. The json_query filter of the community.general collection is the general purpose tool for this and needs the Python library jmespath installed on the controller.
Not always needed
For a key at the top level, plain Jinja is enough and saves us the dependency:
when: (_tailscale__status.stdout | ansible.builtin.from_json).BackendState == 'NeedsLogin'
json_query plays out its strengths as soon as we have to search through nested structures or filter lists.