En la lección anterior recorrimos la consola y creamos nuestro primer bucket con el ratón. Funcionó, pero dejamos una pregunta abierta: ¿cómo repites eso mismo cien veces, o cómo demuestras dentro de seis meses qué hiciste exactamente? La respuesta es dejar de hacer clic y empezar a escribir comandos y código.
Esta lección es la puerta de entrada a todo el trabajo serio en AWS. La AWS CLI es la herramienta que usarás a diario para consultar, crear y diagnosticar; los SDK son las bibliotecas con las que tu aplicación habla con AWS desde dentro. Ambos usan las mismas APIs que la consola, así que todo lo que aprendas aquí te sirve para cualquier servicio del resto del curso.
Contenido
- Por qué automatizar: el problema de la consola
- Instalar AWS CLI v2 en Linux, macOS y Windows
- Anatomía de un comando
- Configurar credenciales:
aws configure, ficheros y perfiles - Orden de precedencia de las credenciales
- Seguridad de las claves de acceso
- Formatos de salida y filtrado con
--query - Paginación,
--dry-runy otras opciones útiles - Comandos de exploración imprescindibles
- SDKs: qué son y qué lenguajes hay
- boto3: el SDK de Python paso a paso
- Un vistazo equivalente en JavaScript
- CloudShell como alternativa sin instalación
Por qué automatizar: el problema de la consola
Marta creó el bucket de pruebas en la consola. Ahora Luis necesita crear tres buckets iguales (uno por entorno) con las mismas etiquetas y la misma configuración. Con la consola tendría que repetir unos quince clics tres veces, y cualquier despiste produciría una diferencia silenciosa entre entornos.
Los cuatro problemas estructurales de la consola:
| Problema | Consecuencia real en MercadoFresco |
|---|---|
| No es reproducible | El entorno de desarrollo nunca acaba siendo idéntico al de producción, y los errores "solo pasan en producción" |
| No es documentable | Nadie puede revisar en Git lo que se hizo con el ratón, ni saber por qué |
| No es componible | No puedes encadenar "lista todas las instancias paradas y etiquétalas" |
| No escala | Etiquetar 200 recursos a mano es inviable |
La CLI resuelve los cuatro: un comando es texto, y el texto se guarda, se versiona, se revisa, se repite y se encadena. Y es el paso previo natural hacia la infraestructura como código del módulo 9, donde ya ni siquiera escribiremos comandos sino declaraciones del estado deseado.
Instalar AWS CLI v2 en Linux, macOS y Windows
Usa siempre la versión 2. La v1 está en mantenimiento, se instalaba con pip (lo que causaba
conflictos de dependencias) y le faltan funciones importantes como el inicio de sesión con IAM
Identity Center.
Linux (x86_64)
# 1. Descarga el instalador oficial en un fichero zip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
# 2. Descomprime el paquete en el directorio actual
unzip awscliv2.zip
# 3. Ejecuta el instalador con permisos de administrador
sudo ./aws/install
# 4. Limpia los ficheros temporales
rm -rf awscliv2.zip aws/Qué hace cada línea:
curl ... -o "awscliv2.zip": descarga el paquete y lo guarda con ese nombre. La URL es la oficial de AWS; desconfía de instaladores de terceros.unzip: extrae una carpetaaws/con el instalador.sudo ./aws/install: copia los binarios a/usr/local/aws-cliy crea el enlace/usr/local/bin/aws. Necesitasudoporque escribe fuera de tu directorio personal.rm -rf: borra lo descargado, que ya no hace falta.
Si tu máquina es ARM (por ejemplo, una instancia Graviton), cambia x86_64 por aarch64.
Para actualizar una instalación existente, añade los parámetros de actualización:
macOS
# Descarga el paquete .pkg oficial
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
# Instala para todos los usuarios del equipo
sudo installer -pkg AWSCLIV2.pkg -target /Alternativa con Homebrew, si ya lo usas: brew install awscli.
Windows
Descarga y ejecuta el instalador MSI oficial:
O desde PowerShell como administrador:
Después cierra y abre de nuevo la terminal para que se recargue la variable PATH.
Verificación
En cualquier sistema:
Salida esperada (los números variarán):
Se lee así: versión de la CLI, versión de Python embebida (no usa tu Python, va incluida), sistema
operativo y tipo de compilación. Si ves aws-cli/1.x, tienes la v1 y deberías desinstalarla antes
de continuar.
Anatomía de un comando
Todos los comandos siguen la misma estructura:
Un ejemplo real, línea a línea:
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--region eu-west-1 \
--output table| Parte | Qué es | Detalle |
|---|---|---|
aws |
El programa | — |
ec2 |
Servicio | Suele coincidir con el nombre del servicio: s3, iam, lambda, rds |
describe-instances |
Operación | La API se llama DescribeInstances; la CLI usa minúsculas y guiones |
--filters "..." |
Parámetro específico | Filtra en el servidor: solo instancias en ejecución |
--region |
Opción global | Sobrescribe la región del perfil para este comando |
--output table |
Opción global | Formato de salida |
Los verbos siguen convenciones muy predecibles, y reconocerlas acelera mucho el aprendizaje:
| Prefijo | Qué hace | Ejemplos |
|---|---|---|
describe- |
Devuelve información detallada | describe-instances, describe-vpcs |
list- |
Devuelve una lista de identificadores | list-buckets, list-functions |
get- |
Obtiene un elemento concreto | get-caller-identity, get-bucket-tagging |
create- |
Crea un recurso | create-bucket, create-tags |
delete- / terminate- |
Elimina | delete-bucket, terminate-instances |
put- |
Escribe o sobrescribe una configuración | put-bucket-tagging, put-metric-alarm |
La ayuda integrada
Es la mejor documentación que tienes y funciona sin conexión:
aws help # lista todos los servicios disponibles
aws s3api help # lista todas las operaciones de s3api
aws s3api create-bucket help # todos los parámetros de esa operación, con ejemplosSe abre en un paginador: navega con las flechas y sal con q.
Configurar credenciales: aws configure, ficheros y perfiles
La CLI necesita saber quién eres. Vamos a crear unas claves de acceso para el usuario
mercadofresco-admin que creamos en la lección 01-02.
Paso 1: crear las claves de acceso
En la consola: IAM → Users → mercadofresco-admin → Security credentials → Create access key. Elige el caso de uso Command Line Interface (CLI), confirma el aviso y crea.
Obtendrás dos valores:
- Access key ID: algo como
AKIAIOSFODNN7EXAMPLE. Es público-ish, identifica la clave. - Secret access key: algo como
wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY. Solo se muestra una vez. Si la pierdes, hay que crear una clave nueva.
Advertencia: nunca crees claves de acceso para el usuario root (lección 01-02). Y si no quieres manejar claves permanentes en absoluto, la alternativa moderna es
aws configure ssocon IAM Identity Center, que emite credenciales temporales.
Paso 2: aws configure
Te hará cuatro preguntas:
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY Default region name [None]: eu-west-1 Default output format [None]: json
- Región por defecto:
eu-west-1, la que decidimos en la lección 01-03. Te evita escribir--regionen cada comando. - Formato de salida:
jsones un buen valor por defecto porque es el que consumen otras herramientas.
Los ficheros de configuración
aws configure no hace magia: escribe dos ficheros de texto en tu directorio personal
(~/.aws/ en Linux y macOS, %USERPROFILE%\.aws\ en Windows).
~/.aws/credentials — contiene los secretos:
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY~/.aws/config — contiene los ajustes no secretos:
Puedes editarlos a mano perfectamente. Comprueba además que los permisos sean restrictivos:
Esto deja el fichero legible y escribible solo por tu usuario. En un equipo compartido es imprescindible.
Perfiles nombrados
Casi nunca trabajarás con una sola identidad. MercadoFresco tendrá pronto un entorno de desarrollo y otro de producción, y conviene que no se puedan confundir.
Crea un perfil nuevo:
Los ficheros quedan así:
# ~/.aws/credentials
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[mercadofresco-dev]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCoEXAMPLEKEY# ~/.aws/config
[default]
region = eu-west-1
output = json
[profile mercadofresco-dev]
region = eu-west-1
output = jsonAtención al detalle: en credentials la sección es [mercadofresco-dev], pero en config es
[profile mercadofresco-dev], con la palabra profile delante. Es una inconsistencia histórica que
provoca errores constantes. Si un perfil "no se encuentra", revisa esto primero.
Para usar un perfil:
# Opción 1: por parámetro, en un comando concreto
aws s3 ls --profile mercadofresco-dev
# Opción 2: por variable de entorno, para toda la sesión de terminal
export AWS_PROFILE=mercadofresco-dev
aws s3 lsY para saber siempre con quién estás actuando:
Adquiere el hábito de ejecutar este comando antes de cualquier operación destructiva. Es la diferencia entre borrar la base de datos de desarrollo y borrar la de producción.
Orden de precedencia de las credenciales
Cuando ejecutas un comando, la CLI busca credenciales en un orden fijo y se queda con la primera fuente que encuentre. Conocer este orden explica el 90 % de los "pero si yo configuré eso":
| # | Fuente | Cómo se indica | Cuándo se usa |
|---|---|---|---|
| 1 | Opciones de línea de comandos | --profile, --region |
Sobrescribe todo lo demás |
| 2 | Variables de entorno | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_PROFILE, AWS_DEFAULT_REGION |
CI/CD, contenedores, sesiones temporales |
| 3 | Fichero ~/.aws/credentials |
Perfil [default] o el indicado |
Trabajo diario en tu portátil |
| 4 | Fichero ~/.aws/config |
Sección [profile ...] |
Configuración y roles |
| 5 | Credenciales de contenedor | Variable inyectada por ECS/EKS | Contenedores en AWS (módulo 10) |
| 6 | Rol de instancia (IMDS) | Metadatos de la propia EC2 | La forma correcta dentro de AWS |
Los dos últimos son los importantes conceptualmente. Cuando tu código se ejecuta dentro de AWS —en una instancia EC2, en una función Lambda, en un contenedor— no necesita claves en absoluto: se le asigna un rol IAM y el SDK obtiene automáticamente credenciales temporales que se rotan solas cada pocas horas.
flowchart TB
A["Comando o SDK necesita credenciales"] --> B{"--profile en la linea?"}
B -->|si| USA1["Usa ese perfil"]
B -->|no| C{"Variables de entorno?"}
C -->|si| USA2["Usa las variables"]
C -->|no| D{"Fichero ~/.aws/credentials?"}
D -->|si| USA3["Usa el perfil default"]
D -->|no| E{"Corriendo dentro de AWS?"}
E -->|si| USA4["Rol de la instancia - credenciales temporales - LO IDEAL"]
E -->|no| F["Error: Unable to locate credentials"]
Los roles IAM se estudian a fondo en la lección 04-01. Quédate con la idea: claves permanentes solo en tu portátil; dentro de AWS, siempre roles.
Seguridad de las claves de acceso
Una clave de acceso de mercadofresco-admin es, literalmente, el control total de la cuenta. Estas
reglas no son opcionales.
Nunca en el código ni en Git
Este es el error que arruina cuentas todos los días. Hay bots que escanean GitHub de forma continua
buscando cadenas que empiecen por AKIA; una clave publicada se explota en minutos, típicamente
lanzando decenas de instancias grandes para minar criptomonedas. La factura resultante puede ser de
miles de euros.
Protección mínima en cualquier repositorio:
Y una barrera automática, muy recomendable:
# git-secrets escanea cada commit buscando patrones de credenciales de AWS
git secrets --install
git secrets --register-awsSi alguna vez publicas una clave por error: desactívala y bórrala inmediatamente en IAM, revisa CloudTrail (lección 05-03) para ver qué se hizo con ella, y crea una nueva. Borrar el commit no sirve de nada: ya está indexado.
Otras reglas
| Regla | Por qué |
|---|---|
| Rotar cada 90 días | Reduce la ventana de explotación de una clave filtrada |
| Una clave por persona y uso | Permite revocar una sin afectar a las demás |
| Eliminar las claves inactivas | IAM muestra la fecha de último uso; lo que no se usa, se borra |
| Nunca claves en el root | Ya visto en 01-02 |
| Dentro de AWS, roles en vez de claves | Credenciales temporales, rotadas automáticamente |
| MFA para operaciones sensibles | Se puede exigir en las políticas IAM (lección 04-01) |
El procedimiento de rotación sin cortes es sencillo: crea una segunda clave, actualiza donde se use, comprueba que todo funciona, desactiva la antigua, espera unos días y bórrala. IAM permite dos claves activas por usuario precisamente para esto.
Formatos de salida y filtrado con --query
Los comandos de AWS devuelven mucha información. Aprender a recortarla es lo que convierte la CLI en una herramienta cómoda.
Formatos de salida
| Formato | Cuándo usarlo |
|---|---|
json |
Por defecto. Ideal para encadenar con jq o para procesar en scripts |
table |
Para leer con los ojos: bordes ASCII y columnas |
text |
Para procesar con grep, awk o cut; separado por tabuladores |
yaml |
Más legible que JSON para configuraciones largas |
--query con JMESPath
--query aplica una expresión JMESPath sobre la respuesta, en el cliente. Vamos de menos a
más.
Nivel 1 — extraer una lista:
Nivel 2 — un campo de cada elemento:
# De cada región, solo su nombre. Los corchetes [] recorren la lista
aws ec2 describe-regions --query 'Regions[].RegionName' --output textSalida:
Nivel 3 — renombrar campos en un objeto:
aws ec2 describe-regions \
--query 'Regions[].{Region:RegionName, Endpoint:Endpoint}' \
--output tableLas llaves {...} construyen un objeto nuevo; a la izquierda del : va el nombre que quieres ver,
a la derecha el campo original.
Nivel 4 — filtrar con una condición:
# Solo las regiones europeas: ? introduce el filtro, contains() es una función JMESPath
aws ec2 describe-regions \
--query 'Regions[?contains(RegionName, `eu-`)].RegionName' \
--output tableFíjate en las comillas invertidas alrededor de eu-: en JMESPath, los literales van entre
backticks, no entre comillas normales. Es una fuente frecuente de errores.
Nivel 5 — un caso real de MercadoFresco:
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{
Id:InstanceId,
Tipo:InstanceType,
Estado:State.Name,
IP:PrivateIpAddress,
Zona:Placement.AvailabilityZone,
Nombre:Tags[?Key==`Name`]|[0].Value
}' \
--output tableDesglose:
Reservations[].Instances[]: aplana la estructura anidada de EC2 (las instancias vienen agrupadas por reserva) en una sola lista.State.Name: navega dentro de un objeto anidado con el punto.Tags[?Key==\Name`]|[0].Value: filtra las etiquetas quedándose con la de claveName, y el|[0]` toma el primer resultado de esa lista para extraer su valor.
--query frente a --filters
--filters |
--query |
|
|---|---|---|
| Dónde se aplica | En el servidor de AWS | En tu equipo, tras recibir la respuesta |
| Efecto | Reduce lo que se transfiere | Solo reduce lo que se muestra |
| Disponibilidad | Solo en algunos servicios y campos | En cualquier comando |
| Recomendación | Úsalo siempre que exista | Para dar forma al resultado |
Lo ideal es combinarlos: --filters para traer poco, --query para presentarlo bien.
Paginación, --dry-run y otras opciones útiles
Paginación
Cuando hay muchos resultados, la CLI v2 los pagina automáticamente y abre un paginador. Opciones para controlarlo:
# Devuelve como máximo 5 elementos
aws s3api list-buckets --max-items 5
# Desactiva el paginador interactivo (útil en scripts)
aws ec2 describe-instances --no-cli-pager
# Desactivarlo de forma permanente
export AWS_PAGER=""Si limitas con --max-items y hay más resultados, la respuesta incluye un NextToken que puedes
pasar con --starting-token para pedir la página siguiente.
--dry-run
Muchas operaciones de EC2 aceptan --dry-run: comprueban si tendrías permisos para hacer la
operación, pero no la ejecutan.
Si tienes permiso, verás:
An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set
Ese mensaje, aunque diga "error", es la confirmación de éxito. Si no tuvieras permiso, el error
sería UnauthorizedOperation. Es una forma segura de verificar permisos sin crear nada ni gastar
nada.
Otras opciones que se usan mucho
| Opción | Para qué |
|---|---|
--no-cli-pager |
Salida directa sin paginador |
--debug |
Traza completa de la petición HTTP; imprescindible para diagnosticar |
--cli-input-json file://params.json |
Pasar todos los parámetros desde un fichero |
--generate-cli-skeleton |
Generar la plantilla JSON de parámetros de una operación |
--endpoint-url |
Apuntar a un endpoint alternativo (p. ej. un emulador local) |
Comandos de exploración imprescindibles
Estos son los comandos que ejecutarás cientos de veces. Guárdalos.
# 2. ¿Qué regiones existen y están habilitadas en mi cuenta?
aws ec2 describe-regions --query 'Regions[].RegionName' --output text# 3. ¿Qué zonas de disponibilidad tiene mi región, con su ID físico?
aws ec2 describe-availability-zones \
--query 'AvailabilityZones[].{AZ:ZoneName, Id:ZoneId, Estado:State}' \
--output table# 5. ¿Qué instancias EC2 tengo y en qué estado?
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id:InstanceId, Estado:State.Name}' \
--output tableLa salida del último es especialmente útil porque indica el origen de cada valor:
Name Value Type Location
---- ----- ---- --------
profile mercadofresco-dev env AWS_PROFILE
access_key ****************MPLE shared-credentials-file
region eu-west-1 config-file ~/.aws/configCuando algo no funcione como esperas, este comando te dirá exactamente qué credencial y qué región está usando la CLI y por qué.
SDKs: qué son y qué lenguajes hay
Un SDK (Software Development Kit) es una biblioteca que permite a tu aplicación llamar a las APIs de AWS desde el lenguaje en el que está escrita, sin construir peticiones HTTP firmadas a mano.
La diferencia con la CLI:
| CLI | SDK | |
|---|---|---|
| Quién lo usa | Una persona en una terminal, o un script | Tu aplicación, en tiempo de ejecución |
| Caso típico | "Lista los buckets", "para esa instancia" | "Cuando el cliente sube una foto, guárdala en S3" |
| Formato | Comandos de texto | Código en tu lenguaje |
Lenguajes con SDK oficial:
| Lenguaje | Nombre del SDK | Nota |
|---|---|---|
| Python | boto3 | El más usado en scripting y automatización |
| JavaScript / TypeScript | AWS SDK for JavaScript v3 | Modular: instalas solo el cliente que usas |
| Java | AWS SDK for Java 2.x | Muy común en empresa |
| .NET (C#) | AWS SDK for .NET | — |
| Go | AWS SDK for Go v2 | Habitual en herramientas de infraestructura |
| PHP | AWS SDK for PHP | Relevante para el monolito actual de MercadoFresco |
| Rust, Ruby, C++, Kotlin, Swift | — | Disponibles |
Todos comparten la misma lógica: cadena de proveedores de credenciales (el mismo orden de precedencia que vimos), reintentos automáticos con retroceso exponencial, y nombres de operación equivalentes a los de la API.
boto3: el SDK de Python paso a paso
Luis va a usar Python para los scripts de MercadoFresco, así que veamos boto3 en detalle.
Instalación
# Crear un entorno virtual aislado (buena práctica: no instalar en el Python del sistema)
python3 -m venv venv
# Activarlo (en Windows: venv\Scripts\activate)
source venv/bin/activate
# Instalar boto3
pip install boto3Cliente frente a recurso
boto3 ofrece dos interfaces distintas para lo mismo:
| Client | Resource | |
|---|---|---|
| Nivel | Bajo: refleja la API 1:1 | Alto: orientado a objetos |
| Cobertura | Todos los servicios y operaciones | Solo algunos servicios; en mantenimiento |
| Estilo | s3.list_buckets() devuelve un diccionario |
for b in s3.buckets.all() |
| Recomendación | Úsalo por defecto | Solo si te lo encuentras en código heredado |
Ejemplo del mismo objetivo con ambos:
import boto3
# --- Interfaz Client (recomendada) ---
s3_client = boto3.client("s3")
respuesta = s3_client.list_buckets() # devuelve un diccionario tal cual lo da la API
for bucket in respuesta["Buckets"]:
print(bucket["Name"], bucket["CreationDate"])
# --- Interfaz Resource (más antigua) ---
s3_recurso = boto3.resource("s3")
for bucket in s3_recurso.buckets.all(): # objetos con atributos
print(bucket.name, bucket.creation_date)Credenciales y sesiones
boto3 usa exactamente el mismo orden de precedencia que la CLI, así que si aws s3 ls funciona,
tu código Python funcionará sin configurar nada.
Para elegir perfil o región explícitamente se usa una sesión:
import boto3
# Una sesión encapsula credenciales + región
sesion = boto3.Session(profile_name="mercadofresco-dev", region_name="eu-west-1")
s3 = sesion.client("s3")
print(sesion.client("sts").get_caller_identity()["Arn"])Manejo de errores con ClientError
Todo error devuelto por un servicio de AWS llega como botocore.exceptions.ClientError. Ignorarlo
es el error más común de los scripts de principiante.
import boto3
from botocore.exceptions import ClientError
s3 = boto3.client("s3", region_name="eu-west-1")
def existe_bucket(nombre: str) -> bool:
"""Comprueba si un bucket existe y es accesible para nuestras credenciales."""
try:
s3.head_bucket(Bucket=nombre) # petición ligera: solo cabeceras
return True
except ClientError as error:
# El código concreto está siempre en error.response["Error"]["Code"]
codigo = error.response["Error"]["Code"]
if codigo == "404":
print(f"El bucket '{nombre}' no existe.")
elif codigo == "403":
print(f"El bucket '{nombre}' existe pero no tienes permiso para verlo.")
else:
raise # cualquier otro error, que se propague
return False
print(existe_bucket("mercadofresco-fotos-producto"))Puntos clave del fragmento:
head_bucketes la forma barata de comprobar existencia: no descarga contenido.error.response["Error"]["Code"]es la ruta estándar para leer el código de error. Memorízala.- Distinguir 404 de 403 importa: "no existe" y "existe pero no puedes verlo" exigen acciones distintas.
- El
raisefinal evita el antipatrón de tragarse errores desconocidos, que oculta problemas reales.
Paginación en boto3
Las APIs devuelven como mucho unos cientos de elementos por llamada. Si iteras solo la primera respuesta, perderás datos en silencio. Los paginadores lo resuelven:
import boto3
s3 = boto3.client("s3", region_name="eu-west-1")
paginador = s3.get_paginator("list_objects_v2")
paginas = paginador.paginate(Bucket="mercadofresco-fotos-producto", Prefix="2026/")
total = 0
for pagina in paginas: # cada iteración es una llamada a la API
for objeto in pagina.get("Contents", []): # .get() evita KeyError si la página viene vacía
total += 1
print(f"Objetos encontrados: {total}")Un vistazo equivalente en JavaScript
Para que veas que los conceptos se trasladan, el mismo listado de buckets con el AWS SDK for JavaScript v3:
# El SDK v3 es modular: se instala solo el cliente del servicio que necesitas
npm install @aws-sdk/client-s3// Importa solo el cliente de S3 y el comando concreto que vamos a usar
import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3";
// El cliente lee credenciales con el mismo orden de precedencia que la CLI
const cliente = new S3Client({ region: "eu-west-1" });
async function listarBuckets() {
try {
// En el v3, cada operación es un objeto Command que se envía con send()
const respuesta = await cliente.send(new ListBucketsCommand({}));
respuesta.Buckets.forEach((b) => console.log(b.Name, b.CreationDate));
} catch (error) {
// El equivalente a ClientError: el código está en error.name
console.error("Error de AWS:", error.name, "-", error.message);
}
}
listarBuckets();Diferencias respecto a boto3: el v3 es modular (menos peso en Lambda) y usa el patrón
comando: creas un objeto XxxCommand y lo envías con send(). La lógica de credenciales,
reintentos y errores es equivalente.
CloudShell como alternativa sin instalación
Como vimos en la lección 01-04, AWS CloudShell te da una terminal en el navegador con la CLI v2, Python y boto3 ya instalados, y autenticada automáticamente con el usuario de la consola.
| CLI local | CloudShell | |
|---|---|---|
| Instalación | Sí | No |
| Credenciales | Configuras claves | Heredadas, temporales |
| Rendimiento y ficheros locales | Total | Limitado (1 GB persistente) |
| Sesión | Permanente | Caduca por inactividad |
| Ideal para | Trabajo diario, scripts, CI | Pruebas rápidas, emergencias, formación |
CloudShell es especialmente útil para el escenario "estoy en un ordenador que no es el mío y necesito diagnosticar algo ya". No sustituye a la CLI local para el trabajo diario, pero es una red de seguridad excelente.
Errores Comunes y Consejos
Unable to locate credentials. No has configurado el perfil, o has usado un nombre de perfil inexistente. Diagnostica conaws configure list.ProfileNotFoundcon el perfil bien escrito. Casi siempre es la sección de~/.aws/configsin el prefijoprofile: debe ser[profile mercadofresco-dev], no[mercadofresco-dev].You must specify a region. Ni el perfil ni la variable de entorno ni el comando indican región. Añade--regiono configúrala en el perfil.AccessDeniedinesperado. Comprueba primero con qué identidad estás actuando conaws sts get-caller-identity; muy a menudo es el perfil equivocado, no un problema de permisos.- Subir claves a Git. El desastre más caro y más frecuente.
.gitignore,git secretsy, mejor aún, credenciales temporales. - Usar claves permanentes dentro de una instancia EC2. Nunca. Se usa un rol de instancia (lección 04-01), que además rota las credenciales solo.
- Olvidar la paginación. Tu script dice "hay 1.000 objetos" cuando hay 50.000. Usa paginadores
en boto3 y
--max-items/NextTokenen la CLI. - Confundir
aws s3conaws s3api. El primero es de alto nivel (ls,cp,sync); el segundo expone la API completa (get-bucket-tagging,put-bucket-policy). Si una opción no existe ens3, búscala ens3api. - Ignorar
ClientError. Un script que falla en silencio es peor que uno que se rompe. - Consejo: define alias en tu terminal para lo que repitas mucho, por ejemplo
alias quien='aws sts get-caller-identity'. - Consejo: cuando un comando no funcione y no entiendas por qué, añade
--debug. Verás la petición firmada completa y la respuesta del servicio.
Ejercicios
Ejercicio 1: instalación y exploración
- Instala AWS CLI v2 en tu sistema y verifica la versión.
- Configura un perfil llamado
mercadofresco-admincon regióneu-west-1y salidajson. - Ejecuta
aws sts get-caller-identityy anota tu ARN completo. - Escribe un solo comando que muestre, en formato tabla, todas las regiones cuyo nombre empiece
por
eu-, con una única columna llamadaRegion.
Ejercicio 2: script bash de inventario
Escribe un script inventario-mercadofresco.sh que:
- Compruebe con qué identidad y en qué cuenta se está ejecutando, y lo muestre.
- Liste todos los buckets de S3 de la cuenta.
- Liste las zonas de disponibilidad de
eu-west-1con su ID físico. - Liste las instancias EC2 con su ID, tipo, estado y zona (si no hay ninguna, debe indicarlo sin fallar).
- Todo con salida en tabla y sin abrir el paginador interactivo.
Ejercicio 3: script boto3 que crea y etiqueta un bucket
Escribe un script Python crear_bucket_mercadofresco.py que:
- Reciba el nombre del bucket como argumento de línea de comandos.
- Compruebe si ya existe (distinguiendo "no existe" de "sin permisos").
- Si no existe, lo cree en
eu-west-1con el bloqueo de acceso público activado. - Le aplique las etiquetas
Proyecto=mercadofresco,Entorno=pruebas,Componente=formacion. - Muestre las etiquetas resultantes.
- Maneje los errores con
ClientErrory devuelva un código de salida distinto de cero si falla.
Coste: un bucket vacío no genera coste apreciable y la capa gratuita cubre 5 GB. Aun así, bórralo al terminar con
aws s3 rb s3://<nombre-del-bucket>.
Soluciones
Solución 1
# 1. Instalación en Linux y verificación
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip && sudo ./aws/install
aws --version
# 2. Perfil
aws configure --profile mercadofresco-admin
# Región: eu-west-1 ; Salida: json
# 3. Identidad
aws sts get-caller-identity --profile mercadofresco-admin
# 4. Regiones europeas en tabla, columna única llamada Region
aws ec2 describe-regions \
--query 'Regions[?starts_with(RegionName, `eu-`)].{Region:RegionName}' \
--output tableSe puede usar starts_with() o contains(); starts_with es más preciso porque contains
también aceptaría un nombre que llevara eu- en el medio.
Solución 2
#!/usr/bin/env bash
# inventario-mercadofresco.sh - inventario rápido de la cuenta de AWS
set -euo pipefail # aborta ante error, variable no definida o fallo en una tubería
PERFIL="${1:-mercadofresco-admin}" # primer argumento o valor por defecto
REGION="eu-west-1"
export AWS_PAGER="" # desactiva el paginador interactivo
echo "===== IDENTIDAD ====="
aws sts get-caller-identity \
--profile "$PERFIL" \
--query '{Cuenta:Account, Identidad:Arn}' \
--output table
echo "===== BUCKETS S3 ====="
aws s3api list-buckets \
--profile "$PERFIL" \
--query 'Buckets[].{Nombre:Name, Creado:CreationDate}' \
--output table
echo "===== ZONAS DE DISPONIBILIDAD DE $REGION ====="
aws ec2 describe-availability-zones \
--profile "$PERFIL" --region "$REGION" \
--query 'AvailabilityZones[].{AZ:ZoneName, IdFisico:ZoneId, Estado:State}' \
--output table
echo "===== INSTANCIAS EC2 ====="
INSTANCIAS=$(aws ec2 describe-instances \
--profile "$PERFIL" --region "$REGION" \
--query 'Reservations[].Instances[].{Id:InstanceId, Tipo:InstanceType, Estado:State.Name, Zona:Placement.AvailabilityZone}' \
--output table)
if [ -z "$INSTANCIAS" ]; then
echo "(no hay instancias EC2 en $REGION)"
else
echo "$INSTANCIAS"
fi
echo "===== FIN DEL INVENTARIO ====="Detalles importantes:
set -euo pipefailevita que el script continúe alegremente tras un fallo.export AWS_PAGER=""es imprescindible en scripts: sin ello, la CLI abriría un paginador y el script se quedaría bloqueado esperando a que alguien pulseq.- Guardar la salida en una variable permite comprobar si está vacía y dar un mensaje claro en lugar de imprimir una tabla vacía.
Se ejecuta con:
Solución 3
#!/usr/bin/env python3
"""crear_bucket_mercadofresco.py - crea y etiqueta un bucket de S3 para MercadoFresco."""
import sys
import boto3
from botocore.exceptions import ClientError
REGION = "eu-west-1"
ETIQUETAS = [
{"Key": "Proyecto", "Value": "mercadofresco"},
{"Key": "Entorno", "Value": "pruebas"},
{"Key": "Componente", "Value": "formacion"},
]
def existe_bucket(s3, nombre: str) -> bool:
"""True si el bucket existe y es accesible; False si no existe."""
try:
s3.head_bucket(Bucket=nombre)
return True
except ClientError as error:
codigo = error.response["Error"]["Code"]
if codigo in ("404", "NoSuchBucket"):
return False
if codigo == "403":
print(f"El bucket '{nombre}' existe pero pertenece a otra cuenta.")
sys.exit(1)
raise
def crear_bucket(s3, nombre: str) -> None:
"""Crea el bucket en REGION con el acceso público bloqueado."""
# Fuera de us-east-1 hay que indicar LocationConstraint explícitamente
s3.create_bucket(
Bucket=nombre,
CreateBucketConfiguration={"LocationConstraint": REGION},
)
print(f"Bucket '{nombre}' creado en {REGION}.")
# Bloqueo total de acceso público: las cuatro opciones a True
s3.put_public_access_block(
Bucket=nombre,
PublicAccessBlockConfiguration={
"BlockPublicAcls": True,
"IgnorePublicAcls": True,
"BlockPublicPolicy": True,
"RestrictPublicBuckets": True,
},
)
print("Acceso público bloqueado.")
def etiquetar(s3, nombre: str) -> None:
"""Aplica el conjunto de etiquetas (sobrescribe las existentes)."""
s3.put_bucket_tagging(Bucket=nombre, Tagging={"TagSet": ETIQUETAS})
print("Etiquetas aplicadas.")
def main() -> int:
if len(sys.argv) != 2:
print("Uso: python crear_bucket_mercadofresco.py <nombre-del-bucket>")
return 1
nombre = sys.argv[1]
s3 = boto3.client("s3", region_name=REGION)
try:
if existe_bucket(s3, nombre):
print(f"El bucket '{nombre}' ya existe en tu cuenta; no se crea de nuevo.")
else:
crear_bucket(s3, nombre)
etiquetar(s3, nombre)
actuales = s3.get_bucket_tagging(Bucket=nombre)["TagSet"]
print("\nEtiquetas actuales del bucket:")
for etiqueta in actuales:
print(f" {etiqueta['Key']} = {etiqueta['Value']}")
except ClientError as error:
print(f"Error de AWS: {error.response['Error']['Code']} - "
f"{error.response['Error']['Message']}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())Claves de la solución:
LocationConstraint: si creas un bucket en cualquier región que no seaus-east-1y omites este parámetro, S3 lo crea enus-east-1. Es una de las rarezas históricas de la API.put_public_access_blockcon las cuatro opciones aTruees la protección que evita la fuga de datos más habitual en AWS.put_bucket_taggingsobrescribe el conjunto completo de etiquetas: si quisieras añadir una sin borrar las demás, tendrías que leerlas primero conget_bucket_taggingy fusionarlas.- Códigos de salida:
0si todo va bien,1si falla, para poder encadenar el script en un pipeline.
Ejecución y limpieza:
python crear_bucket_mercadofresco.py mercadofresco-pruebas-cli-jc-2741
# Limpieza obligatoria al terminar (rb = remove bucket; debe estar vacío)
aws s3 rb s3://mercadofresco-pruebas-cli-jc-2741Conclusión
Con esta lección cierras el módulo de introducción y, sobre todo, das el salto de "usuario de AWS" a
"operador de AWS". Has instalado la AWS CLI v2, has entendido la anatomía de un comando
(aws <servicio> <operación> --parámetros) y las convenciones de nombres que te permiten adivinar
comandos que aún no conoces. Has configurado credenciales con aws configure, has visto qué hay
realmente dentro de ~/.aws/credentials y ~/.aws/config, y sabes trabajar con perfiles
nombrados para no confundir jamás desarrollo con producción. Conoces el orden de precedencia de
las credenciales, que explica casi todos los comportamientos extraños, y las reglas de seguridad
innegociables: nunca claves en Git, rotación periódica y, dentro de AWS, roles en lugar de claves.
Has aprendido a domar la salida de los comandos con --output y con --query y JMESPath, a evitar
la trampa silenciosa de la paginación, a verificar permisos sin gastar nada con --dry-run, y
tienes en la mano el puñado de comandos de exploración que usarás cada día. Y has dado tus primeros
pasos con los SDK: boto3 en Python, con la distinción entre client y resource, el manejo
correcto de errores mediante ClientError y los paginadores, más el equivalente en JavaScript para
ver que los conceptos son universales.
MercadoFresco ya tiene todo lo necesario para empezar de verdad: una cuenta segura y vigilada, una región elegida con criterio, una consola que sabes manejar y unas herramientas de línea de comandos y programación que hacen el trabajo repetible.
En el módulo 2, «Servicios principales de AWS», empezando por la lección 02-01 «Amazon EC2», levantaremos el primer servidor real de MercadoFresco en la nube y comenzaremos la migración que resolverá, uno a uno, los cuatro problemas que descubrimos en la primera lección.
Curso de AWS
Módulo 1: Introducción a AWS
- ¿Qué es AWS?
- Configuración de tu cuenta de AWS
- Infraestructura global de AWS
- Consola de administración de AWS
- AWS CLI y SDKs
Módulo 2: Servicios principales de AWS
Módulo 3: Redes y entrega de contenido
- Amazon VPC
- Grupos de seguridad y listas de control de acceso
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Módulo 4: Seguridad e identidad
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager y Parameter Store
- AWS Shield
- AWS WAF
Módulo 5: Monitorización y gestión
- Amazon CloudWatch
- AWS X-Ray y trazabilidad distribuida
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Módulo 6: Bases de datos
- Cómo elegir la base de datos adecuada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Módulo 7: Integración de aplicaciones
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrones de integración: idempotencia, reintentos y colas de mensajes fallidos
