Infraestructura como código en AWS con CloudFormation: guía práctica

AWS CloudFormation convierte una plantilla YAML o JSON en una pila de recursos de AWS que puedes crear, actualizar y eliminar como una unidad. En esta guía crearás una pila con un bucket S3 sin acceso público, cifrado por defecto. También verás cómo revisar actualizaciones antes de ejecutarlas, cómo conservar recursos y qué cubren la validación y la detección de drift.

Los comandos que crean, actualizan o eliminan la pila modificarán recursos de tu cuenta si los ejecutas. Revisa antes el perfil, la región, los permisos y los cambios mostrados, y practica en un entorno aislado.

Qué necesitas

Los ejemplos de terminal usan Bash o Zsh; adapta la sintaxis de variables si trabajas con PowerShell.

Con AWS IAM Identity Center puedes iniciar sesión y usar credenciales temporales desde un perfil:

aws sso login --profile iac-lab
export AWS_PROFILE=iac-lab
export AWS_REGION=us-east-1
aws sts get-caller-identity

Consulta la guía oficial de AWS CLI para configurar IAM Identity Center. Confirma que la cuenta y el rol sean los esperados antes de ejecutar comandos que cambien recursos. La identidad que inicia la operación necesita permisos de CloudFormation; el servicio también necesita permisos para aprovisionar el recurso. Si configuras un rol de servicio de CloudFormation, concede solo las acciones y recursos requeridos por la plantilla.

1. Escribe una plantilla segura

Guarda este contenido como bucket-privado.yaml:

AWSTemplateFormatVersion: "2010-09-09"
Description: Bucket S3 privado de ejemplo, con cifrado y bloqueo de acceso público.

Resources:
  PrivateBucket:
    Type: AWS::S3::Bucket
    Properties:
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        IgnorePublicAcls: true
        BlockPublicPolicy: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      Tags:
        - Key: Purpose
          Value: iac-learning

Outputs:
  BucketName:
    Description: Nombre generado para el bucket privado.
    Value: !Ref PrivateBucket

CloudFormation genera un nombre único para el bucket porque no declaramos BucketName. Las cuatro opciones de S3 bloquean el acceso público a través de ACL y políticas. Esto no reemplaza los permisos de IAM: las identidades autorizadas todavía pueden acceder según sus políticas. El cifrado del ejemplo usa claves administradas por S3.

La plantilla define un recurso en Resources; CloudFormation llama pila al conjunto gestionado a partir de esa plantilla. Outputs publica el nombre asignado al bucket. Consulta la referencia de AWS::S3::Bucket para ver propiedades y qué cambios interrumpen o reemplazan el recurso.

2. Valida antes de crear la pila

Valida la plantilla localmente con cfn-lint:

cfn-lint bucket-privado.yaml

También puedes pedirle a CloudFormation que lea la plantilla:

aws cloudformation validate-template \
  --template-body file://bucket-privado.yaml \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Estas comprobaciones ayudan a encontrar errores de sintaxis o de estructura, pero no prueban que tu identidad tenga permisos, que haya cuota disponible ni que una operación real vaya a terminar bien. Los cambios de servicio y las condiciones al desplegar aún pueden causar un fallo.

3. Crea una pila con un change set

Un change set muestra los cambios que CloudFormation calcula para una operación y espera a que lo ejecutes. Sirve para revisar recursos que agregaría, modificaría, reemplazaría o eliminaría. No es una garantía de que el despliegue vaya a tener éxito: el resultado puede depender de permisos, cuotas, estado de los recursos y condiciones que solo aparecen durante la operación.

Crea primero un change set de tipo CREATE:

aws cloudformation create-change-set \
  --stack-name iac-bucket-demo \
  --change-set-name crear-bucket \
  --change-set-type CREATE \
  --template-body file://bucket-privado.yaml \
  --description "Bucket privado para practicar CloudFormation" \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Espera a que CloudFormation termine de calcularlo y examina los recursos:

aws cloudformation wait change-set-create-complete \
  --stack-name iac-bucket-demo \
  --change-set-name crear-bucket \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

aws cloudformation describe-change-set \
  --stack-name iac-bucket-demo \
  --change-set-name crear-bucket \
  --query 'Changes[].ResourceChange.{Type:ResourceType,Action:Action,Replacement:Replacement}' \
  --output table \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Si el resumen coincide con lo que esperabas, ejecuta el change set y espera a que la pila quede lista:

aws cloudformation execute-change-set \
  --stack-name iac-bucket-demo \
  --change-set-name crear-bucket \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

aws cloudformation wait stack-create-complete \
  --stack-name iac-bucket-demo \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Después de que la pila quede lista, consulta su salida y el estado del bucket:

aws cloudformation describe-stacks \
  --stack-name iac-bucket-demo \
  --query 'Stacks[0].Outputs' \
  --output table \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

# Usa el valor BucketName que devolvió el comando anterior.
aws s3api get-public-access-block \
  --bucket NOMBRE_DEL_BUCKET \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

aws s3api get-bucket-encryption \
  --bucket NOMBRE_DEL_BUCKET \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Confirma que las cuatro opciones de bloqueo público estén en true y que el cifrado use AES256.

Si aparece un cambio inesperado, no ejecutes ese change set. Ajusta la plantilla, crea otro change set y vuelve a revisarlo.

Si falla la creación del change set, consulta su StatusReason con describe-change-set. Si falla la creación o actualización de la pila, revisa su pestaña Events o ejecuta aws cloudformation describe-stack-events --stack-name iac-bucket-demo con el mismo perfil y región. Busca el recurso que falló y su ResourceStatusReason antes de corregir permisos, cuotas o la plantilla; no vuelvas a ejecutar una operación sin entender su estado.

