Skip to content

AWS CDK (Cloud Development Kit)

Chapter 34: AWS CDK - Cloud Development Kit

Section titled “Chapter 34: AWS CDK - Cloud Development Kit”

Infrastructure as Code with Programming Languages

Section titled “Infrastructure as Code with Programming Languages”

AWS Cloud Development Kit (CDK) is an open-source software development framework to define cloud infrastructure in code and provision it through AWS CloudFormation.

AWS CDK Overview
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | AWS CDK | |
| +------------------------+ |
| | |
| +---------------------+---------------------+ |
| | | | |
| v v v |
| +----------+ +----------+ +----------+ |
| | Code in | | CDK | | CloudFor-| |
| | Language | | Synthesize| | mation | |
| | | | | | Template | |
| | - Type | | - CFN | | | |
| | Script | | Template| | - Deploy | |
| | - Python | | - YAML/ | | - Manage | |
| | - Java | | JSON | | | |
| +----------+ +----------+ +----------+ |
| |
+------------------------------------------------------------------+
LanguageFile ExtensionNotes
TypeScript.tsPrimary language, best support
JavaScript.jsNode.js runtime
Python.pyPopular for DevOps
Java.javaEnterprise applications
C#.cs.NET developers
Go.goGrowing support

CDK Architecture
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | CDK App | |
| +------------------------+ |
| | |
| +---------------------+---------------------+ |
| | | | |
| v v v |
| +----------+ +----------+ +----------+ |
| | Stack 1 | | Stack 2 | | Stack N | |
| | | | | | | |
| | - VPC | | - ECS | | - Lambda | |
| | - Subnets| | - ALB | | - API GW | |
| +----------+ +----------+ +----------+ |
| | | | |
| v v v |
| +----------+ +----------+ +----------+ |
| | Constructs| | Constructs| | Constructs| |
| | | | | | | |
| | - L1 | | - L2 | | - L3 | |
| | - L2 | | - L3 | | | |
| +----------+ +----------+ +----------+ |
| |
+------------------------------------------------------------------+
CDK Construct Levels
+------------------------------------------------------------------+
| |
| L1 Constructs (Cfn Resources) |
| +------------------------------------------------------------+ |
| | | |
| | - Direct mapping to CloudFormation resources | |
| | - Names start with "Cfn" (e.g., CfnBucket, CfnVPC) | |
| | - Requires all properties to be set | |
| | - No defaults or validation | |
| | | |
| | Example: | |
| | new CfnBucket(this, 'MyBucket', { | |
| | bucketName: 'my-bucket', | |
| | versioningConfiguration: { | |
| | status: 'Enabled' | |
| | } | |
| | }); | |
| | | |
| +------------------------------------------------------------+ |
| |
| L2 Constructs (Higher-Level) |
| +------------------------------------------------------------+ |
| | | |
| | - AWS best practices built-in | |
| | - Sensible defaults | |
| | - Helper methods and properties | |
| | - Most commonly used | |
| | | |
| | Example: | |
| | new Bucket(this, 'MyBucket', { | |
| | versioned: true, | |
| | encryption: BucketEncryption.S3_MANAGED, | |
| | blockPublicAccess: BlockPublicAccess.BLOCK_ALL | |
| | }); | |
| | | |
| +------------------------------------------------------------+ |
| |
| L3 Constructs (Patterns) |
| +------------------------------------------------------------+ |
| | | |
| | - Complete solutions for common use cases | |
| | - Multiple resources bundled together | |
| | - Opinionated configurations | |
| | | |
| | Examples: | |
| | - aws-cdk-lib/aws-apigateway.LambdaRestApi | |
| | - aws-cdk-lib/aws-ecs-patterns.ApplicationLoadBalancedFargateService |
| | - aws-cdk-lib/aws-s3-deployment.BucketDeployment | |
| | | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

