Tema
📙 Clase 1 — Infraestructura como código con CloudFormation
Curso AWS (emulador Floci) · 2026-08-08 · Carpeta:
02-Ejercicios/clase-01-cloudformation⬅️ Volver al índice de clases
🎯 Qué aprendí
- Qué es una pila (stack) y por qué agrupar recursos cambia la forma de trabajar.
- Escribir plantillas YAML:
Resources,Outputs,Parameters,Mappings. - Las funciones intrínsecas que dan vida a una plantilla:
!Ref,!GetAtt,!Sub,!Join,!FindInMap. - El ciclo completo: crear → actualizar → eliminar, y cómo mirar por dentro con
describe-stack-events. - Compartir valores entre pilas con
Export/!ImportValue. - Dos cosas que el emulador NO hace igual que AWS — y por qué conviene saberlo desde el principio.
📖 PARTE TEÓRICA
🧱 1. El problema: infraestructura hecha a mano
Hasta ahora, cada recurso se crea con su propio comando:
bash
aws s3 mb s3://informes # un bucket
aws sqs create-queue --queue-name avisos # una cola
aws dynamodb create-table ... # una tablaFunciona, pero tiene tres agujeros:
- No queda escrito en ningún sitio. Dentro de tres meses, ¿quién recuerda el orden y los nombres?
- No se puede repetir. Montar lo mismo en otro entorno es volver a teclearlo todo, y equivocarse en algo.
- Nadie sabe qué va junto. Si borras el bucket, ¿había algo que dependía de él?
Infraestructura como código resuelve las tres: describes lo que quieres en un archivo, y ese archivo es la fuente de verdad.
A MANO COMO CÓDIGO
─────── ───────────
comando 1 ─┐ plantilla.yaml
comando 2 ─┼─ ¿en qué orden? │
comando 3 ─┘ ¿con qué nombres? ▼
┌──────────┐
✗ no versionable │ PILA │ crea todo, en orden
✗ no repetible └──────────┘ y sabe qué va junto
✗ no se sabe qué va junto │
✓ un archivo en git
✓ mismo resultado siempre
✓ se borra entero de una vez📄 2. Anatomía de una plantilla
Una plantilla es un YAML con secciones. La única obligatoria es Resources:
yaml
AWSTemplateFormatVersion: '2010-09-09' # versión del formato (siempre esta)
Description: Mi primera pila # texto libre, opcional
Parameters: # valores que se pasan al crear la pila
NombreBucket:
Type: String
Default: mi-bucket
Mappings: # tablas de consulta fijas
PorRegion:
us-east-1: { Sufijo: este }
Resources: # ← LO ÚNICO OBLIGATORIO
MiBucket: # nombre lógico (lo eliges tú)
Type: AWS::S3::Bucket # tipo de recurso
Properties: # su configuración
BucketName: !Ref NombreBucket
Outputs: # valores que la pila devuelve
Nombre:
Value: !Ref MiBucket💡 Nombre lógico vs nombre físico.
MiBucketes el nombre lógico: solo existe dentro de la plantilla, para referirte a él. El nombre físico (mi-bucket) es el real en AWS. Esa separación es la que permite que!Reffuncione.
🔗 3. Funciones intrínsecas — lo que hace viva a una plantilla
Sin ellas, una plantilla sería una lista de recursos sueltos. Con ellas, los recursos se conocen entre sí.
| Función | Qué devuelve | Ejemplo |
|---|---|---|
!Ref | El identificador principal del recurso | !Ref MiBucket → mi-bucket |
!GetAtt | Un atributo concreto | !GetAtt MiCola.Arn → arn:aws:sqs:... |
!Sub | Texto con sustituciones | !Sub 'app-${AWS::Region}' → app-us-east-1 |
!Join | Une trozos con un separador | !Join ['-', ['app', 'prod']] → app-prod |
!FindInMap | Busca en Mappings | !FindInMap [PorRegion, us-east-1, Sufijo] → este |
Y los pseudo-parámetros, que existen siempre sin declararlos:
| Pseudo-parámetro | Valor |
|---|---|
AWS::Region | us-east-1 |
AWS::AccountId | 000000000000 (en el emulador) |
AWS::StackName | El nombre de tu pila |
🧪 Pregunta de entrevista: ¿diferencia entre
!Refy!GetAtt?!Refdevuelve el identificador por defecto del recurso (el nombre en un bucket, la URL en una cola).!GetAttdevuelve un atributo concreto que tú eliges (.Arn,.DomainName). Si necesitas el ARN, casi siempre es!GetAtt.
🔄 4. El ciclo de vida de una pila
create-stack update-stack delete-stack
│ │ │
▼ ▼ ▼
CREATE_COMPLETE ──▶ UPDATE_COMPLETE ──▶ DELETE_COMPLETE
│ │ │
└── crea todos └── calcula la └── borra TODO
los recursos diferencia y lo que creó
aplica solo esoLo importante de delete-stack: se lleva por delante todo lo que la pila creó. Es su mayor virtud (nada olvidado cobrando) y su mayor peligro (un borrado accidental se lleva más de lo que crees).
⚠️ Una pila solo gestiona lo que ella creó. Si creaste un bucket a mano con
aws s3 mby luego escribes una plantilla con ese mismo nombre, no lo "adopta": intentará crearlo y chocará porque ya existe.
🧭 5. Compartir valores entre pilas
Una pila puede publicar un valor para que otra lo use:
yaml
# Pila A — publica
Outputs:
ColaArn:
Value: !GetAtt MiCola.Arn
Export:
Name: arn-de-mi-cola # nombre único en toda la cuenta
# Pila B — consume
Outputs:
Heredado:
Value: !ImportValue arn-de-mi-cola📌 Mientras una pila importe un valor exportado, la pila que lo exporta no se puede borrar. Es una protección deliberada, no un fallo.
⚠️ 6. Dos cosas que el emulador NO hace como AWS
Esto no está en la documentación; lo descubrí probándolo, y conviene saberlo antes de confiar en un ejercicio.
1 · Las Conditions se ignoran. En AWS, un recurso con Condition: EsProd no se crea si la condición es falsa. En Floci se crea igual:
yaml
Parameters:
Entorno: { Type: String, Default: dev }
Conditions:
EsProd: !Equals [!Ref Entorno, prod]
Resources:
SoloEnProd:
Type: AWS::SQS::Queue
Condition: EsProd # ← con Entorno=dev, AWS lo omite; Floci lo crea2 · No valida los tipos de recurso. Una plantilla con Type: AWS::Inventado::NoExiste devuelve CREATE_COMPLETE en vez de fallar y hacer rollback.
📝 No son motivo para descartar el emulador: todo lo demás de esta clase funciona igual que en AWS. Pero significa que una plantilla que aquí pasa, en AWS podría no pasar. Para lo que aprendes en esta clase, da igual; para producción, valida contra AWS real.
💻 PARTE PRÁCTICA
Trabajaremos en 02-Ejercicios/clase-01-cloudformation/. Antes de nada, el entorno:
bash
floci start
eval $(floci env)La plantilla mínima con la que empieza todo:
yaml
# 02-Ejercicios/clase-01-cloudformation/01-minima.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: La pila mas simple posible
Resources:
MiBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: clase01-informesbash
aws cloudformation create-stack \
--stack-name clase01 \
--template-body file://01-minima.yaml$ aws cloudformation describe-stacks --stack-name clase01 \
--query 'Stacks[0].StackStatus' --output text
CREATE_COMPLETE
$ aws s3 ls
2026-08-08 16:27:50 clase01-informes💡 Si algo falla,
aws cloudformation describe-stack-events --stack-name clase01cuenta paso a paso qué intentó y dónde se atascó. Es el primer sitio donde mirar.
🏋️ EJERCICIOS CON SOLUCIÓN
40 ejercicios en cinco niveles, de la pila más simple a los límites del emulador. Todos verificados contra el laboratorio real.
💡 Antes de empezar:
eval $(floci env). Y trabaja desde02-Ejercicios/clase-01-cloudformation/.
🟢 Nivel 1 — La pila mínima (1-8)
Ejercicio 1 — Tu primera pila
Crea una pila llamada ej01 que contenga un único bucket de S3 llamado ej01-datos.
💡 ¿Sabías que…? — la sección mínima de una plantilla
De todas las secciones de una plantilla, solo Resources es obligatoria. Cada recurso necesita un nombre lógico, un Type y sus Properties.
yaml
# ejemplo de referencia — una cola, no un bucket
AWSTemplateFormatVersion: '2010-09-09'
Resources:
MiColita:
Type: AWS::SQS::Queue
Properties:
QueueName: ejemplo-colaVer solución
yaml
# ej01.yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
Datos:
Type: AWS::S3::Bucket
Properties:
BucketName: ej01-datosbash
aws cloudformation create-stack --stack-name ej01 --template-body file://ej01.yamlEjercicio 2 — Consultar el estado
Averigua en qué estado quedó la pila ej01, mostrando solo el estado, sin el resto del JSON.
💡 ¿Sabías que…? — `--query` filtra la respuesta
El AWS CLI trae JMESPath integrado en --query, así que no hace falta jq. Combínalo con --output text para obtener un valor pelado, ideal para scripts.
bash
# ejemplo de referencia — el nombre de una cola
aws sqs get-queue-url --queue-name otra-cola --query 'QueueUrl' --output textVer solución
bash
aws cloudformation describe-stacks --stack-name ej01 \
--query 'Stacks[0].StackStatus' --output text
# CREATE_COMPLETEEjercicio 3 — Listar los recursos de la pila
Muestra qué recursos gestiona ej01, con su nombre lógico y su estado.
💡 ¿Sabías que…? — la pila lleva su propio inventario
Una pila sabe exactamente qué creó. Ese inventario es lo que le permite borrarlo todo después sin dejar restos.
bash
# ejemplo de referencia — solo los tipos de recurso
aws cloudformation list-stack-resources --stack-name otra-pila \
--query 'StackResourceSummaries[*].ResourceType' --output textVer solución
bash
aws cloudformation list-stack-resources --stack-name ej01 \
--query 'StackResourceSummaries[*].[LogicalResourceId,ResourceStatus]' --output text
# Datos CREATE_COMPLETEEjercicio 4 — Listar todas las pilas
Cuenta cuántas pilas hay ahora mismo.
💡 ¿Sabías que…? — `length()` cuenta sin contar a mano
JMESPath tiene funciones. length(...) devuelve el tamaño de una lista, útil para comprobaciones rápidas.
bash
# ejemplo de referencia — cuántos buckets hay
aws s3api list-buckets --query 'length(Buckets)' --output textVer solución
bash
aws cloudformation list-stacks --query 'length(StackSummaries)' --output textEjercicio 5 — Recuperar la plantilla guardada
Sin abrir el archivo, muestra la plantilla que la pila ej01 tiene almacenada.
💡 ¿Sabías que…? — la pila guarda su plantilla
Al crear una pila, AWS guarda una copia de la plantilla. Sirve para saber qué se desplegó de verdad cuando el archivo local ya cambió.
bash
# ejemplo de referencia — solo la descripción
aws cloudformation get-template --stack-name otra-pila --query 'TemplateBody' --output text | head -3Ver solución
bash
aws cloudformation get-template --stack-name ej01 --query 'TemplateBody' --output textEjercicio 6 — Ver los eventos
Muestra cuántos eventos registró la pila ej01 durante su creación.
💡 ¿Sabías que…? — los eventos son la caja negra
Cada paso deja un evento con marca de tiempo. Cuando una pila falla, la causa está en el primer evento de fallo, no en el último.
bash
# ejemplo de referencia — los estados por los que pasó otra pila
aws cloudformation describe-stack-events --stack-name otra-pila \
--query 'StackEvents[*].ResourceStatus' --output textVer solución
bash
aws cloudformation describe-stack-events --stack-name ej01 \
--query 'length(StackEvents)' --output textEjercicio 7 — Eliminar la pila
Borra ej01 por completo.
💡 ¿Sabías que…? — borrar la pila borra sus recursos
delete-stack no borra "la definición": borra todo lo que la pila creó. Es la ventaja de agrupar, y también el riesgo.
bash
# ejemplo de referencia
aws cloudformation delete-stack --stack-name otra-pilaVer solución
bash
aws cloudformation delete-stack --stack-name ej01Ejercicio 8 — Comprobar que el recurso desapareció
Verifica que el bucket ej01-datos ya no existe.
💡 ¿Sabías que…? — comprobar el efecto, no solo el comando
Que un comando no dé error no significa que hiciera lo que crees. Comprobar el efecto real es el hábito que distingue a quien sabe operar.
bash
# ejemplo de referencia
aws s3 ls | grep otro-bucket || echo "ya no está"Ver solución
bash
aws s3 ls | grep ej01-datos || echo "el bucket se fue con la pila"🔵 Nivel 2 — Referencias entre recursos (9-16)
Ejercicio 9 — Devolver un valor con !Ref
Crea la pila ej09 con un bucket ej09-datos que devuelva su nombre como salida.
💡 ¿Sabías que…? — `Outputs` es la interfaz pública de la pila
Lo que pones en Outputs es lo que la pila expone al mundo. !Ref sobre un bucket devuelve su nombre.
yaml
# ejemplo de referencia — devolver el nombre de una cola
Resources:
Cola: { Type: AWS::SQS::Queue, Properties: { QueueName: ref-demo } }
Outputs:
UrlDeLaCola:
Value: !Ref ColaVer solución
yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
Datos:
Type: AWS::S3::Bucket
Properties: { BucketName: ej09-datos }
Outputs:
NombreBucket:
Value: !Ref DatosEjercicio 10 — Dos recursos en una pila
Amplía la plantilla anterior para que, además del bucket, cree una cola SQS ej10-avisos.
💡 ¿Sabías que…? — el orden en el YAML no manda
CloudFormation decide el orden de creación por las dependencias, no por cómo los escribas. Sin dependencias entre ellos, los crea en paralelo.
yaml
# ejemplo de referencia — dos recursos independientes
Resources:
Tabla:
Type: AWS::DynamoDB::Table
Properties:
TableName: demo-tabla
BillingMode: PAY_PER_REQUEST
AttributeDefinitions: [{ AttributeName: id, AttributeType: S }]
KeySchema: [{ AttributeName: id, KeyType: HASH }]
Cola:
Type: AWS::SQS::Queue
Properties: { QueueName: demo-cola }Ver solución
yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
Datos:
Type: AWS::S3::Bucket
Properties: { BucketName: ej10-datos }
Avisos:
Type: AWS::SQS::Queue
Properties: { QueueName: ej10-avisos }Ejercicio 11 — El ARN con !GetAtt
Haz que la pila devuelva el ARN de la cola, no su URL.
💡 ¿Sabías que…? — `!Ref` no siempre da lo que necesitas
En una cola SQS, !Ref devuelve la URL. Para el ARN (que es lo que piden los permisos y las integraciones) hace falta !GetAtt <Recurso>.Arn.
yaml
# ejemplo de referencia — ARN de un bucket
Outputs:
ArnDelBucket:
Value: !GetAtt OtroBucket.ArnVer solución
yaml
Outputs:
ArnCola:
Value: !GetAtt Avisos.Arn
# arn:aws:sqs:us-east-1:000000000000:ej10-avisosEjercicio 12 — Varias salidas a la vez
Devuelve en la misma pila el nombre del bucket y el ARN de la cola, con nombres de salida distintos.
💡 ¿Sabías que…? — `Outputs` admite tantas entradas como quieras
Cada salida es una clave dentro de Outputs, con su Value y opcionalmente Description.
yaml
# ejemplo de referencia
Outputs:
Region: { Value: !Ref 'AWS::Region' }
NombrePila: { Value: !Ref 'AWS::StackName' }Ver solución
yaml
Outputs:
NombreBucket:
Description: Nombre fisico del bucket
Value: !Ref Datos
ArnCola:
Description: ARN de la cola de avisos
Value: !GetAtt Avisos.ArnEjercicio 13 — Componer un nombre con !Sub y la región
Crea un bucket cuyo nombre incluya la región automáticamente, sin escribirla a mano.
💡 ¿Sabías que…? — los pseudo-parámetros existen siempre
AWS::Region, AWS::AccountId y AWS::StackName están disponibles sin declararlos. !Sub los sustituye dentro de una cadena.
yaml
# ejemplo de referencia
Properties:
QueueName: !Sub 'cola-${AWS::StackName}'Ver solución
yaml
Resources:
Datos:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'ej13-${AWS::Region}'
# ej13-us-east-1Ejercicio 14 — El identificador de cuenta
Devuelve como salida el identificador de la cuenta.
💡 ¿Sabías que…? — en el emulador la cuenta es de doce ceros
Floci usa 000000000000 como identificador de cuenta. En AWS real serían tus doce dígitos. Que aparezca en los ARN es normal.
yaml
# ejemplo de referencia
Outputs:
Region: { Value: !Sub '${AWS::Region}' }Ver solución
yaml
Outputs:
Cuenta:
Value: !Sub '${AWS::AccountId}'
# 000000000000Ejercicio 15 — Unir trozos con !Join
Compón el nombre ej15-informes-prod uniendo tres trozos con guiones.
💡 ¿Sabías que…? — `!Join` frente a `!Sub`
!Join [separador, [lista]] une elementos. Es útil cuando los trozos vienen de sitios distintos (parámetros, mappings). Para texto fijo con sustituciones, !Sub se lee mejor.
yaml
# ejemplo de referencia
BucketName: !Join ['.', ['datos', 'equipo', 'interno']] # datos.equipo.internoVer solución
yaml
Resources:
Datos:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join ['-', ['ej15', 'informes', 'prod']]Ejercicio 16 — Una tabla de consulta con Mappings
Usa un Mappings que asocie us-east-1 al sufijo este, y úsalo en el nombre del bucket.
💡 ¿Sabías que…? — `Mappings` es una tabla fija
Mappings guarda valores constantes organizados en dos niveles. !FindInMap [Mapa, Clave1, Clave2] los recupera. Se usa mucho para valores que cambian por región o entorno.
yaml
# ejemplo de referencia
Mappings:
PorEntorno:
dev: { Tamano: pequeno }
prod: { Tamano: grande }Ver solución
yaml
AWSTemplateFormatVersion: '2010-09-09'
Mappings:
PorRegion:
us-east-1: { Sufijo: este }
Resources:
Datos:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join ['-', ['ej16', !FindInMap [PorRegion, us-east-1, Sufijo]]]
# ej16-este🟡 Nivel 3 — Parámetros (17-24)
Ejercicio 17 — Un parámetro con valor por defecto
Convierte el nombre del bucket en un parámetro con Default: ej17-datos.
💡 ¿Sabías que…? — un parámetro con `Default` es opcional
Si el parámetro tiene Default, puedes crear la pila sin pasarlo. Sin Default, el CLI exige que lo indiques.
yaml
# ejemplo de referencia
Parameters:
Entorno:
Type: String
Default: devVer solución
yaml
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
NombreBucket:
Type: String
Default: ej17-datos
Resources:
Datos:
Type: AWS::S3::Bucket
Properties: { BucketName: !Ref NombreBucket }Ejercicio 18 — Pasar el parámetro desde la línea de comandos
Crea la pila anterior pero con el nombre ej18-otro, sin tocar el archivo.
💡 ¿Sabías que…? — la misma plantilla, distintos resultados
Ese es el punto de los parámetros: una plantilla, muchos entornos. La sintaxis del CLI es ParameterKey=...,ParameterValue=....
bash
# ejemplo de referencia
aws cloudformation create-stack --stack-name demo --template-body file://t.yaml \
--parameters ParameterKey=Entorno,ParameterValue=prodVer solución
bash
aws cloudformation create-stack --stack-name ej18 --template-body file://ej17.yaml \
--parameters ParameterKey=NombreBucket,ParameterValue=ej18-otroEjercicio 19 — Restringir los valores admitidos
Añade un parámetro Entorno que solo acepte dev o prod.
💡 ¿Sabías que…? — `AllowedValues` documenta y protege
Restringir valores evita erratas (prd, produccion) y además sirve de documentación: quien lea la plantilla sabe qué opciones hay.
yaml
# ejemplo de referencia
Parameters:
Tamano:
Type: String
AllowedValues: [pequeno, mediano, grande]Ver solución
yaml
Parameters:
Entorno:
Type: String
Default: dev
AllowedValues: [dev, prod]Ejercicio 20 — Documentar el parámetro
Añade una Description al parámetro Entorno explicando para qué sirve.
💡 ¿Sabías que…? — la plantilla también es documentación
Description aparece en la consola de AWS al desplegar. Una plantilla bien descrita es la diferencia entre que otro la use o te pregunte a ti.
yaml
# ejemplo de referencia
Parameters:
Retencion:
Type: Number
Description: Dias que se conservan los registrosVer solución
yaml
Parameters:
Entorno:
Type: String
Default: dev
AllowedValues: [dev, prod]
Description: Entorno de despliegue; afecta al nombre de los recursosEjercicio 21 — Un parámetro numérico
Añade un parámetro Dias de tipo Number con valor 7, y devuélvelo como salida.
💡 ¿Sabías que…? — los tipos de parámetro más usados
String, Number, List<Number> y CommaDelimitedList. El tipo se valida al crear la pila: pasar texto a un Number la rechaza.
yaml
# ejemplo de referencia
Parameters:
Reintentos: { Type: Number, Default: 3 }Ver solución
yaml
Parameters:
Dias:
Type: Number
Default: 7
Outputs:
DiasConfigurados:
Value: !Ref Dias
# 7Ejercicio 22 — Un parámetro que no se muestra
Añade un parámetro Clave que no aparezca en la consola ni en los eventos.
💡 ¿Sabías que…? — `NoEcho` oculta, no cifra
NoEcho: true hace que el valor salga como **** en la consola y los eventos. No es cifrado: para secretos de verdad, Secrets Manager o SSM Parameter Store.
yaml
# ejemplo de referencia
Parameters:
TokenApi:
Type: String
NoEcho: trueVer solución
yaml
Parameters:
Clave:
Type: String
Default: secreta
NoEcho: trueEjercicio 23 — Dos parámetros combinados
Usa Entorno y NombreBase para componer un nombre de bucket del tipo datos-dev.
💡 ¿Sabías que…? — `!Sub` acepta parámetros propios
Dentro de !Sub, ${MiParametro} se sustituye igual que los pseudo-parámetros. Se lee mucho mejor que anidar !Join.
yaml
# ejemplo de referencia
QueueName: !Sub '${Prefijo}-cola-${Entorno}'Ver solución
yaml
Parameters:
NombreBase: { Type: String, Default: datos }
Entorno: { Type: String, Default: dev, AllowedValues: [dev, prod] }
Resources:
Datos:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub '${NombreBase}-${Entorno}'Ejercicio 24 — Parámetro, pseudo-parámetro y texto, juntos
Compón un nombre que incluya un parámetro, la región y un texto fijo.
💡 ¿Sabías que…? — se pueden mezclar en la misma cadena
!Sub no distingue entre parámetros tuyos y pseudo-parámetros: todos se escriben igual, ${nombre}.
yaml
# ejemplo de referencia
Value: !Sub 'app-${Entorno}-${AWS::AccountId}'Ver solución
yaml
Parameters:
Entorno: { Type: String, Default: dev }
Resources:
Datos:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'ej24-${Entorno}-${AWS::Region}'
# ej24-dev-us-east-1🟠 Nivel 4 — Ciclo de vida (25-32)
Ejercicio 25 — Añadir un recurso a una pila existente
Sobre una pila ya creada, añade una cola SQS sin borrarla ni recrearla.
💡 ¿Sabías que…? — actualizar no es recrear
update-stack compara la plantilla nueva con la guardada y aplica solo la diferencia. Los recursos que no cambian se quedan intactos.
bash
# ejemplo de referencia
aws cloudformation update-stack --stack-name otra --template-body file://nueva.yamlVer solución
bash
# ej25.yaml: la plantilla anterior + un recurso nuevo
aws cloudformation update-stack --stack-name ej25 --template-body file://ej25.yaml
aws cloudformation describe-stacks --stack-name ej25 \
--query 'Stacks[0].StackStatus' --output text
# UPDATE_COMPLETEEjercicio 26 — Quitar un recurso
Actualiza la pila borrando la cola de la plantilla. Comprueba que la cola desapareció.
💡 ¿Sabías que…? — lo que no está en la plantilla, se borra
Quitar un recurso del YAML no lo "deja como está": CloudFormation entiende que ya no debe existir y lo elimina. Es el error clásico de quien edita una plantilla a la ligera.
bash
# ejemplo de referencia — comprobar el efecto
aws sqs get-queue-url --queue-name la-cola >/dev/null 2>&1 || echo "ya no existe"Ver solución
bash
aws cloudformation update-stack --stack-name ej25 --template-body file://ej26.yaml
aws cloudformation list-stack-resources --stack-name ej25 \
--query 'StackResourceSummaries[*].LogicalResourceId' --output textEjercicio 27 — Forzar un orden con DependsOn
Haz que un bucket se cree después de una cola, aunque no dependa de ella.
💡 ¿Sabías que…? — dependencia implícita frente a explícita
Usar !Ref o !GetAtt sobre otro recurso ya crea una dependencia implícita. DependsOn es para cuando el orden importa pero no hay referencia que lo exprese.
yaml
# ejemplo de referencia
Recurso2:
Type: AWS::SQS::Queue
DependsOn: Recurso1
Properties: { QueueName: segunda }Ver solución
yaml
Resources:
Avisos:
Type: AWS::SQS::Queue
Properties: { QueueName: ej27-avisos }
Datos:
Type: AWS::S3::Bucket
DependsOn: Avisos
Properties: { BucketName: ej27-datos }Ejercicio 28 — Etiquetar la pila
Crea una pila con la etiqueta curso=aws y compruébala.
💡 ¿Sabías que…? — las etiquetas bajan a los recursos
Las etiquetas de la pila se propagan a los recursos que la soportan. En AWS real es la manera de saber qué equipo o proyecto genera cada línea de la factura.
bash
# ejemplo de referencia
aws cloudformation create-stack --stack-name demo --template-body file://t.yaml \
--tags Key=equipo,Value=plataformaVer solución
bash
aws cloudformation create-stack --stack-name ej28 --template-body file://ej28.yaml \
--tags Key=curso,Value=aws
aws cloudformation describe-stacks --stack-name ej28 \
--query 'Stacks[0].Tags[0].[Key,Value]' --output text
# curso awsEjercicio 29 — Preparar un cambio sin aplicarlo
Crea un change set llamado cs1 con una plantilla modificada, sin ejecutarlo.
💡 ¿Sabías que…? — el ensayo antes del estreno
Un change set responde "¿qué pasaría si aplico esto?" sin tocar nada. En producción es la diferencia entre un cambio revisado y una sorpresa.
bash
# ejemplo de referencia
aws cloudformation create-change-set --stack-name otra \
--change-set-name prueba --template-body file://nueva.yamlVer solución
bash
aws cloudformation create-change-set --stack-name ej28 \
--change-set-name cs1 --template-body file://ej29.yaml \
--query 'Id' --output textEjercicio 30 — Comparar el inventario antes y después
Cuenta los recursos de una pila, actualízala añadiendo uno, y vuelve a contar.
💡 ¿Sabías que…? — medir antes y después
Comprobar un número antes y después es la forma más simple de verificar que un cambio hizo lo que esperabas.
bash
# ejemplo de referencia
aws cloudformation list-stack-resources --stack-name otra \
--query 'length(StackResourceSummaries)' --output textVer solución
bash
aws cloudformation list-stack-resources --stack-name ej25 \
--query 'length(StackResourceSummaries)' --output text # p.ej. 1
aws cloudformation update-stack --stack-name ej25 --template-body file://ej30.yaml
sleep 5
aws cloudformation list-stack-resources --stack-name ej25 \
--query 'length(StackResourceSummaries)' --output text # 2Ejercicio 31 — Confirmar que la plantilla guardada cambió
Tras actualizar, comprueba que get-template devuelve la versión nueva, no la original.
💡 ¿Sabías que…? — la pila recuerda la última aplicada
La plantilla almacenada se sustituye en cada actualización correcta. Comparar lo guardado con tu archivo local revela si alguien desplegó algo distinto a lo que hay en git.
bash
# ejemplo de referencia
aws cloudformation get-template --stack-name otra --query 'TemplateBody' --output text | grep ResourcesVer solución
bash
aws cloudformation get-template --stack-name ej25 --query 'TemplateBody' --output text \
| grep -c 'AWS::SQS::Queue'Ejercicio 32 — Seguir la historia completa
Muestra la secuencia de estados por los que pasó una pila que fue creada y actualizada.
💡 ¿Sabías que…? — los eventos cuentan la historia entera
Los eventos incluyen creación y actualizaciones. Leerlos en orden explica por qué una pila está como está.
bash
# ejemplo de referencia
aws cloudformation describe-stack-events --stack-name otra \
--query 'StackEvents[*].ResourceStatus' --output textVer solución
bash
aws cloudformation describe-stack-events --stack-name ej25 \
--query 'StackEvents[*].[LogicalResourceId,ResourceStatus]' --output text🔴 Nivel 5 — Varias pilas y los límites del emulador (33-40)
Ejercicio 33 — Una pila con tres servicios
Monta en una sola pila un bucket, una tabla DynamoDB y una cola SQS.
💡 ¿Sabías que…? — DynamoDB necesita su clave declarada
Una tabla exige AttributeDefinitions y KeySchema coherentes: todo atributo declarado debe usarse en la clave. Con PAY_PER_REQUEST no hace falta indicar capacidad.
yaml
# ejemplo de referencia
Tabla:
Type: AWS::DynamoDB::Table
Properties:
TableName: referencia
BillingMode: PAY_PER_REQUEST
AttributeDefinitions: [{ AttributeName: clave, AttributeType: S }]
KeySchema: [{ AttributeName: clave, KeyType: HASH }]Ver solución
yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
Datos:
Type: AWS::S3::Bucket
Properties: { BucketName: ej33-datos }
Auditoria:
Type: AWS::DynamoDB::Table
Properties:
TableName: Ej33Auditoria
BillingMode: PAY_PER_REQUEST
AttributeDefinitions: [{ AttributeName: id, AttributeType: S }]
KeySchema: [{ AttributeName: id, KeyType: HASH }]
Avisos:
Type: AWS::SQS::Queue
Properties: { QueueName: ej33-avisos }Ejercicio 34 — Devolver las tres identidades
Haz que esa pila devuelva el nombre del bucket, el nombre de la tabla y el ARN de la cola.
💡 ¿Sabías que…? — cada tipo devuelve algo distinto con `!Ref`
!Ref sobre un bucket da su nombre; sobre una tabla, su nombre; sobre una cola, su URL. Cuando dudes, !GetAtt X.Arn es lo más explícito.
yaml
# ejemplo de referencia
Outputs:
Tabla: { Value: !Ref MiTabla }Ver solución
yaml
Outputs:
Bucket: { Value: !Ref Datos }
Tabla: { Value: !Ref Auditoria }
Cola: { Value: !GetAtt Avisos.Arn }Ejercicio 35 — Publicar un valor para otras pilas
Exporta el ARN de la cola con el nombre ej35-cola-arn.
💡 ¿Sabías que…? — el nombre exportado es único en la cuenta
Dos pilas no pueden exportar el mismo nombre. Conviene prefijarlos con el nombre de la pila o del proyecto para no chocar.
yaml
# ejemplo de referencia
Outputs:
Bus:
Value: !GetAtt MiCola.Arn
Export: { Name: plataforma-bus-arn }Ver solución
yaml
Outputs:
ColaArn:
Value: !GetAtt Avisos.Arn
Export:
Name: ej35-cola-arnEjercicio 36 — Consultar lo exportado
Lista los valores exportados y localiza el tuyo.
💡 ¿Sabías que…? — los exports son un catálogo de la cuenta
list-exports muestra todo lo publicado por cualquier pila. Es la forma de descubrir qué puedes reutilizar sin preguntar.
bash
# ejemplo de referencia
aws cloudformation list-exports --query 'Exports[*].Name' --output textVer solución
bash
aws cloudformation list-exports \
--query 'Exports[?Name==`ej35-cola-arn`].Value' --output text
# arn:aws:sqs:us-east-1:000000000000:ej33-avisosEjercicio 37 — Consumirlo desde otra pila
Crea una segunda pila que devuelva como salida el valor exportado por la primera.
💡 ¿Sabías que…? — así se compone una arquitectura por capas
Una pila de red, otra de datos, otra de aplicación: cada una exporta lo que las de arriba necesitan. Evita una plantilla gigante imposible de revisar.
yaml
# ejemplo de referencia
Outputs:
BusHeredado:
Value: !ImportValue plataforma-bus-arnVer solución
yaml
AWSTemplateFormatVersion: '2010-09-09'
Resources:
Local:
Type: AWS::S3::Bucket
Properties: { BucketName: ej37-local }
Outputs:
ColaHeredada:
Value: !ImportValue ej35-cola-arnEjercicio 38 — El candado entre pilas
Con la pila consumidora viva, intenta borrar la que exporta. Observa qué ocurre y explica por qué.
💡 ¿Sabías que…? — un export en uso bloquea el borrado
Es una protección: si se pudiera borrar, la pila consumidora se quedaría apuntando a algo inexistente. Primero se borra quien importa, después quien exporta.
bash
# ejemplo de referencia — el orden correcto
aws cloudformation delete-stack --stack-name consumidora
aws cloudformation delete-stack --stack-name exportadoraVer solución
bash
aws cloudformation delete-stack --stack-name ej33 # la que exporta
aws cloudformation describe-stacks --stack-name ej33 \
--query 'Stacks[0].StackStatus' --output text
# Orden correcto: primero ej37 (importa), despues ej33 (exporta)Ejercicio 39 — Descubre una diferencia con AWS real
Crea una pila con un parámetro Entorno en dev y un recurso marcado Condition: EsProd. Comprueba si el recurso se creó. ¿Coincide con lo que haría AWS?
💡 ¿Sabías que…? — cómo deberían funcionar las condiciones
En AWS, Conditions decide si un recurso llega a existir. Con la condición en falso, el recurso se omite y no aparece en el inventario de la pila.
yaml
# ejemplo de referencia — lo que AWS haría
Conditions:
EsGrande: !Equals [!Ref Tamano, grande]
Resources:
Extra:
Type: AWS::SQS::Queue
Condition: EsGrande # con Tamano=pequeno, AWS NO la crea
Properties: { QueueName: solo-si-grande }Ver solución
bash
aws cloudformation list-stack-resources --stack-name ej39 \
--query 'StackResourceSummaries[*].LogicalResourceId' --output text
# Base SoloEnProd ← el emulador lo creó igual
aws sqs get-queue-url --queue-name ej39-solo-prod # existeEl emulador ignora Condition: crea el recurso aunque la condición sea falsa. En AWS real solo aparecería Base. Verificado el 2026-08-08.
Ejercicio 40 — Un recurso que no existe
Escribe una plantilla con Type: AWS::Inventado::NoExiste y crea la pila. ¿Falla?
💡 ¿Sabías que…? — qué haría AWS ante un tipo desconocido
AWS rechaza la plantilla o marca la pila CREATE_FAILED y hace rollback, dejándolo todo como estaba. Ese rollback automático es una de sus mayores garantías.
bash
# ejemplo de referencia — leer el motivo del fallo
aws cloudformation describe-stack-events --stack-name fallida \
--query 'StackEvents[?ResourceStatus==`CREATE_FAILED`].ResourceStatusReason' --output textVer solución
bash
aws cloudformation describe-stacks --stack-name ej40 \
--query 'Stacks[0].StackStatus' --output text
# CREATE_COMPLETE ← el emulador lo aceptaEl emulador no valida los tipos de recurso ni hace rollback. Consecuencia práctica: una plantilla que aquí pasa puede fallar en AWS. Para lo que se aprende en esta clase da igual, pero conviene no confiarse. Verificado el 2026-08-08.
❓ Preguntas y respuestas (autoevaluación)
1. ¿Cuál es la única sección obligatoria de una plantilla?
Resources. Todo lo demás (Parameters,Mappings,Outputs,Description) es opcional.
2. ¿Qué diferencia hay entre el nombre lógico y el físico de un recurso?
El lógico solo existe dentro de la plantilla, para referenciarlo. El físico es el nombre real del recurso en AWS.
3. ¿Qué devuelve !Ref sobre una cola SQS?
Su URL. Para el ARN hay que usar
!GetAtt MiCola.Arn.
4. ¿Qué pasa si quitas un recurso de la plantilla y actualizas la pila?
CloudFormation lo elimina. Lo que no está en la plantilla, no debe existir.
5. ¿Para qué sirve DependsOn si ya existen las dependencias implícitas?
Para forzar un orden cuando dos recursos no se referencian entre sí pero uno debe crearse antes.
6. ¿Qué hace NoEcho: true exactamente?
Oculta el valor en la consola y los eventos. No lo cifra: para secretos reales, Secrets Manager o SSM.
7. ¿Por qué no se puede borrar una pila cuyo export está en uso?
Porque dejaría a la pila consumidora apuntando a un valor inexistente. Primero se borra quien importa.
8. ¿Qué ventaja tiene un change set frente a actualizar directamente?
Muestra qué cambiaría sin aplicar nada, así el cambio se revisa antes de tocar producción.
9. ¿Dónde miras primero cuando una pila falla?
En
describe-stack-events, y concretamente en el primer evento de fallo: los siguientes suelen ser consecuencia.
10. ¿Qué dos comportamientos del emulador no coinciden con AWS, y por qué importa?
Ignora
Conditions(crea recursos que AWS omitiría) y no valida tipos de recurso ni hace rollback. Importa porque una plantilla válida aquí puede fallar en AWS real.
📎 Apuntes relacionados
- Comandos de CloudFormation
- Conceptos del curso
- Preguntas de entrevista
- Limitaciones conocidas del emulador
➡️ Siguiente
Clase 2 — Bases de datos gestionadas (RDS): pedir un PostgreSQL por API, conectarse con psql y entender por qué el motor tarda más que la API en aceptar conexiones.