AWS Cloud Development Kit (CDK) enables defining infrastructure using familiar programming languages—TypeScript, Python, Java, C#, and Go. Instead of writing YAML/JSON, you use constructs, loops, conditionals, and functions. CDK synthesizes to CloudFormation, providing the best of imperative programming with declarative deployment. This guide covers CDK patterns, construct levels, and best practices for production deployments.
CDK Architecture
flowchart TB
Code["CDK App (TypeScript)"] --> Synth["cdk synth"]
Synth --> CFN["CloudFormation Template"]
CFN --> Deploy["cdk deploy"]
Deploy --> AWS["AWS Resources"]
style Code fill:#E1F5FE,stroke:#0277BD
style CFN fill:#FFF3E0,stroke:#E65100
Construct Levels
- L1 (Cfn*): Direct CloudFormation resources
- L2: Higher-level constructs with sensible defaults
- L3 (Patterns): Complete solution patterns
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigw from 'aws-cdk-lib/aws-apigateway';
export class ApiStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// L2 Construct: Lambda function with sensible defaults
const handler = new lambda.Function(this, 'Handler', {
runtime: lambda.Runtime.NODEJS_18_X,
code: lambda.Code.fromAsset('lambda'),
handler: 'index.handler',
memorySize: 256,
timeout: cdk.Duration.seconds(30),
environment: {
TABLE_NAME: table.tableName,
},
});
// L3 Construct: LambdaRestApi (complete pattern)
const api = new apigw.LambdaRestApi(this, 'Api', {
handler,
proxy: false,
});
const orders = api.root.addResource('orders');
orders.addMethod('GET');
orders.addMethod('POST');
}
}
Aspects for Cross-Cutting Concerns
import { IAspect, Stack, Tags } from 'aws-cdk-lib';
import { IConstruct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
class BucketSecurityAspect implements IAspect {
visit(node: IConstruct): void {
if (node instanceof s3.Bucket) {
// Enforce encryption on all buckets
const bucket = node as s3.CfnBucket;
bucket.addPropertyOverride('BucketEncryption', {
ServerSideEncryptionConfiguration: [{
ServerSideEncryptionByDefault: {
SSEAlgorithm: 'aws:kms'
}
}]
});
}
}
}
// Apply to entire app
cdk.Aspects.of(app).add(new BucketSecurityAspect());
Testing with CDK Assertions
import { Template } from 'aws-cdk-lib/assertions';
import * as cdk from 'aws-cdk-lib';
import { ApiStack } from '../lib/api-stack';
test('Lambda function created with correct memory', () => {
const app = new cdk.App();
const stack = new ApiStack(app, 'TestStack');
const template = Template.fromStack(stack);
template.hasResourceProperties('AWS::Lambda::Function', {
MemorySize: 256,
Runtime: 'nodejs18.x',
});
});
test('API Gateway has expected routes', () => {
template.resourceCountIs('AWS::ApiGateway::Method', 2);
});
Key Takeaways
- CDK uses TypeScript/Python for infrastructure code
- L2 constructs provide sensible defaults
- L3 patterns implement complete solutions
- Aspects enable cross-cutting security policies
- CDK Assertions enable infrastructure unit tests
- Synthesizes to CloudFormation for deployment
Discover more from C4: Container, Code, Cloud & Context
Subscribe to get the latest posts sent to your email.