Ansible

Ansible Json_Query

JSON, which stands for JavaScript Object Notation, is a lightweight, text-based data interchange format that is human-readable and machine-parsable.

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:

$ pip install jmespath

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:

ok: [localhost]
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:

ok: [localhost] => {
    "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:

ok: [localhost]
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:

ok: [localhost]
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.

About the author

John Otieno

My name is John and am a fellow geek like you. I am passionate about all things computers from Hardware, Operating systems to Programming. My dream is to share my knowledge with the world and help out fellow geeks. Follow my content by subscribing to LinuxHint mailing list