AWS CDK: Viết Infrastructure as Code Với TypeScript
Có một câu hỏi mình nhận được khá thường xuyên từ các developer trong team: "Tại sao phải học AWS CDK trong khi Terraform đang chạy ngon lành?" Mình sẽ trả lời câu đó — nhưng trước tiên, hãy để mình kể về cái đêm mình phải debug một CloudFormation template YAML dài 3.000 dòng lúc 2 giờ sáng vì một dấu cách bị thụt lề sai. Sau đêm đó, mình quyết định chuyển sang CDK và không bao giờ nhìn lại.

AWS CDK Là Gì Và Tại Sao Nó Khác Với CloudFormation?
AWS CDK (Cloud Development Kit) là framework cho phép bạn định nghĩa hạ tầng AWS bằng ngôn ngữ lập trình thực sự — TypeScript, Python, Java, C#, Go. Về bản chất, CDK compile code của bạn thành CloudFormation template, nhưng thay vì viết YAML hay JSON, bạn viết code có type-checking, autocomplete, và có thể test được.
Nghe có vẻ chỉ là "wrapper cho CloudFormation" — nhưng thực tế khác hẳn. Với CloudFormation thuần, để tạo một Lambda function với API Gateway, EventBridge rule, và IAM role đúng chuẩn, bạn cần viết khoảng 200-300 dòng YAML. Với CDK TypeScript, con số đó rút xuống còn 20-30 dòng, và IDE sẽ báo lỗi ngay nếu bạn truyền sai parameter.
// CDK TypeScript — autocomplete + type safety
const fn = new lambda.Function(this, 'MyLambda', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('src/lambda'),
memorySize: 512,
});Khác biệt không chỉ ở độ ngắn gọn. CDK cho phép bạn dùng loop, condition, abstraction — những thứ mà YAML không thể làm được một cách tự nhiên.
Khi Nào CDK Vượt Trội Hơn Terraform?
Mình sẽ nói thẳng: Terraform không phải kẻ thù của CDK. Cả hai có use case khác nhau. Nhưng trong ngữ cảnh team dev đang build sản phẩm trên AWS, CDK có những lợi thế cụ thể.
Tái sử dụng code qua Constructs: CDK có khái niệm Construct — bạn có thể đóng gói một pattern hạ tầng thành class, publish lên npm, và dùng lại trên nhiều project. Ví dụ, mình tạo một SecureApiConstruct gồm API Gateway + Lambda + WAF + CloudWatch Alarm, rồi dùng nó ở 5 service khác nhau chỉ bằng 5 dòng code.
Type safety: Terraform dùng HCL — một DSL riêng không có type-checking thực sự trong IDE. CDK TypeScript cho bạn full IntelliSense: sai property name → red underline ngay lập tức, không phải chờ đến lúc terraform plan.
Testing hạ tầng: Với CDK, bạn có thể viết unit test kiểm tra xem stack có tạo đúng resource không. Điều này gần như không thể với CloudFormation thuần.
import { Template } from 'aws-cdk-lib/assertions';
test('Lambda should have correct runtime', () => {
const template = Template.fromStack(stack);
template.hasResourceProperties('AWS::Lambda::Function', {
Runtime: 'nodejs18.x',
MemorySize: 512,
});
});Bắt Đầu Với CDK TypeScript: Setup Thực Tế

