🛠️ AWS CLI Mastery
October 8, 2025
JMESPath: Queries Avanzadas en AWS CLI
📋 Contexto
JMESPath es el lenguaje de query que usa AWS CLI con el parámetro --query. Dominar JMESPath transforma AWS CLI de "herramienta básica" a "súper poder de automatización". En este post, las queries que más uso y patrones avanzados.
🎯 ¿Qué es JMESPath?
JMESPath es un lenguaje de query para JSON, similar a:
- XPath: para XML
- SQL SELECT: para bases de datos
- jq: para JSON en CLI
Ejemplo básico:
# JSON
{
"Users": [
{"Name": "Alice", "Age": 30},
{"Name": "Bob", "Age": 25}
]
}
# Query
Users[0].Name
# Output
"Alice"
💡 Sintaxis Básica
1. Acceder a Campos
# Dot notation
aws ec2 describe-instances --query 'Reservations[0].Instances[0].InstanceId'
# Output
"i-1234567890abcdef0"
2. Wildcard (Todas las Instancias)
# Get all instance IDs
aws ec2 describe-instances --query 'Reservations[*].Instances[*].InstanceId'
# Flatten (remove nested arrays)
aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId'
3. Proyecciones (Seleccionar Múltiples Campos)
# Multi-field projection
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,InstanceType,State.Name]'
# Output (array of arrays)
[
["i-111", "t3.micro", "running"],
["i-222", "t3.small", "stopped"]
]
4. Filtros
# Filter running instances
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`].InstanceId'
# Multiple conditions (AND)
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running` && InstanceType==`t3.micro`]'
# OR condition
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running` || State.Name==`stopped`]'
💡 Nota: En JMESPath, los valores literales van con backticks:
`running` no 'running'
🚀 Queries Prácticas (Casos Reales)
EC2: Instancias con Tags
# Get Name tag value
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,Tags[?Key==`Name`].Value|[0]]'
# Filter by Environment tag
aws ec2 describe-instances --query 'Reservations[].Instances[?Tags[?Key==`Environment` && Value==`production`]]'
# Get all instances WITHOUT a specific tag
aws ec2 describe-instances --query 'Reservations[].Instances[?!Tags[?Key==`Owner`]]'
EBS: Volúmenes No Adjuntos
# Unattached volumes
aws ec2 describe-volumes --query 'Volumes[?State==`available`].[VolumeId,Size,VolumeType,CreateTime]'
# Volumes older than X (requires external date comparison)
aws ec2 describe-volumes --query 'Volumes[?State==`available`]|[?CreateTime<`2024-01-01`]'
S3: Buckets con Tamaño
# List buckets with creation date
aws s3api list-buckets --query 'Buckets[].[Name,CreationDate]' --output table
# Get bucket sizes (requires CloudWatch)
aws cloudwatch get-metric-statistics \
--namespace AWS/S3 \
--metric-name BucketSizeBytes \
--dimensions Name=BucketName,Value=my-bucket Name=StorageType,Value=StandardStorage \
--start-time 2024-12-01T00:00:00Z \
--end-time 2024-12-30T23:59:59Z \
--period 86400 \
--statistics Average \
--query 'Datapoints|[0].Average'
IAM: Usuarios sin MFA
# Get users without MFA enabled
aws iam get-credential-report --query 'Content' --output text | base64 -d | \
awk -F, '$4=="false" {print $1}'
# Using JMESPath (if data is JSON)
aws iam list-users --query 'Users[?!MfaActive].[UserName,CreateDate]'
RDS: Instancias Públicamente Accesibles
# Find public RDS instances
aws rds describe-db-instances --query 'DBInstances[?PubliclyAccessible==`true`].[DBInstanceIdentifier,Engine,DBInstanceClass]'
🔥 Queries Avanzadas
1. sort_by() - Ordenar Resultados
# Sort instances by launch time (newest first)
aws ec2 describe-instances --query 'reverse(sort_by(Reservations[].Instances[], &LaunchTime))[].[InstanceId,LaunchTime]'
# Sort by name tag
aws ec2 describe-instances --query 'sort_by(Reservations[].Instances[], &Tags[?Key==`Name`].Value|[0])[]'
2. length() - Contar Elementos
# Count running instances
aws ec2 describe-instances --query 'length(Reservations[].Instances[?State.Name==`running`][])'
# Count security groups
aws ec2 describe-security-groups --query 'length(SecurityGroups)'
3. contains() - Buscar Strings
# Instances with "prod" in name
aws ec2 describe-instances --query 'Reservations[].Instances[?contains(Tags[?Key==`Name`].Value|[0], `prod`)]'
# Security groups with port 22 open
aws ec2 describe-security-groups --query 'SecurityGroups[?IpPermissions[?contains(ToPort, `22`)]]'
4. max_by() / min_by() - Máximo/Mínimo
# Largest EBS volume
aws ec2 describe-volumes --query 'max_by(Volumes, &Size).[VolumeId,Size]'
# Most recently launched instance
aws ec2 describe-instances --query 'max_by(Reservations[].Instances[], &LaunchTime).[InstanceId,LaunchTime]'
5. Nested Projections
# Complex nested query
aws ec2 describe-instances --query 'Reservations[].{
InstanceId: Instances[0].InstanceId,
Name: Instances[0].Tags[?Key==`Name`].Value|[0],
Type: Instances[0].InstanceType,
State: Instances[0].State.Name,
SecurityGroups: Instances[0].SecurityGroups[].GroupName,
PrivateIP: Instances[0].PrivateIpAddress
}'
6. Pipe Expressions (Procesamiento en Cadena)
# Filter, then sort, then project
aws ec2 describe-instances \
--query 'Reservations[].Instances[?State.Name==`running`] | [].{
ID: InstanceId,
Type: InstanceType
} | sort_by(@, &Type)'
📊 Formato de Output
JSON (default)
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,State.Name]' --output json
Table (legible)
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId,InstanceType,State.Name]' --output table
# Output:
---------------------------------------------------------
| DescribeInstances |
+-------------------+--------------+--------------------+
| i-1234567890 | t3.micro | running |
| i-0987654321 | t3.small | stopped |
+-------------------+--------------+--------------------+
Text (para scripts)
aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId' --output text
# Output (tab-separated):
i-1234567890 i-0987654321
🛠️ Testing JMESPath Queries
jpterm (Interactive)
# Install
pip install jmespath-terminal
# Use
aws ec2 describe-instances | jpterm
# Interactively test queries with autocomplete
jmespath.org (Online)
Visita jmespath.org para testear queries con tu propio JSON.
Python (Scripting)
import json
import jmespath
data = json.loads(aws_output)
result = jmespath.search('Reservations[].Instances[].InstanceId', data)
print(result)
💡 Tips y Trucos
1. Flatten Arrays con []
# Con [*] (nested arrays)
[
["i-111"],
["i-222"]
]
# Con [] (flattened)
[
"i-111",
"i-222"
]
2. Default Values con ||
# Si Name tag no existe, usa "N/A"
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId, Tags[?Key==`Name`].Value|[0] || `N/A`]'
3. First Element con [0]
# Get first running instance
aws ec2 describe-instances --query 'Reservations[].Instances[?State.Name==`running`][0].InstanceId'
4. Combining Filters
# Running t3.micro instances in production
aws ec2 describe-instances --query 'Reservations[].Instances[?
State.Name==`running` &&
InstanceType==`t3.micro` &&
Tags[?Key==`Environment` && Value==`production`]
]'
📚 Cheat Sheet
| Operation | Syntax | Example |
|---|---|---|
| Field access | . |
Users[0].Name |
| Array index | [0] |
Users[0] |
| Wildcard | [*] |
Users[*].Name |
| Flatten | [] |
Users[].Name |
| Filter | [?expr] |
Users[?Age>`25`] |
| Projection | {key:value} |
{Name:Name,Age:Age} |
| Pipe | | |
Users[] | [0] |
| Sort | sort_by() |
sort_by(Users, &Age) |
| Length | length() |
length(Users) |
| Max/Min | max_by() |
max_by(Users, &Age) |
🎯 Caso Real: Script Completo
#!/bin/bash
# ec2-report.sh - Comprehensive EC2 report using JMESPath
set -euo pipefail
echo "=== EC2 Instances Report ==="
echo ""
# Running instances count
running=$(aws ec2 describe-instances \
--query 'length(Reservations[].Instances[?State.Name==`running`][])' \
--output text)
echo "Running instances: $running"
# Stopped instances count
stopped=$(aws ec2 describe-instances \
--query 'length(Reservations[].Instances[?State.Name==`stopped`][])' \
--output text)
echo "Stopped instances: $stopped"
echo ""
echo "=== By Instance Type ==="
aws ec2 describe-instances \
--query 'Reservations[].Instances[].[InstanceType] | [*] | group_by(@, &[0])' \
--output table
echo ""
echo "=== Production Instances ==="
aws ec2 describe-instances \
--query 'Reservations[].Instances[?Tags[?Key==`Environment` && Value==`production`]].[
InstanceId,
InstanceType,
State.Name,
Tags[?Key==`Name`].Value|[0],
PrivateIpAddress
]' \
--output table
echo ""
echo "=== Instances Without Name Tag ==="
aws ec2 describe-instances \
--query 'Reservations[].Instances[?!Tags[?Key==`Name`]].[InstanceId,InstanceType]' \
--output table
✅ Resultado: Un reporte completo de EC2 con una sola herramienta (AWS CLI + JMESPath), sin necesidad de jq, awk o Python.
📚 Recursos
- JMESPath Official Site - Playground y docs
- JMESPath Tutorial
- AWS CLI Filtering
💭 Conclusión
JMESPath transforma AWS CLI de herramienta de consulta a lenguaje de automatización. Invertir tiempo en aprender JMESPath paga dividendos inmediatos: scripts más simples, menos dependencias (adiós jq/awk), y outputs exactamente como los necesitas.
Mi consejo: practica con jpterm hasta que las queries complejas se vuelvan segunda naturaleza. Es una habilidad que usarás todos los días.