JSON has become the lingua franca for data exchange on the web. Its simplicity, readability, and universality make JSON an ideal format for transmitting structured data between web services, servers, and applications.
Ansible, being a prolific automation tool, often needs to parse, process, and filter JSON data from various sources like APIs, file contents, command outputs, etc. This is where Ansible‘s json_query filter comes into play.
In this comprehensive guide, we will explore various practical examples of how to leverage Ansible json_query to slice and dice JSON data with precision for your automation tasks.
Understanding JSON in Ansible
Let‘s first quickly go through some JSON basics:
- JSON stands for JavaScript Object Notation
- Lightweight text-based data format
- Easy for humans to read/write and machines to parse/generate
- Ideal for transmitting data across networks
- Uses JavaScript syntax to structure data
- Common data types in JSON:
- Number (integers, floats)
- String (text within quotes)
- Boolean (true/false)
- Array (ordered list within square brackets)
- Object (collection of key-value pairs within curly braces)
- Null
Here is an example JSON document:
{
"name": "John",
"age": 30,
"skills": ["Python", "Ansible", "Linux"],
"active": true
}
As you can see, JSON maps directly to common data structures. This makes it a great fit for data exchange.
Now let‘s see how Ansible handles JSON.
Ansible provides various plugins to deal with JSON data:
from_jsonfilter – Converts JSON string to Ansible datato_jsonfilter – Converts Ansible data to JSON stringfrom_yamlfilter – Converts YAML string to Ansible datato_yamlfilter – Converts Ansible data to YAML stringjson_queryfilter – Query/filter JSON data using JMESPath notation
Among these, json_query provides the most flexible capabilities for filtering JSON data.
Introducing JMESPath for JSON Querying
The Ansible json_query filter uses JMESPath under the hood for querying and filtering JSON documents.
JMESPath allows you to select and transform elements from a JSON document. It provides Ansible the power to query JSON data with expressions similar to how you would query a database.
For example, consider the following simple inventory data in JSON format:
[
{
"name": "host1",
"ip": "10.20.30.40",
"active": true,
"services": ["http", "smtp", "ssh"]
},
{
"name": "host2",
"ip": "10.20.30.41",
"active": false,
"services": ["http", "ftp"]
}
]
Here are some example JMESPath queries on this data:
hosts[].name # Get names of all hosts
hosts[?active==true].ip # Get IP of active hosts
hosts[].services[1] # Get second service of all hosts
As you can see, JMESPath allows:
- Selecting JSON elements using dot notation and square brackets
- Filtering arrays using comparison operators, functions and wildcards
- Output formatting using built-in functions
This provides immense power for querying JSON documents.
Note: JMESPath syntax may remind you of MongoDB queries. But JMESPath is its own API optimized for data filtering use cases.
We have only scratched the surface of what‘s possible with JMESPath. Visit the JMESPath tutorial to explore its full functionality.
Now let‘s see how we can leverage this within Ansible.
Using json_query Filter in Ansible
The json_query filter allows querying JSON data using JMESPath expressions. This works regardless of the JSON source – hardcoded data, vars, register outputs, command/shell outputs etc.
Let‘s start with some examples to see it in action.
Example 1: Query Simple JSON Array
For our first example, we will query a simple JSON array hardcoded in a variable:
- hosts: localhost
gather_facts: no
vars:
my_hosts:
[
{"name": "host1", "group": "web"},
{"name": "host2", "group": "db"},
{"name": "host3", "group": "storage"}
]
tasks:
- name: Fetch all host names
debug:
msg: "{{ my_hosts | json_query(‘[].name‘) }}"
This JMESPath query *[].name selects the name key from all objects within the array.
The output will be:
ok: [localhost] => {
"msg": [
"host1",
"host2",
"host3"
]
}
Let‘s try another example.
Example 2: Filter Hosts Belonging to a Group
Here we will filter the hosts belonging to the web group:
- hosts: localhost
gather_facts: no
vars:
my_hosts:
[
{"name": "host1", "group": "web"},
{"name": "host2", "group": "db"}
]
tasks:
- name: Fetch all web hosts
debug:
msg: "{{ my_hosts | json_query(‘[?group==`web`].name‘) }}"
This uses the JMESPath expression [?group==web].name to filter the array.
The ? operator allows filtering the elements in an array. Here we are selecting elements whose group key matches web.
The output filters host1 successfully:
ok: [localhost] => {
"msg": [
"host1"
]
}
As you can see, combining JMESPath filtering and projections allows extracting precise data from JSON documents.
Example 3: Query Command Output
In addition to hardcoded data, the json_query filter can query the output of any Ansible command/shell module:
- hosts: localhost
tasks:
- name: Run ip command
shell: ip addr show
register: ip_output
- name: Fetch public IP
debug:
msg: "{{ ip_output.stdout | from_json | json_query(‘[?family==`inet`].[addr]‘) }}"
Here we use the shell ip addr show command whose output is JSON formatted. The stdout is registered to parse using json_query.
We first convert the raw text to JSON using the from_json filter. This allows subsequently applying the JMESPath query to fetch the public IP address.
Example 4: Chain Multiple json_query Calls
The json_query filter can be chained to perform complex multi-step querying:
- hosts: localhost
vars:
hosts:
[
{"name": "host1", "services": [{"name": "http", "port": 80}]},
{"name": "host2", "services": [{"name": "smtp", "port": 25}]}
]
tasks:
- name: Get service ports
set_fact:
ports: "{{ hosts |
json_query(‘[].services[].port‘) |
json_query(‘[*][]‘) }}"
- name: Print ports
debug:
var: ports
Here we first extract all service ports and then further flatten the output into a simple array. Chaining json_query allows constructing flexible transformation pipelines.
The debug output will be:
ok: [localhost] => {
"ports": [
80,
25
]
}
Example 5: Ansible Playbook to Monitor Websites
To conclude, let‘s go through a real-world example playbook showcasing the strengths of json_query:
playbook.yml
---
- hosts: localhost
vars:
sites:
- name: example1.com
url: https://example1.com
status_codes: [200, 301]
- name: example2.com
url: https://example2.com
status_codes: [200]
tasks:
- name: Get site status
uri:
url: "{{ item.url }}"
return_content: yes
loop: "{{ sites }}"
register: result
- name: Set site status
set_fact:
site_status: "{{ result.results |
json_query(‘[].{ name: item.name, ok: item.status in codes }‘, {codes: item.item.status_codes}) }}"
- name: Print status
debug:
var: site_status
This playbook performs the following steps:
- Fetch site pages using
urimodule - Process output and set availability status for every site
- Print the availability report
Here json_query constructs the desired availability data by:
- Transforming
urioutput into required shape - Checking if returned status is expected
The output will be:
ok: [localhost] => {
"site_status": [
{
"name": "example1.com",
"ok": true
},
{
"name": "example2.com",
"ok": false
}
]
}
This demonstrates converting raw JSON output into business data using json_query.
Tips for Effective Data Filtering
Here are some tips for unlocking the true power of Ansible‘s json_query:
1. Structure and Register Command Outputs
Many Ansible modules like uri, command, shell etc. output unstructured text data.
Always register these outputs and convert to JSON using from_json before querying:
- command: <...>
register: result
- set_fact:
data: "{{ result.stdout | from_json }}"
- debug:
msg: "{{ data | json_query(...) }}"
This structures the output and enables flexible JSON-based filtering.
2. Use Dedicated Filter Vars
Avoid cluttering your playbooks with complex json_query filters.
Set the filtered data to a clean var instead:
- set_fact:
names: "{{ data | json_query(‘[].name‘) }}"
- debug:
var: names
This improves readability by separating query logic.
3. Reuse Common Query Snippets
Certain filter snippets like [*].ip_address tend to repeat across tasks.
Store them in vars for better reusability:
ip_address_path: "[*].ip_address"
- debug:
msg: "{{ data | json_query(ip_address_path) }}"
Bonus tip: Set vars in group_vars/host_vars for global reuse!
4. Use JMESPath Functions
JMESPath provides builtin functions extending filtering capabilities:
- String functions:
length(),starts_with(),ends_with()etc. - Math functions:
max(),min(),avg()etc. - Date functions:
date(), time manipulation etc. - Encoding:
base64(),csv()etc. - Sorting:
sort(),sort_by()etc.
Leverage these functions to reduce playbook complexity.
Conclusion
Ansible json_query is an invaluable asset when working with JSON data. It makes easy work of even highly complex JSON processing tasks using the power of JMESPath querying.
We explored various examples like:
- Filtering simple JSON arrays
- Processing command outputs
- Chaining for flexible transformations
- Constructing business data
The key takeaway is structuring unorganized JSON data enables simpler playbooks through precise filtering. Fix the data, not the playbooks!
As a bonus, following JSON querying best practices improves playbook flexibility, modularity and reusability.
JSON is now an integral part of modern web APIs and data pipelines. I hope this guide helps you become a JSON querying ninja boosting your Ansible skills!


