Problema
Los ingenieros de software con experiencia en .NET a menudo se encuentran con dos barreras al intentar usar AWS CDK: la curva de aprendizaje de la infraestructura como código y la fricción al empaquetar y depurar Lambdas sin Docker. El patrón típico es: “Quiero lanzar una API basada en Lambda, conectarla a una base de datos y que todo quede versionado en código, pero cada paso me obliga a leer documentación de CloudFormation, a batallar con dotnet lambda package y a perder tiempo en permisos de IAM”. El resultado es un ciclo de iteración lento que desincentiva a los equipos de backend a adoptar CDK.
Causa
-
Falta de referencia concreta en .NET
La mayoría de los tutoriales oficiales están en TypeScript. Cuando se cambia a C# el flujo de empaquetado y de síntesis cambia ligeramente, lo que genera dudas sobre dónde colocar los artefactos y cómo invocarcdk synth. -
Gestión de dependencias de Lambda
Empaquetar una Lambda .NET sin Docker implica que el runtime de Lambda (Amazon Linux) difiere del entorno de desarrollo (Windows). Si el proyecto usa paquetes nativos, el binario resultante no será compatible y la función fallará al iniciar. -
Políticas de IAM implícitas
CDK crea roles automáticamente, pero al añadir recursos como RDS o SQS, es fácil olvidar los permisos desecretsmanager:GetSecretValueords-db:connect. La falta de logs claros lleva a errores de autorización que aparecen solo en CloudWatch. -
Visibilidad limitada del CloudFormation generado
Los ingenieros acostumbrados a leer código fuente pueden perderse en los miles de recursos que CloudFormation produce, lo que dificulta la depuración de errores de dependencia o de orden de creación.
Solución
Adoptar un flujo de trabajo estructurado que combine: (a) un proyecto CDK en C# bien organizado, (b) empaquetado local de Lambdas usando dotnet lambda package con la opción --framework net6.0 y --output-package, (c) pruebas unitarias de la lógica Lambda antes del despliegue, y (d) uso de cdk diff y cdk deploy --require-approval never para iteraciones rápidas.
Paso a paso
-
Inicializar el proyecto CDK en C#
mkdir MyInfra && cd MyInfra cdk init app --language csharp -
Crear una solución .NET para la Lambda
Dentro del mismo repositorio, genera un proyecto de tipoAmazon.Lambda.AspNetCoreServeroAmazon.Lambda.SimpleFunction. Mantén el proyecto bajosrc/LambdaFunction. -
Empaquetar sin Docker
dotnet lambda package --configuration Release --framework net6.0 --output-package ./bin/Release/net6.0/function.zipEste comando produce un ZIP listo para ser subido a S3 y referenciado por el construct
Functionde CDK. -
Definir la pila CDK
EnMyInfraStack.cs:using Amazon.CDK; using Amazon.CDK.AWS.Lambda; using Amazon.CDK.AWS.APIGateway; using Amazon.CDK.AWS.RDS; using Amazon.CDK.AWS.EC2; public class MyInfraStack : Stack { public MyInfraStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props) { // VPC básica var vpc = new Vpc(this, "AppVpc", new VpcProps { MaxAzs = 2 }); // Base de datos PostgreSQL var db = new DatabaseInstance(this, "Postgres", new DatabaseInstanceProps { Engine = DatabaseInstanceEngine.Postgres(new PostgresInstanceEngineProps { Version = PostgresEngineVersion.VER_13_4 }), InstanceType = InstanceType.Of(InstanceClass.BURSTABLE2, InstanceSize.SMALL), Vpc = vpc, DeletionProtection = false, Credentials = Credentials.FromGeneratedSecret("postgres") }); // Lambda var lambda = new Function(this, "MyDotnetLambda", new FunctionProps { Runtime = Runtime.DOTNET_6, Code = Code.FromAsset("./src/LambdaFunction/bin/Release/net6.0/function.zip"), Handler = "LambdaFunction::LambdaFunction.Function::FunctionHandler", Vpc = vpc, Environment = new Dictionary<string, string> { { "DB_ENDPOINT", db.InstanceEndpoint.Hostname }, { "DB_SECRET_ARN", db.Secret.SecretArn } } }); // Permisos de la Lambda para acceder a la base db.GrantConnect(lambda); // API Gateway var api = new LambdaRestApi(this, "Api", new LambdaRestApiProps { Handler = lambda, DeployOptions = new StageOptions { StageName = "prod" } }); // LogGroup con retención de 1 día new LogGroup(this, "ApiLogs", new LogGroupProps { LogGroupName = $"/aws/apigateway/{api.RestApiId}", Retention = RetentionDays.ONE_DAY, RemovalPolicy = RemovalPolicy.DESTROY }); } } -
Iterar con
cdk diff
Cada cambio en la pila se visualiza antes de aplicar:cdk diff -
Desplegar
cdk deploy --require-approval never -
Depurar
- Usa
dotnet testpara validar la lógica Lambda. - Añade
Console.WriteLiney revisa los logs en CloudWatch. - Si la Lambda no arranca, verifica la arquitectura del binario (
file function.zip) y la versión de .NET soportada por Lambda.
- Usa
Alternativas prácticas
- Docker para empaquetado: Si la Lambda depende de librerías nativas, habilita Docker con
--dockerendotnet lambda package. - Constructs de alto nivel: Usa
aws-cdk-lib/aws-apigatewayv2-alphapara HTTP APIs más ligeras. - CDK Pipelines: Cuando el proyecto crezca, migra a
CodePipelinepara CI/CD automático.
Cuándo aplicar esta solución
- Síntomas: Cada despliegue lleva más de una hora, los logs de CloudWatch muestran errores de “module not found”, o los roles IAM aparecen incompletos después de añadir recursos nuevos.
- Escenarios válidos: Equipos .NET que construyen microservicios serverless, pruebas de concepto que incluyen RDS o SQS, y proyectos donde la velocidad de iteración es crítica.
- Exclusiones: Si la arquitectura requiere contenedores ECS/Fargate o está basada en monolitos EC2, la solución enfocada en Lambda + API Gateway no será suficiente.
Código
# Inicializar CDK en C#
cdk init app --language csharp
# Empaquetar Lambda .NET sin Docker
dotnet lambda package --configuration Release --framework net6.0 --output-package ./src/LambdaFunction/bin/Release/net6.0/function.zip
# Ver diferencias antes del despliegue
cdk diff
# Desplegar sin aprobaciones interactivas
cdk deploy --require-approval never
Verificación
- Endpoint activo: Ejecuta
curl https://{api-id}.execute-api.{region}.amazonaws.com/prod/y verifica que la respuesta provenga de la Lambda. - Conexión a la BD: Desde la Lambda, escribe un registro en la tabla
testy comprueba en RDS que la fila exista. - Retención de logs: En CloudWatch, abre el LogGroup creado y confirma que la política de retención sea “1 day”.
- Roles IAM: En la consola IAM, revisa que el role asociado a la Lambda tenga la política
AWSSecretsManagerReadWriteyAmazonRDSFullAccess(o permisos más restrictivos según necesidad).
Notas adicionales
- Versiones de runtime: Lambda soporta .NET 6 y .NET 8 (preview). Mantén la versión del proyecto alineada con la que declares en
Runtime.DOTNET_6. - Secretos: Usa
SecretsManagerpara credenciales de RDS y evita hardcodear contraseñas enEnvironment. - Tiempo de despliegue: La mayor parte del tiempo se consume en la creación de la VPC y la base de datos. En entornos de desarrollo, considera usar
DatabaseInstanceFromSnapshotoRdsInstanceconDeletionProtection = falsepara acelerar la eliminación y recreación. - Depuración local:
dotnet lambda invoke-functionpermite probar la Lambda en tu máquina antes de subirla, reduciendo ciclos de error de dependencias. - Manejo de versiones: Cada despliegue crea una nueva versión de la Lambda; usa alias como
prodpara apuntar a la versión estable y evitar interrupciones durante actualizaciones.