Skip to content

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.

See also#