JSON is derived from the JavaScript programming language and delineates the data using a subset of the JavaScript syntax. However, despite its origins in JavaScript, JSON is versatile and widely adopted in all programming languages. This means that you will find many applications, libraries, and APIs that generate and parse the JSON data, regardless of the language.
JSON primarily facilitates data transmission between a server and a web application or between different layers of an application. Due to its compact and structured format, it’s applicable in serializing and deserializing the structured data.
Ansible and JSON
If you tried Ansible, you will probably understand the importance of tools that can quickly parse and filter the JSON data to get a precise information.
Luckily, Ansible has a powerful tool for exactly this purpose called json_query. This module leverages the power of the JMESPath language specification to query and parse the JSON structures.
Whether fetching an information from an API or iterating over a complex data in your playbooks, understanding the json_query can be your secret weapon.
Basic JSON Data
Before we start using the json_query method, let us create a basic JSON data structure for illustration purposes.
"servers": [
{
"id": 1,
"name": "Node 1",
"roles": [
"database",
"backup"
],
"details": {
"ip_address": "192.168.0.101",
"location": "us-east-1",
"cpu_cores": 8,
"ram_gb": 16
}
},
{
"id": 2,
"name": "Node 2 ",
"roles": [
"load_balance",
"backup"
],
"details": {
"ip_address": "192.168.0.102",
"location": "us-west-1",
"cpu_cores": 4,
"ram_gb": 8
}
}
],
"groups": [
{
"id": "A",
"name": "load_balance",
"members": [
2
]
},
{
"id": "B",
"name": "backup",
"members": [
1,
2
]
},
{
"id": "C",
"name": "database",
"members": [
1
]
}
]
}
In this case, a JSON structure stores an information about the available servers in a given deployment.
Installing JMESPath
Before using the json_query module in Ansible, we must install the JMESPath package on the Ansible controller. We can do this using pip as follows:
Example 1: Fetch All Server Names
To fetch all server names from the sample JSON data, we can run the playbook as shown in the following:
- hosts: localhost
vars:
cluster_info: "{{ lookup('file', 'cluster_info.json') | from_json }}"
tasks:
- debug:
msg: "{{ cluster_info | json_query('servers[*].name') }}"
In this case, create a variable to store the contents of the cluster_info.json file. Finally, we use the json_query module to check the name of the servers as shown in the resulting output:
TASK [debug] ********************************************
ok: [localhost] => {
"msg": [
"Node 1",
"Node 2 "
]
}
Example 2: Fetch the Details of Servers with the “Load_Balance” Role
To retrieve the details for the servers with the role of load_balance, we can query the previous JSON data as follows:
- hosts: localhost
vars:
cluster_info: "{{ lookup('file', 'cluster_info.json') | from_json }}"
tasks:
- debug:
msg: "{{ cluster_info | json_query('servers[?roles.contains(@, `load_balance`)].details') }}"
In this playbook, we use the json_query to get the details of the server whose role contains the load_balance string. The resulting output is as follows:
"msg": [
{
"cpu_cores": 4,
"ip_address": "192.168.0.102",
"location": "us-west-1",
"ram_gb": 8
}
]
}
Example 3: Fetch the Group Names Containing the Given ID
We can also get the names of the groups that contains the server with an ID of 1.
- hosts: localhost
vars:
cluster_info: "{{ lookup('file', 'cluster_info.json') | from_json }}"
tasks:
- debug:
msg: "{{ cluster_info | json_query('groups[?members.contains(@, `1`)].name') }}"
This should return the names that contains the specified ID.
An example output is as follows:
TASK [debug] ************************
ok: [localhost] => {
"msg": [
"backup",
"database"
]
}
Example 4: Combining Multiple Filters (Advanced Usage)
We can also combine multiple filters to fetch a more precise information. For example, suppose we wish to fetch the names of the servers with more than five CPU cores and have the role of backup.
- hosts: localhost
vars:
cluster_info: "{{ lookup('file', 'cluster_info.json') | from_json }}"
tasks:
- debug:
msg: "{{ cluster_info | json_query('servers[?details.cpu_cores > `5` && roles.contains(@, `backup`)].name') }}"
This should return the name of the servers that match the specified parameters. In our case, that is only one entry as shown in the output:
TASK [debug] *************************
ok: [localhost] => {
"msg": [
"Node 1"
]
}
Conclusion
The json_query filter in Ansible is a game-changer when dealing with structured data. As you saw in this tutorial, this module offers many capabilities that quickly enhance your JSON needs when working in Ansible. It goes without saying that the given examples only scratch the surface.
You can check the JMESPath examples to learn what more this module can do and how you can leverage its power.