Trước khi dive vào, bạn cần có: Node.js 18+, AWS CLI đã configure, và account AWS. Sau đó:
npm install -g aws-cdk
cdk bootstrap aws://ACCOUNT_ID/ap-southeast-1
mkdir my-infra && cd my-infra
cdk init app --language typescriptFile lib/my-infra-stack.ts là nơi bạn viết hạ tầng. Một stack cơ bản với S3, Lambda, API Gateway:
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import { Construct } from 'constructs';
export class MyInfraStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, 'DataBucket', {
versioned: true,
removalPolicy: cdk.RemovalPolicy.RETAIN,
encryption: s3.BucketEncryption.S3_MANAGED,
});
const handler = new lambda.Function(this, 'ApiHandler', {
runtime: lambda.Runtime.NODEJS_18_X,
code: lambda.Code.fromAsset('src/lambda'),
handler: 'index.handler',
environment: { BUCKET_NAME: bucket.bucketName },
memorySize: 512,
timeout: cdk.Duration.seconds(30),
});
bucket.grantRead(handler);
const api = new apigateway.RestApi(this, 'MyApi', {
restApiName: 'My Service API',
});
api.root.addMethod('GET', new apigateway.LambdaIntegration(handler));
}
}Mẹo xương máu: Luôn chạy
cdk difftrướccdk deploytrong production. CDK có thể replace resource thay vì update — và replace RDS instance thì mất data.cdk diffsẽ cảnh báo "Replacement" màu đỏ trước khi quá muộn.
Quản Lý Multi-Environment: Dev, Staging, Production
Làm sao để cùng một CDK code deploy lên 3 môi trường khác nhau với config khác nhau? Cách mình đang dùng — CDK Context kết hợp environment-specific props:
// bin/my-infra.ts
const app = new cdk.App();
const env = app.node.tryGetContext('env') || 'dev';
const envConfig = {
dev: { account: '111111111111', region: 'ap-southeast-1', minCapacity: 1 },
prod: { account: '333333333333', region: 'ap-southeast-1', minCapacity: 3 },
};
new MyInfraStack(app, `MyInfra-${env}`, {
env: envConfig[env],
envName: env,
});
// Deploy:
// cdk deploy -c env=dev
// cdk deploy -c env=prodNhững Cái Bẫy Hay Gặp Khi Dùng CDK
Logical ID thay đổi gây replace resource: CDK tạo Logical ID từ construct path. Nếu bạn đổi tên construct, Logical ID thay đổi → CloudFormation delete resource cũ và create mới. Với DynamoDB, RDS — đây là thảm họa. Giải pháp: dùng overrideLogicalId để pin ID cho resource quan trọng:
const table = new dynamodb.Table(this, 'UserTable', { ... });
(table.node.defaultChild as CfnTable).overrideLogicalId('UserTableStable');CDK bootstrap version mismatch: CDK CLI version mới hơn bootstrap version trong account sẽ báo lỗi khi deploy. Fix: chạy lại cdk bootstrap để update.
Asset bundling chậm trong CI: Nếu bạn dùng NodejsFunction với bundling, mỗi lần cdk synth sẽ bundle lại Lambda. Dùng --hotswap flag khi dev để skip CloudFormation và update Lambda code trực tiếp, tiết kiệm 60-80% thời gian deploy trong development.
Circular dependency giữa các Stack: Khi Stack A export một giá trị mà Stack B import, nếu bạn thay đổi export name, cả hai stack bị locked. Giải pháp: dùng SSM Parameter Store làm intermediary thay vì cross-stack export trực tiếp.
FAQ
Q: CDK có thể import resource đã tồn tại trên AWS không?A: Có. Dùng
from*method nhưs3.Bucket.fromBucketName(),ec2.Vpc.fromLookup(). CDK sẽ reference resource đó mà không manage lifecycle của nó — tức làcdk destroysẽ không xóa resource đó.
Q: CDK có hỗ trợ multi-account deployment không?A: Có, thông qua CDK Pipelines — một high-level construct để build CI/CD pipeline với CodePipeline. Bạn có thể định nghĩa pipeline deploy qua nhiều account (dev → staging → prod) và CDK tự handle cross-account permissions.
Q: State của CDK lưu ở đâu?A: CDK không có state file riêng. State được lưu trong CloudFormation — mỗi CDK Stack tương ứng với một CloudFormation Stack. Không cần lo về S3 backend hay state locking như Terraform, nhưng bạn phụ thuộc hoàn toàn vào CloudFormation API.
Từ Code Đến Cloud — Trải Nghiệm Thực Tế
Sau hơn 2 năm dùng CDK trong production, điều mình đánh giá cao nhất không phải là cú pháp ngắn gọn hay autocomplete — mà là việc hạ tầng trở thành first-class citizen trong codebase. Code review hạ tầng giờ diễn ra trên GitHub như review code thường, team developer có thể đọc và hiểu hạ tầng mà không cần học CloudFormation syntax riêng.
CDK cũng có learning curve ban đầu — hiểu Construct levels (L1/L2/L3), nắm lifecycle của Stack, tránh các pattern gây replace resource vô tình. Nhưng khi đã vượt qua giai đoạn đó, năng suất tăng rõ rệt. Team mình giảm được khoảng 70% thời gian viết và maintain hạ tầng so với CloudFormation thuần.
Nếu bạn đang dùng CloudFormation thuần và cảm thấy đau đớn mỗi lần sửa YAML — đó là dấu hiệu đã đến lúc thử CDK. Bắt đầu bằng một stack nhỏ, không có stateful resource, và cảm nhận sự khác biệt.
