You run an az command and get two screens of JSON when all you wanted was one name to put in a script. Or you get a neat table that silently hides the field you were looking for. This lesson is how to choose what az prints.
What you need to know already: 22.1 (az, resource groups, signing in), 7.11 (JSON and jq), 6.1 (a bash script and variables).
Every az command produces JSON internally. -o (long form --output) only changes how that JSON is printed, and --query (next lesson) reshapes it before printing. Learn what each format does to the same data.
The examples use aks-sysop, the lab's Kubernetes cluster on Azure (Azure's managed Kubernetes service: Azure runs the Kubernetes control plane for you, you get the nodes - Ch 23 covers it). A node pool is a group of identical nodes (same VM size) in that cluster. You do not need more than that here.
json (the default)
az aks nodepool show -g rg-oncall-lab --cluster-name aks-sysop -n user: show one node pool; -g its resource group, --cluster-name which cluster, -n the pool's name.
$ az aks nodepool show -g rg-oncall-lab --cluster-name aks-sysop -n user
{
"availabilityZones": ["1", "2", "3"],
"count": 3,
"enableAutoScaling": false,
"maxCount": null,
"maxPods": 30,
"mode": "User",
"name": "user",
...
}
Two things to notice. Keys are sorted alphabetically - the CLI sorts them, so do not expect the order from the Azure documentation. And null is printed, so you can see which settings are unset.
-o jsonc is the same with colour, for humans.
table
$ az aks nodepool list -g rg-oncall-lab --cluster-name aks-sysop -o table
Name OsType KubernetesVersion VmSize Count MaxPods ProvisioningState Mode
------ ------ ----------------- ---------------- ----- ------- ----------------- ------
system Linux 1.33.3 Standard_D4ds_v5 2 30 Succeeded System
user Linux 1.33.3 Standard_D8ds_v5 3 30 Succeeded User
nodepool list lists every pool. Read the columns: VmSize is the machine size of each node (Standard_D4ds_v5 = 4 vCPUs), Count how many nodes, ProvisioningState whether Azure finished creating it (Succeeded).
Many commands ship a table transformer (a built-in choice of columns) that picks these. Commands without one get an automatic table: every top-level scalar field (a plain value like a string or number, not an object or a list), sorted, with the first letter capitalised - and id, etag and type dropped. Nested objects and lists silently disappear:
$ az network lb outbound-rule list -g MC_rg-oncall-lab_aks-sysop_westeurope --lb-name kubernetes -o table
AllocatedOutboundPorts EnableTcpReset IdleTimeoutInMinutes Name Protocol ProvisioningState ResourceGroup
---------------------- -------------- -------------------- --------------- -------- ----------------- -------------
0 True 30 aksOutboundRule All Succeeded MC_rg-...
(This lists the outbound rules of the cluster's load balancer - Ch 23 explains what they are. What matters here is the shape.) The rule's backendAddressPool and frontendIPConfigurations are not there, because they are nested. If a column you expect is missing from a table, it is nested - use --query.
Booleans print as True/False (the CLI is written in Python). Never parse table output in a script: column widths change with the data.
tsv - the format for scripts
tsv = tab-separated values: plain text, one row per line, a tab between columns.
az group list --query "[].name" -o tsv: list the resource groups, keep only their names (the query is next lesson), print them bare.
$ az group list --query "[].name" -o tsv
rg-oncall-lab
rg-shop-prod
MC_rg-oncall-lab_aks-sysop_westeurope
MC_rg-shop-prod_aks-shop-prod_westeurope
rg-tfstate
No quotes, no brackets. This is how a value gets into a shell variable:
KV_ID=$(az keyvault show -n kv-shop-prod --query id -o tsv)
az role assignment list --scope "$KV_ID" -o table
az keyvault show -n kv-shop-prod shows the vault; --query id keeps only its resource ID; $( ) puts that into KV_ID. The second command lists who has access at exactly that scope (lesson 22.13).
Do the same with the default JSON output and you get the value with quotes around it, which then fails in the next command:
KV_ID=$(az keyvault show -n kv-shop-prod --query id) # "\"/subscriptions/...\""
For several fields per row, the column order in tsv follows your --query in the order you wrote it, so --query "[].[name, location]" gives name<TAB>location. Without a query, tsv prints the scalars of each object in sorted key order - fragile. Always pair -o tsv with an explicit --query.
yaml, none
$ az aks show -g rg-oncall-lab -n aks-sysop --query networkProfile -o yaml
dnsServiceIp: 10.0.0.10
loadBalancerProfile:
allocatedOutboundPorts: 0
idleTimeoutInMinutes: 30
...
networkPlugin: azure
networkPluginMode: null
serviceCidr: 10.0.0.0/16
az aks show shows the whole cluster; --query networkProfile keeps just its network settings. -o yaml reads well for deep objects like this one.
-o none prints nothing - for commands in scripts whose output you do not want in the log (a secret set that would echo the secret's value, for instance).
jq still works
az aks show -g rg-oncall-lab -n aks-sysop | jq -r '.agentPoolProfiles[] | "\(.name) \(.count)"'
Chapter 7's jq is fine here. --query has one advantage: it runs inside the CLI, so it works the same in PowerShell, bash and a pipeline without jq installed.
Summary
| format | for | trap |
|---|---|---|
| json | reading, piping to jq | keys sorted |
| table | eyes only | nested fields vanish |
| tsv | shell variables, loops | needs an explicit --query |
| yaml | deep objects | - |
| none | silence | - |
What you can now do
- Pick the output format for the job: table to look, tsv to script, json for jq.
- Explain why a column is missing from a table, and why a variable got quotes in it.