4. Actualiza sin tratar la vista previa como una promesa

Para practicar una actualización, agrega el bloque VersioningConfiguration dentro de Properties de PrivateBucket, al mismo nivel que PublicAccessBlockConfiguration y BucketEncryption:

      VersioningConfiguration:
        Status: Enabled

Luego crea un change set de tipo UPDATE para la pila existente. En esta operación usa el nombre habilitar-versionado en los comandos de espera, revisión y ejecución; el change set crear-bucket y el waiter stack-create-complete corresponden solo a la creación inicial:

aws cloudformation create-change-set \
  --stack-name iac-bucket-demo \
  --change-set-name habilitar-versionado \
  --change-set-type UPDATE \
  --template-body file://bucket-privado.yaml \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Espera a que se calcule y revisa el change set de actualización:

aws cloudformation wait change-set-create-complete \
  --stack-name iac-bucket-demo \
  --change-set-name habilitar-versionado \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

aws cloudformation describe-change-set \
  --stack-name iac-bucket-demo \
  --change-set-name habilitar-versionado \
  --query 'Changes[].ResourceChange.{Type:ResourceType,Action:Action,Replacement:Replacement}' \
  --output table \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Solo si las acciones son las esperadas, ejecuta ese mismo change set y espera a que finalice la actualización:

aws cloudformation execute-change-set \
  --stack-name iac-bucket-demo \
  --change-set-name habilitar-versionado \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

aws cloudformation wait stack-update-complete \
  --stack-name iac-bucket-demo \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

aws s3api get-bucket-versioning \
  --bucket NOMBRE_DEL_BUCKET \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

La salida de get-bucket-versioning debe incluir Status: Enabled. AWS clasifica algunas modificaciones como reemplazos. Por ejemplo, cambiar el nombre explícito de un bucket requiere reemplazarlo. Un reemplazo puede crear otro recurso y retirar el anterior; borrar un bucket con objetos puede fallar.

Los change sets muestran el efecto calculado sobre recursos y propiedades, no una simulación perfecta de todas las condiciones de ejecución. Pueden no anticipar una restricción de servicio o un fallo en tiempo de despliegue. La guía de change sets explica sus límites y cómo revisar las acciones.

Eliminar o conservar un recurso

Al eliminar esta pila de práctica, CloudFormation intenta eliminar también el bucket. S3 solo permite borrar buckets vacíos; elimina antes los objetos y, si habilitaste versionado, sus versiones. La operación puede fallar si el bucket sigue teniendo contenido.

Para conservar un bucket al borrar la pila, puedes añadir una política de eliminación:

  PrivateBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      # Las propiedades del bucket van aquí.

DeletionPolicy: Retain conserva el recurso al eliminar la pila o quitarlo de la plantilla. UpdateReplacePolicy: Retain regula qué pasa con el recurso anterior cuando una actualización lo reemplaza; son controles distintos. Un recurso retenido queda fuera de la pila y tendrás que gestionarlo y, si corresponde, eliminarlo por separado. Consulta la documentación de DeletionPolicy y UpdateReplacePolicy antes de usar estas opciones con datos que deban preservarse.

Para eliminar la pila de ejemplo cuando ya no la necesites:

aws cloudformation delete-stack \
  --stack-name iac-bucket-demo \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

aws cloudformation wait stack-delete-complete \
  --stack-name iac-bucket-demo \
  --profile "$AWS_PROFILE" \
  --region "$AWS_REGION"

Este cierre elimina el bucket solo si no se retuvo y está vacío. Si una política lo conserva, la pila desaparece pero el bucket permanece en la cuenta. Revisa los recursos retenidos y los costos de tu cuenta después de cada práctica. S3 cobra según almacenamiento, solicitudes y recuperaciones; el versionado también cobra por cada versión que conserva. Un bucket pequeño no implica que el uso sea gratuito: consulta los precios actuales de Amazon S3 y estima el uso para tu región antes de desplegar.

Drift e importación

Si alguien cambia un recurso fuera de CloudFormation, la pila puede desviarse de la plantilla. La detección de drift compara propiedades compatibles con los valores esperados; no inspecciona todo lo que existe en la cuenta y solo compara los valores definidos explícitamente en la plantilla o por parámetros. Puedes iniciar una revisión de drift desde la consola o con la API de CloudFormation y consultar los resultados antes de decidir cómo reconciliar el recurso.

CloudFormation también permite importar ciertos recursos existentes a una pila. La importación no crea ni modifica recursos como parte de esa operación: primero declaras la configuración, identificas el recurso y creas una operación de importación. Cada recurso que importes necesita una DeletionPolicy; después de importar, AWS recomienda ejecutar detección de drift para comprobar que la plantilla coincide con el recurso. Lee la guía de detección de drift y la de importación de recursos antes de incorporar recursos existentes.

CloudFormation o Terraform

CloudFormation integra plantillas y pilas con el servicio de AWS; Terraform usa proveedores y mantiene estado propio, local o remoto. No hay una opción universal: compara el flujo que ya conoce tu equipo, el alcance de proveedores que necesitas y cómo administrará cambios y estado. Si un recurso ya pertenece a una pila de CloudFormation, no lo declares también en Terraform sin planificar una migración o importación: dos herramientas intentando ser dueñas del mismo recurso pueden producir cambios en conflicto.

La guía prescriptiva de AWS para elegir una herramienta de IaC ofrece criterios de comparación. También puedes ver la charla comunitaria IaC: Terraform, CDK y CloudFormation, grabada por AWS User Group Medellín.

Recursos, charlas y comunidades

Para continuar dentro del blog, compara esta guía con Terraform y S3 y consulta nuestros controles de seguridad para IaC. Si luego trabajas con instancias EC2 privadas, revisa AWS Session Manager.