Ansible json_query
is an on-demand feature that every ansible user wants to explore. In this post we are going to how Ansible JSON processing works.
During the infrastructure automation, we might end up in a situation where we need to process Huge datasets, especially in JSON format. We might also want to extract Single data or property from the huge dataset.
Typically, we would be looking for JSON parsers to do the job for us or we would create some in-house ones.
But here comes ansible's json_query
filter to save the day
What is json_query and how does it work?
As the name suggests, the Ansible json_query
filter is helping you to query the JSON document and get the elements in the JSON tree structure.
json_query is using the jmespath
Query language. It is a powerful query language to parse JSON content. It helps you to parse JSON content and filter the elements you want. You can pretty much do everything with JSON
Look at the following screenshot, A Home Page of JMESPATH
It has a nice toolset as well, where you could write and test your queries before you try it in realtime.
It is more like regex101 we use for Regular expression testing.
Some Sample JSON for testing - Ansible JSON
This is the sample data we are going to take for our testing. This is taken from the JSON Placeholder users endpoint URL.
JSON Placeholder has a set of JSON endpoints/URLs you can use for testing. It is more like a lorem ipsum
for JSON. You can visit the site and explore.
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "[email protected]",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
},
{
"id": 2,
"name": "Ervin Howell",
"username": "Antonette",
"email": "[email protected]",
"address": {
"street": "Victor Plains",
"suite": "Suite 879",
"city": "Wisokyburgh",
"zipcode": "90566-7771",
"geo": {
"lat": "-43.9509",
"lng": "-34.4618"
}
},
"phone": "010-692-6593 x09125",
"website": "anastasia.net",
"company": {
"name": "Deckow-Crist",
"catchPhrase": "Proactive didactic contingency",
"bs": "synergize scalable supply-chains"
}
}]
In this article, we are going to parse JSON response given by the URL directly in case if you would like to read JSON file and parse them with ansible. It is possible too.
Parse JSON data from URL response - using Ansible json_query
Let us consider the JSON content given above and try to filter only the following fields for each user using ansible json_query and create a Business card type of data for each user.
So now we are going to create a playbook, which is going to do the following operation
- Download the preceding JSON users data from the JSON placeholder URL
- Using the ansible json_query filter. Print the following fields for each user ( with custom key names)
- Name
- Phone
- CompanyName
- WebSite
- City
Now we need to come up with the JMESpath
query that we are going to use in ansible json_query
filter to get only these elements from on otherwise huge dataset.
I have used the JMESPath examples and tester to come up with the query and this is what the query I came up with
We are using the MultiSelect syntax of jmespath for this.
[*].{Name: name, Email: email, Phone: phone, CompanyName: company.name, WebSite: website, City: address.city}
Here is the playbook.
---
- name: JsonQuery Playbook
hosts: localhost
tasks:
- name: Download JSON content play
uri:
url: https://jsonplaceholder.typicode.com/users
return_content: yes
register: jsoncontent
- name: Business Card
debug: msg="{{ jsoncontent.json | json_query(jmesquery) }}"
vars:
jmesquery: "[*].{Name: name, Email: email, Phone: phone, CompanyName: company.name, WebSite: website, City: address.city}"
If you go through the preceding playbook you would understand that there is two tasks or Plays. the former one is to download the JSON content from the remote JSON PlaceHolder URL and the latter one is to parse the JSON content we got in the previous task and prepare a Business Card by choosing a set of selective fields.
I am assigning our query to the variable named jmesquery
and using the variable as a parameter inside the ansible json_query.
When I run the playbook, My output looked something like this ( I have truncated some of the users to keep it simple) Ideally it would give me 10 business cards as the endpoint has 10 set of users.
➜ AnsibleWorkSpace git:(master) ✗ ansible-playbook ansible_json.yml
PLAY [JsonQuery Playbook] ********************************************************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [localhost]
TASK [Download JSON content play] ************************************************************************************************************************************************
ok: [localhost]
TASK [Business Card] *************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"City": "Gwenborough",
"CompanyName": "Romaguera-Crona",
"Email": "[email protected]",
"Name": "Leanne Graham",
"Phone": "1-770-736-8031 x56442",
"WebSite": "hildegard.org"
},
{
"City": "Wisokyburgh",
"CompanyName": "Deckow-Crist",
"Email": "[email protected]",
"Name": "Ervin Howell",
"Phone": "010-692-6593 x09125",
"WebSite": "anastasia.net"
},
............
............
PLAY RECAP ***********************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
From the result shown. You could understand that the result has been Sorted by Ansible, despite our query is designed to give the City as the last value.
Example2: List only the Names
Here is one more example to list only the names from the JSON Dataset
---
- name: JsonQuery Playbook
hosts: localhost
tasks:
- name: Download JSON content play
uri:
url: https://jsonplaceholder.typicode.com/users
return_content: yes
register: jsoncontent
- name: Just the Names
debug: msg="{{ jsoncontent.json | json_query(jmesquery)}}"
vars:
jmesquery: "[*].name"
here is the result of the playbook
➜ AnsibleWorkSpace git:(master) ✗ ansible-playbook ansible_json.yml
PLAY [JsonQuery Playbook] **********************************************************************************
TASK [Gathering Facts] *************************************************************************************
ok: [localhost]
TASK [Download JSON content play] **************************************************************************
ok: [localhost]
TASK [Just the Names] ***************************************************************************************
ok: [localhost] => {
"msg": [
"Leanne Graham",
"Ervin Howell",
"Clementine Bauch",
"Patricia Lebsack",
"Chelsey Dietrich",
"Mrs. Dennis Schulist",
"Kurtis Weissnat",
"Nicholas Runolfsdottir V",
"Glenna Reichert",
"Clementina DuBuque"
]
}
PLAY RECAP *************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Example3: Filter only specific user or users
In the first example, we have seen how to filter out the necessary fields to create business card type data set. But there we got all 10 users data. What if we want to limit it to a specific user
Here is the query we are going to use to limit the user by name
In the following query the name is surrounded by a backquote/backtick
`
not a Single quote.
[? name==`Leanne Graham`].{Name: name, Email: email, Phone: phone, CompanyName: company.name, WebSite: website, City: address.city}
The preceding JMESPath Query finds the user with a name Leanne Graham
and getting his business card.
to Run this in a playbook, You just have to modify the jmesquery
variable
---
- name: JsonQuery Playbook
hosts: localhost
tasks:
- name: Download JSON content play
uri:
url: https://jsonplaceholder.typicode.com/users
return_content: yes
register: jsoncontent
- name: Business Card
debug: msg="{{ jsoncontent.json | json_query(jmesquery)}}"
vars:
jmesquery: "[? name==`Leanne Graham`].{Name: name, Email: email, Phone: phone, CompanyName: company.name, WebSite: website, City: address.city}"
The output of this playbook is given below.
➜ AnsibleWorkSpace git:(master) ✗ ansible-playbook ansible_json.yml
PLAY [JsonQuery Playbook] **********************************************************************************
TASK [Gathering Facts] *************************************************************************************
ok: [localhost]
TASK [Download JSON content play] **************************************************************************
ok: [localhost]
TASK [Business Card] ***************************************************************************************
ok: [localhost] => {
"msg": [
{
"City": "Gwenborough",
"CompanyName": "Romaguera-Crona",
"Email": "[email protected]",
"Name": "Leanne Graham",
"Phone": "1-770-736-8031 x56442",
"WebSite": "hildegard.org"
}
]
}
PLAY RECAP *************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
If you want to use multiple users in your search. You can do it just like you would do any other programming language by using or ||
Here is the jmespath query you can use in your ansible json_query
[? name==`Leanne Graham` || name==`Ervin Howell`].{Name: name, Email: email, Phone: phone, CompayName: company.name, WebSite: website, City: address.city}
You can write many jmes queries using the JMESPath testing tool and use it in your playbook.
Example4: Parse json_query result in a loop and access it as variable
So far we have seen jmespath queries and how to use them in ansible json_query module. Now we are going to see how to parse the json_query result in an ansible loop and create some dynamic variables (or) use them in the task.
Now we are going to create a Phonebook with the dataset we have.
Here we are NOT going to use major jmes query. Rather, we are going to use Ansible's inbuilt features like combine
to create the phonebook.
The Expected result is a set of
PersonName: PhoneNumber
Here is the playbook we use to get this done for us
---
- name: JsonQuery Playbook
hosts: localhost
tasks:
- name: Download JSON content play
uri:
url: https://jsonplaceholder.typicode.com/users
return_content: yes
register: jsoncontent
- name: Collecting UserName and Mobile Number info
no_log: True
set_fact:
phonebook: "{{phonebook|default({}) | combine ( {item.name : item.phone}) }}"
with_items: "{{ jsoncontent.json | json_query('[*]')}}"
- name: The Phonebook
debug: var=phonebook
you can see that the json_query we have used is pretty simple, All the logic part is being done by Ansible.
A quick explanation of the logic
- phonebook|default({})
creates a new dictionary named phonebook
- combine ({item.name : item.phone})
used to add an element in the dictionary.
There is a new keyword used in this playbook which deserves an explanation
- no_log
this prevents the content being displayed as ansible loop iterate the json which is otherwise displayed. You can try to remove this and execute the playbook to see what does it result.
Here is the execution output of this playbook
➜ AnsibleWorkSpace git:(master) ✗ ansible-playbook ansible_json.yml
PLAY [JsonQuery Playbook] **********************************************************************************
TASK [Gathering Facts] *************************************************************************************
ok: [localhost]
TASK [Download JSON content play] **************************************************************************
ok: [localhost]
TASK [Collecting UserName and Mobile Number info] **********************************************************
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost] => (item=None)
ok: [localhost]
TASK [The Phonebook] ***************************************************************************************
ok: [localhost] => {
"phonebook": {
"Chelsey Dietrich": "(254)954-1289",
"Clementina DuBuque": "024-648-3804",
"Clementine Bauch": "1-463-123-4447",
"Ervin Howell": "010-692-6593 x09125",
"Glenna Reichert": "(775)976-6794 x41206",
"Kurtis Weissnat": "210.067.6132",
"Leanne Graham": "1-770-736-8031 x56442",
"Mrs. Dennis Schulist": "1-477-935-8478 x6430",
"Nicholas Runolfsdottir V": "586.493.6943 x140",
"Patricia Lebsack": "493-170-9623 x156"
}
}
PLAY RECAP *************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
We have seen various examples of ansible json_query and how ansible works with json. Hope this helps.
If you have any questions. Feel free to comment
Cheers
Sarav AK
Follow me on Linkedin My Profile Follow DevopsJunction onFacebook orTwitter For more practical videos and tutorials. Subscribe to our channel
Signup for Exclusive "Subscriber-only" Content