Cloud, DevOps e IoT en español

Parte 12 de 22 de la serie Domótica con ESP32 y AWS desde cero

AWS CDK con TypeScript para un proyecto IoT: infraestructura como código

7 de octubre de 2026 · Steven Carvajal · Tutoriales

Código de este artículo en GitHub →

Casi todos los proyectos empiezan igual: una Lambda creada en la consola, un rol con un permiso agregado "solo para probar", una tabla creada con la CLI. Funciona, pero si mañana alguien borra un rol por error, reconstruirlo exactamente igual es una tarde de arqueología.

AWS CDK permite describir esa infraestructura con TypeScript y desplegarla con un comando. En esta parte definimos con CDK las piezas del proyecto de ejemplo de la serie y vemos cómo adoptar recursos que ya creaste a mano.

Qué es CDK

CDK es un framework que convierte código (TypeScript, Python, Java...) en plantillas de CloudFormation. Escribes clases como dynamodb.Table o lambda.Function, y CDK genera la plantilla y la despliega.

Ventajas frente a escribir CloudFormation a mano:

Empezar

npm install -g aws-cdk
mkdir infra && cd infra
cdk init app --language typescript
cdk bootstrap   # una vez por cuenta y región

cdk bootstrap crea los recursos que CDK necesita para desplegar (un bucket para los assets y unos roles).

El stack del proyecto de ejemplo

import * as cdk from "aws-cdk-lib";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as iam from "aws-cdk-lib/aws-iam";
import * as iot from "aws-cdk-lib/aws-iot";
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
import { Construct } from "constructs";

export class CasaDemoStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const thingName = "mi-esp32";

    // 1. Tabla de dispositivos (con estado: no se borra con el stack)
    const tabla = new dynamodb.Table(this, "Dispositivos", {
      partitionKey: { name: "casaId", type: dynamodb.AttributeType.STRING },
      sortKey: { name: "dispositivoId", type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: cdk.RemovalPolicy.RETAIN,
      pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: true },
    });

    // 2. Lambda de la API (parte 8)
    const api = new NodejsFunction(this, "ApiOrdenes", {
      entry: "lambda/api-ordenes.ts",
      runtime: lambda.Runtime.NODEJS_22_X,
      timeout: cdk.Duration.seconds(10),
      memorySize: 256,
      environment: { THING_NAME: thingName, TABLA: tabla.tableName },
    });
    tabla.grantReadData(api);
    api.addToRolePolicy(
      new iam.PolicyStatement({
        actions: ["iot:UpdateThingShadow"],
        resources: [`arn:aws:iot:${this.region}:${this.account}:thing/${thingName}`],
      })
    );
    const url = api.addFunctionUrl({ authType: lambda.FunctionUrlAuthType.NONE });

    // 3. Política de IoT del dispositivo (parte 13)
    new iot.CfnPolicy(this, "PoliticaDispositivo", {
      policyName: "casa-demo-dispositivo",
      policyDocument: {
        Version: "2012-10-17",
        Statement: [
          {
            Effect: "Allow",
            Action: "iot:Connect",
            Resource: `arn:aws:iot:${this.region}:${this.account}:client/\${iot:Connection.Thing.ThingName}`,
          },
          // ... Subscribe, Receive y Publish sobre los tópicos de su Shadow
        ],
      },
    });

    new cdk.CfnOutput(this, "ApiUrl", { value: url.url });
  }
}

Detalles a notar:

Recursos con estado: el cuidado principal

Si CDK gestiona un recurso, un cambio en el código puede reemplazarlo (borrar y crear otro), y borrar el stack puede eliminarlo. Para recursos sin estado (Lambdas, roles) no importa. Para recursos con estado, sí:

RecursoRiesgo si se recreaQué hacer
Tabla de DynamoDBPierdes todos los datosremovalPolicy: RETAIN y respaldos
User Pool de CognitoSe invalidan todas las cuentasRETAIN, y cuidado con cambios que fuerzan reemplazo
Certificados de IoTLos dispositivos dejan de conectarGestionarlos fuera del stack

Antes de cada despliegue, ejecuta cdk diff y busca la palabra replace. Algunos cambios aparentemente inocentes (como renombrar la clave de una tabla o ciertos atributos de un User Pool) obligan a reemplazar el recurso.

Otra opción válida: dejar los recursos con estado fuera del stack y solo referenciarlos por nombre o ARN para dar permisos:

const tabla = dynamodb.Table.fromTableName(this, "TablaExistente", "dispositivos");
tabla.grantReadData(api);  // funciona igual, pero CDK nunca la crea ni la borra

Si ya creaste todo a mano

Es lo más habitual. Pasos para llevarlo a CDK sin romper nada:

  1. Haz un inventario en solo lectura: cada Lambda (runtime, memoria, variables), cada rol con el texto exacto de sus políticas (aws iam get-role-policy), cada tabla con sus índices.
  2. Escribe el código para describir exactamente lo que existe, no para mejorarlo todavía. Usa los mismos nombres.
  3. Compara la plantilla generada (cdk synth) con el inventario.
  4. Adopta los recursos uno por uno con cdk import, empezando por los más simples (roles), y comprueba después de cada uno que nada cambió.
  5. Solo entonces, mejora: quita permisos que sobran, unifica nombres.

Un efecto secundario útil: describir la infraestructura en código obliga a mirar cada permiso. Casi siempre aparecen permisos que ya nadie usa.

Comandos del día a día

npx tsc --noEmit   # el código compila
npx cdk synth      # genera la plantilla de CloudFormation
npx cdk diff       # qué va a cambiar en la cuenta
npx cdk deploy     # despliega (pide confirmación si cambian permisos IAM)

Errores comunes

Preguntas frecuentes

¿CDK o Terraform?

Los dos sirven. CDK encaja si tu proyecto ya es TypeScript y solo usas AWS. Terraform tiene ventajas con varios proveedores o si tu equipo ya lo conoce. Los comparo en AWS CDK vs Terraform.

¿CDK cuesta algo?

No. Pagas los recursos que despliegas y unos céntimos por el bucket de assets del bootstrap.

¿Tengo que pasar todo a CDK de una vez?

No. Puedes empezar por las Lambdas y sus roles, que son los que más cuesta reconstruir a mano, y referenciar el resto por nombre.

Sigue leyendo