Azure CLI and JMESPath Query
On
JMESPath is a query language for JSON which you can use to extract and transform elements in a JSON document. So what has that got to do with Azure CLI?
Azure CLI command results are either a JSON array or dictionary. Even when an output format other than JSON is specified, CLI command results are first treated as JSON to enable queries to be performed, before outputted into the format specified.
All CLI commands supports the --query
parameter, and accepts a JMESPath argument.
If we send the following command to azure
az vm show -g demo -n test-vm -o json
we will get a similar result as
{
"additionalCapabilities": null,
"availabilitySet": null,
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": true,
"storageUri": "https://xxxxxx.blob.core.windows.net/"
}
},
...
"osProfile": {
"adminPassword": null,
"adminUsername": "azureuser",
"allowExtensionOperations": true,
"computerName": "test-vm",
"customData": null,
"linuxConfiguration": {
"disablePasswordAuthentication": true,
"provisionVmAgent": true,
"ssh": {
"publicKeys": [
{
"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDMobZNJTqgjWn/IB5xlilvE4Y+BMYpqkDnGRUcA0g9BYPgrGSQquCES37v2e3JmpfDPHFsaR+CPKlVr2GoVJMMHeRcMJhj50ZWq0hAnkJBhlZVWy8S7dwdGAqPyPmWM2iJDCVMVrLITAJCno47O4Ees7RCH6ku7kU86b1NOanvrNwqTHr14wtnLhgZ0gQ5GV1oLWvMEVg1YFMIgPRkTsSQKWCG5lLqQ45aU/4NMJoUxGyJTL9i8YxMavaB1Z2npfTQDQo9+womZ7SXzHaIWC858gWNl9e5UFyHDnTEDc14hKkf1CqnGJVcCJkmSfmrrHk/CkmF0ZT3whTHO1DhJTtV stramer@contoso",
"path": "/home/azureuser/.ssh/authorized_keys"
}
]
}
},
"secrets": [],
"windowsConfiguration": null
},
....
}
This is really not a pretty sight. If we are interested in some specific values, then we have to use the JMESPath query to look for those values.
Basic Expressions
The simplest JMESPath expression is an identifier, which selects a key in an JSON object: Given
{"a": "foo", "b": "bar", "c": "baz","d"; {"e" :value}}
Let’s see what we get by using the queries below
a => "foo"
c => "baz"
q => null
When a key does not exists, the query will return null
, (or the language equivalent of null
) .
You can use a subexpression to return to nested values in a JSON object:
d.e => value
d.f => null
With our Azure CLI command earlier, if we want to check the status of the bootDiagnostics
, we can use
az vm show -g demo -n test-vm -query diagnosticsProfile.bootDiagnostics.enabled
which will return
true
Index Expressions allow us to select a specific element in a list. This is similar to array access in common programming languages. Indexing is 0 based.
Example
["a", "b", {"c": [
{"d": [0, [1, 2]]},
{"d": [3, 4]}
]}
]
[0] => "a"
[1] => "b"
[2]c[0].d[1] => [1, 2]
If you specify an index that’s larger than the list, a value of null
is returned.
Slicing
Slices allow you to select a contiguous subset of an array. If has the format [start:end:step]
. By default, the start value is 0, the step value is 1, and all these values are optional.
The ending index is the first index which you do not want included in the slice.
Example
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0:4] => [0,1,2,3]
[:4} => [0,1,2,3]
[2:4:2] => [2]
[::2] => [0,2,4,6,8]
[::] => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Projections
Consider the following JSON document;
{
"people": [
{"name": "James", "age": 30,"state": {"name": "up"}},
{"name": "Jacob", "age": 40,"state": {"name": "down"}},
{"name": "Jayden", "age": 50,"state": {"name": "up"}},
{"missing": "different"}
],
"foo": {"bar": "baz"}
}
Say we want to select all people who have a name, or put differently, want to select the names of all people, how do we do it? That is where projections come in.
Projections allow you to apply an expression to a collection of elements. Example is
people[*].name
Projections break an expression into two parts, left hand side (LHS) and right hand side (RHS).
- The LHS is the section of the expression before the . character.
- The LHS creates a JSON array of initial values.
- The RHS is the expression to project for each element in the JSON array created by LHS.
There are four kinds of projections:
- List Projections
- Slice Projections
- Object Projections
- Flatten Projections
List and Slice Projections
A wildcard expression creates a list projection, which is a projection over a JSON array.
What does this expression mean?
people[*].name
- The LHS is
people[*]
and RHS isname
- A wildcard * is used to index the people array
The LHS produces a list projection
[
{
"name": "James",
"age": 30,
"state": {"name": "up"}
},
{
"name": "Jacob",
"age": 40,
"state": {"name": "down"}
},
{
"name": "Jayden",
"age": 50,
"state": {"name": "up"}
},
{
"missing": "different"
}
]
The RHS .name
expression is then projected over the array, which returns
[
"James",
"Jacob",
"Jayden"
]
The results are collected into a JSON array and returned as the result of the expression. The last element, {“missing”: “different”} evaluates to null, so it is not added to the returned array.
Slice projections are almost identical to a list projection, with the exception that the left hand side is the result of evaluating the slice, which may not include all the elements in the original list:
people[:2].name
returns
[
"James",
"Jacob"
]
Object Projections
Whereas a list projection is defined for a JSON array, an object projection is defined for a JSON object. You can create an object projection using the .*
syntax. This will create a list of the values of the JSON object, and project the right hand side of the projection onto the list of values.
Consider
{
"ops": {
"functionA": {"numArgs": 2},
"functionB": {"numArgs": 3},
"functionC": {"variadic": true}
}
}
For the expression ops.*.numArgs, the LHS (ops.*) will produce a list projection
[
{
"numArgs": 2
},
{
"numArgs": 3
},
{
"variadic": true
}
]
And with the RHS expression .numArgs
, we get
[
2,
3
]
null
values are not included in the final result.
Other posts in this series
- Azure CLI and JMESPath Query
- Azure CLI and JMESPath Query -Part 2