CDK Project Structure
+------------------------------------------------------------------+
| |
| my-cdk-app/ |
| +------------------------------------------------------------+ |
| | | |
| | cdk.json # CDK configuration | |
| | package.json # Node.js dependencies | |
| | tsconfig.json # TypeScript configuration | |
| | cdk.context.json # Context values (cached) | |
| | | |
| | bin/ | |
| | my-cdk-app.ts # Entry point | |
| | | |
| | lib/ | |
| | my-cdk-app-stack.ts # Stack definition | |
| | my-cdk-app-stack.ts # Additional stacks | |
| | | |
| | test/ | |
| | my-cdk-app.test.ts # Unit tests | |
| | | |
| | node_modules/ # Dependencies | |
| | | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class MyCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create VPC
const vpc = new ec2.Vpc(this, 'MyVpc', {
maxAzs: 2,
natGateways: 1,
});
// Create S3 Bucket
const bucket = new s3.Bucket(this, 'MyBucket', {
versioned: true,
encryption: s3.BucketEncryption.S3_MANAGED,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
// Create Lambda Function
const fn = new lambda.Function(this, 'MyFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromInline(`
exports.handler = async function(event) {
console.log("request:", JSON.stringify(event, undefined, 2));
return {
statusCode: 200,
headers: { "Content-Type": "text/plain" },
body: "Hello from CDK!"
};
};
`),
});
// Grant Lambda permission to read from bucket
bucket.grantRead(fn);
}
}
from aws_cdk import (
Stack,
aws_ec2 as ec2,
aws_s3 as s3,
aws_lambda as lambda_,
RemovalPolicy,
)
from constructs import Construct
class MyCdkStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# Create VPC
vpc = ec2.Vpc(
self, "MyVpc",
max_azs=2,
nat_gateways=1,
)
# Create S3 Bucket
bucket = s3.Bucket(
self, "MyBucket",
versioned=True,
encryption=s3.BucketEncryption.S3_MANAGED,
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
removal_policy=RemovalPolicy.DESTROY,
)
# Create Lambda Function
fn = lambda_.Function(
self, "MyFunction",
runtime=lambda_.Runtime.NODEJS_18_X,
handler="index.handler",
code=lambda_.Code.from_inline("""
exports.handler = async function(event) {
return {
statusCode: 200,
body: "Hello from CDK!"
};
};
"""),
)
# Grant Lambda permission to read from bucket
bucket.grant_read(fn)

Terminal window
# Initialize new CDK app
cdk init app --language typescript
cdk init app --language python
cdk init app --language java
# Bootstrap CDK (first time setup)
cdk bootstrap aws://123456789012/us-east-1
# Synthesize CloudFormation template
cdk synth
# Synthesize specific stack
cdk synth MyStack
# Diff changes
cdk diff
# Deploy stack
cdk deploy
# Deploy specific stack
cdk deploy MyStack
# Deploy with parameters
cdk deploy --parameters Env=prod
# Deploy with context
cdk deploy --context key=value
# List stacks
cdk list
cdk ls
# Destroy stack
cdk destroy
# Destroy specific stack
cdk destroy MyStack
# Watch for changes (hotswap)
cdk deploy --hotswap
# Import existing resource
cdk import
# Generate CloudFormation template
cdk synth > template.yaml
# Context management
cdk context --list
cdk context --clear
# Doctor (troubleshooting)
cdk doctor

// Create VPC with custom configuration
const vpc = new ec2.Vpc(this, 'MyVpc', {
ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
maxAzs: 3,
natGateways: 1,
subnetConfiguration: [
{
cidrMask: 24,
name: 'Public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: 'Private',
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
},
{
cidrMask: 28,
name: 'Database',
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
gatewayEndpoints: {
S3: ec2.GatewayVpcEndpointAwsService.S3,
},
});
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecs_patterns from 'aws-cdk-lib/aws-ecs-patterns';
// Create ECS cluster
const cluster = new ecs.Cluster(this, 'MyCluster', {
vpc: vpc,
});
// Create Fargate service with load balancer
const fargateService = new ecs_patterns.ApplicationLoadBalancedFargateService(
this,
'MyFargateService',
{
cluster: cluster,
cpu: 256,
desiredCount: 2,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry('nginx:latest'),
containerPort: 80,
},
memoryLimitMiB: 512,
publicLoadBalancer: true,
}
);
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as lambda from 'aws-cdk-lib/aws-lambda';
// Create Lambda function
const fn = new lambda.Function(this, 'MyFunction', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda'),
});
// Create API Gateway
const api = new apigateway.LambdaRestApi(this, 'MyApi', {
handler: fn,
restApiName: 'My Service API',
defaultCorsPreflightOptions: {
allowOrigins: apigateway.Cors.ALL_ORIGINS,
allowMethods: apigateway.Cors.ALL_METHODS,
},
});

CDK Pipelines Architecture
+------------------------------------------------------------------+
| |
| +------------------------+ |
| | CDK Pipeline | |
| +------------------------+ |
| | |
| +---------------------+---------------------+ |
| | | | |
| v v v |
| +----------+ +----------+ +----------+ |
| | Source | | Build | | Deploy | |
| | Stage | | Stage | | Stage | |
| | | | | | | |
| | - Code | | - Synth | | - Dev | |
| | Commit | | - Test | | - Stage | |
| | | | | | - Prod | |
| +----------+ +----------+ +----------+ |
| |
+------------------------------------------------------------------+
import * as codecommit from 'aws-cdk-lib/aws-codecommit';
import * as pipelines from 'aws-cdk-lib/pipelines';
// Create repository
const repo = new codecommit.Repository(this, 'MyRepo', {
repositoryName: 'my-repo',
});
// Create pipeline
const pipeline = new pipelines.CodePipeline(this, 'MyPipeline', {
synth: new pipelines.ShellStep('Synth', {
input: pipelines.CodePipelineSource.codeCommit(repo, 'main'),
commands: [
'npm ci',
'npm run build',
'npx cdk synth',
],
}),
});
// Add stages
const devStage = pipeline.addStage(new MyStack(this, 'Dev', {
env: { account: '123456789012', region: 'us-east-1' },
}));
const prodStage = pipeline.addStage(new MyStack(this, 'Prod', {
env: { account: '123456789013', region: 'us-east-1' },
}));
// Add manual approval before prod
prodStage.addPre(new pipelines.ManualApprovalStep('ApproveDeployment'));

CDK Best Practices
+------------------------------------------------------------------+
| |
| 1. Use L2 constructs over L1 when possible |
| +------------------------------------------------------------+ |
| | - L2 constructs have best practices built-in | |
| | - Less code to write | |
| +------------------------------------------------------------+ |
| |
| 2. Create reusable constructs |
| +------------------------------------------------------------+ |
| | - Extend Construct class | |
| | - Encapsulate common patterns | |
| +------------------------------------------------------------+ |
| |
| 3. Use context for environment-specific values |
| +------------------------------------------------------------+ |
| | - Use cdk.json for configuration | |
| | - Use --context flag for overrides | |
| +------------------------------------------------------------+ |
| |
| 4. Write tests for stacks |
| +------------------------------------------------------------+ |
| | - Use Jest for TypeScript | |
| | - Test resource creation and properties | |
| +------------------------------------------------------------+ |
| |
| 5. Use CDK Pipelines for CI/CD |
| +------------------------------------------------------------+ |
| | - Automate deployments | |
| | - Use stages for environments | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

CDK is a powerful abstraction over CloudFormation, enabling developers to define infrastructure using familiar programming languages. SREs benefit from type safety, testing, and reusability.

CDK in DevOps/SRE
+------------------------------------------------------------------+
| |
| SRE Infrastructure Principles: |
| |
| 1. Infrastructure as Code with Programming Languages |
| +----------------------------------------------------------+ |
| | - Use TypeScript/Python/Java for infrastructure | |
| | - Leverage IDE features: autocomplete, refactoring | |
| | - Write unit tests for infrastructure logic | |
| +----------------------------------------------------------+ |
| |
| 2. Composability & Reusability |
| +----------------------------------------------------------+ |
| | - Create custom constructs for organization patterns | |
| | - Publish constructs to internal registries | |
| | - Share tested patterns across teams | |
| +----------------------------------------------------------+ |
| |
| 3. Safe Deployments |
| +----------------------------------------------------------+ |
| | - cdk diff shows exact CloudFormation changes | |
| | - cdk synth generates template for review | |
| | - CDK Pipelines provide self-mutating CI/CD | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Terminal window
# Install Node.js and CDK on Arch Linux
sudo pacman -S nodejs npm python-pip
gnpm install -g aws-cdk
# CDK project setup
cdk init app --language typescript
# Common CDK commands
cdk list # List stacks
cdk synth my-stack # Generate CloudFormation
cdk diff my-stack # Show changes
cdk deploy my-stack # Deploy stack
cdk destroy my-stack # Delete stack
# CDK bootstrap (one-time per account/region)
cdk bootstrap aws://123456789012/us-east-1
# CDK Pipeline setup
cdk init --pipeline-type classic-app --language typescript

CDK Anti-Patterns
+------------------------------------------------------------------+
| |
| ❌ Mistake 1: Using L1 Constructs for Everything |
| +----------------------------------------------------------+ |
| | Problem: Writing verbose Cfn* resources directly | |
| | Impact: More code, less best practices | |
| | Fix: Use L2/L3 constructs when available | |
| +----------------------------------------------------------+ |
| |
| ❌ Mistake 2: Not Running cdk synth Before Deploy |
| +----------------------------------------------------------+ |
| | Problem: Deploying without seeing generated CloudFormation| |
| | Impact: Unexpected resource changes in production | |
| | Fix: Always run cdk diff/synth before deploy | |
| +----------------------------------------------------------+ |
| |
| ❌ Mistake 3: Hardcoding Credentials in Code |
| +----------------------------------------------------------+ |
| | Problem: Secrets in source code | |
| | Impact: Security vulnerabilities, credential exposure | |
| | Fix: Use Secrets Manager or Parameter Store | |
| +----------------------------------------------------------+ |
| |
| ❌ Mistake 4: Not Testing CDK Stacks |
| +----------------------------------------------------------+ |
| | Problem: No unit tests for infrastructure code | |
| | Impact: Bugs reach production, unexpected failures | |
| | Fix: Write Jest/Pytest unit tests for stacks | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

  1. Q: Explain the difference between L1, L2, and L3 CDK constructs.

    • A: L1 constructs are direct CloudFormation resources (CfnBucket, CfnVPC). They mirror CloudFormation exactly. L2 constructs are higher-level with additional defaults, convenience methods, and best practices (Bucket, Vpc). L3 constructs are complete patterns solving specific problems (aws-ecs-patterns.ApplicationLoadBalancedFargateService).
  2. Q: How does CDK Pipelines work?

    • A: CDK Pipelines are self-mutating: the pipeline stack is defined in CDK, and when it runs, it can update itself based on changes to the CDK code. It uses CodePipeline internally and supports stages, waves, and manual approvals. The pipeline automatically provisions the environment needed to deploy the CDK app.
  1. Q: Design a multi-account deployment strategy using CDK.
    • A: Use CDK Pipelines: define a pipeline in the tools account that deploys to dev/staging/prod accounts using cross-account roles. Use StackSynthesizer for different accounts. Use SSM parameters or Secrets Manager for cross-account secrets. Implement approval gates between environments. Use context flags for environment-specific configurations.

Exam Tip

Key Exam Points
+------------------------------------------------------------------+
| |
| 1. CDK generates CloudFormation templates |
| |
| 2. L1 constructs = CFN resources (Cfn prefix) |
| |
| 3. L2 constructs = Higher-level with best practices |
| |
| 4. L3 constructs = Complete patterns/solutions |
| |
| 5. CDK supports TypeScript, Python, Java, C#, Go |
| |
| 6. Use cdk synth to generate CloudFormation template |
| |
| 7. Use cdk diff to see changes before deployment |
| |
| 8. CDK Pipelines for CI/CD of infrastructure |
| |
| 9. Bootstrap is required for first-time CDK use |
| |
| 10. Constructs are the building blocks of CDK apps |
| |
+------------------------------------------------------------------+

Chapter 34 Summary
+------------------------------------------------------------------+
| |
| AWS CDK Core Concepts |
| +------------------------------------------------------------+ |
| | - Infrastructure as Code using programming languages | |
| | - Synthesizes to CloudFormation templates | |
| | - Supports multiple languages | |
| +------------------------------------------------------------+ |
| |
| Construct Levels |
| +------------------------------------------------------------+ |
| | - L1: Direct CFN mapping (CfnXxx) | |
| | - L2: Higher-level with defaults | |
| | - L3: Complete patterns | |
| +------------------------------------------------------------+ |
| |
| Key Commands |
| +------------------------------------------------------------+ |
| | - cdk init: Create new project | |
| | - cdk synth: Generate template | |
| | - cdk deploy: Deploy stack | |
| | - cdk diff: Show changes | |
| +------------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Previous Chapter: Chapter 33: AWS CloudFormation - Infrastructure as Code Next Chapter: Chapter 35: AWS Systems Manager