back
loading skill details...
Provides AWS CDK TypeScript patterns for defining, validating, and deploying AWS infrastructure as code. Use when creating CDK apps, stacks, and reusable…
AWS CDK TypeScript
Overview
Use this skill to build AWS infrastructure in TypeScript with reusable constructs, safe defaults, and a validation-first delivery loop.
When to Use
Use this skill when:
Creating or refactoring a CDK app, stack, or reusable construct in TypeScript
Choosing between L1, L2, and L3 constructs
Building serverless, networking, or security-focused AWS infrastructure
Wiring multi-stack applications and environment-aware deployments
Validating infrastructure changes with cdk synth, tests, cdk diff, and cdk deploy
Instructions
1. Project Initialization
# Create a new CDK app
npx cdk init app --language typescript
# Project structure
my-cdk-app/
├── bin/
│ └── my-cdk-app.ts # App entry point (instantiates stacks)
├── lib/
│ └── my-cdk-app-stack.ts # Stack definition
├── test/
│ └── my-cdk-app.test.ts # Tests
├── cdk.json # CDK configuration
├── tsconfig.json
└── package.json
2. Core Architecture
import { App, Stack, StackProps, CfnOutput, RemovalPolicy } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
// Define a reusable stack
class StorageStack extends Stack {
public readonly bucketArn: string;
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, 'DataBucket', {
versioned: true,
encryption: s3.BucketEncryption.S3_MANAGED,
removalPolicy: RemovalPolicy.RETAIN,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});
this.bucketArn = bucket.bucketArn;
new CfnOutput(this, 'BucketName', { value: bucket.bucketName });
}
}
// App entry point
const app = new App();
new StorageStack(app, 'DevStorage', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'us-east-1' },
tags: { Environment: 'dev' },
});
new StorageStack(app, 'ProdStorage', {
env: { account: '123456789012', region: 'eu-west-1' },
tags: { Environment: 'prod' },
terminationProtection: true,
});
app.synth();
3. Construct Levels
Level
Description
Use When
L1 (Cfn*)
Direct CloudFormation mapping, full control
Need properties not exposed by L2
L2
Curated with sensible defaults and helper methods
Standard resource provisioning (recommended)
L3 (Patterns)
Multi-resource architectures
Common patterns like LambdaRestApi
// L1 — Raw CloudFormation
new s3.CfnBucket(this, 'L1Bucket', { bucketName: 'my-l1-bucket' });
// L2 — Sensible defaults + grant helpers
const bucket = new s3.Bucket(this, 'L2Bucket', { versioned: true });
bucket.grantRead(myLambda);
// L3 — Multi-resource pattern
new apigateway.LambdaRestApi(this, 'Api', { handler: myLambda });
4. CDK Lifecycle Commands
cdk synth # Synthesize CloudFormation template
cdk diff # Compare deployed vs local changes
cdk deploy # Deploy stack(s) to AWS
cdk deploy --all # Deploy all stacks
cdk destroy # Tear down stack(s)
cdk ls # List all stacks in the app
cdk doctor # Check environment setup
5. Recommended Delivery Loop
Model the stack
Start with L2 constructs and extract repeated logic into custom constructs.
Run cdk synth
Checkpoint: synthesis succeeds with no missing imports, invalid props, missing context, or unresolved references.
If it fails: fix the construct configuration or context values, then rerun cdk synth.
Run infrastructure tests
Checkpoint: assertions cover IAM scope, stateful resources, and critical outputs.
If tests fail: update the stack or test expectations, then rerun the test suite.
Run cdk diff
Checkpoint: review IAM broadening, resource replacement, export changes, and deletes on stateful resources.
If the diff is risky: adjust names, dependencies, or RemovalPolicy, then rerun cdk diff.
Run cdk deploy
Checkpoint: the stack reaches CREATE_COMPLETE or UPDATE_COMPLETE.
If deploy fails: inspect CloudFormation events, fix quotas, permissions, export conflicts, or bootstrap issues, then retry cdk deploy.
Verify runtime outcomes
Confirm stack outputs, endpoints, alarms, and integrations behave as expected before moving on.
6. Cross-Stack References
// Stack A exports a value
class NetworkStack extends Stack {
public readonly vpc: ec2.Vpc;
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
this.vpc = new ec2.Vpc(this, 'Vpc', { maxAzs: 2 });
}
}
// Stack B imports it via props
interface AppStackProps extends StackProps {
vpc: ec2.Vpc;
}
class AppStack extends Stack {
constructor(scope: Construct, id: string, props: AppStackProps) {
super(scope, id, props);
new lambda.Function(this, 'Fn', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda'),
vpc: props.vpc,
});
}
}
// Wire them together
const network = new NetworkStack(app, 'Network');
new AppStack(app, 'App', { vpc: network.vpc });
Examples
Example 1: Serverless API
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
class ServerlessApiStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const table = new dynamodb.Table(this, 'Items', {
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const fn = new lambda.Function(this, 'Handler', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda'),
environment: { TABLE_NAME: table.tableName },
});
table.grantReadWriteData(fn);
new apigateway.LambdaRestApi(this, 'Api', { handler: fn });
}
}
Example 2: CDK Assertion Test
import { Template } from 'aws-cdk-lib/assertions';
import { App } from 'aws-cdk-lib';
import { ServerlessApiStack } from '../lib/serverless-api-stack';
test('creates DynamoDB table with PAY_PER_REQUEST', () => {
const app = new App();
const stack = new ServerlessApiStack(app, 'TestStack');
const template = Template.fromStack(stack);
template.hasResourceProperties('AWS::DynamoDB::Table', {
BillingMode: 'PAY_PER_REQUEST',
});
template.resourceCountIs('AWS::Lambda::Function', 1);
});
Best Practices
One concern per stack — Separate network, compute, storage, and monitoring.
Prefer L2 constructs — Drop to Cfn* only when you need unsupported properties.
Set explicit environments — Pass env with account and region; avoid implicit production targets.
Use grant helpers — Prefer .grant*() over handwritten IAM where possible.
Review the diff before deploy — Treat IAM expansion, replacement, and deletes as mandatory checkpoints.
Test infrastructure — Cover critical resources with fine-grained assertions.
Avoid hardcoded values — Use context, parameters, or environment variables.
Use the right RemovalPolicy — RETAIN for production data, DESTROY only for disposable environments.
Constraints and Warnings
CloudFormation limits — Max 500 resources per stack; split large apps into multiple stacks
Synthesis is not deployment — cdk synth only generates templates; cdk deploy applies changes
Cross-stack references create CloudFormation exports; removing them requires careful ordering
Stateful resources (RDS, DynamoDB, S3 with data) — Always set removalPolicy: RETAIN in production
Bootstrap required — Run cdk bootstrap once per account/region before first deploy
Asset bundling — Lambda code and Docker images are uploaded to the CDK bootstrap bucket
References
Detailed implementation guides are available in the references/ directory:
Core Concepts — App lifecycle, stacks, constructs, environments, assets
Serverless Patterns — Lambda, API Gateway, DynamoDB, S3 events, Step Functions
Networking & VPC — VPC design, subnets, NAT, security groups, VPC endpoints
Security Hardening — IAM, KMS, Secrets Manager, WAF, compliance
Testing Strategies — Assertions, snapshots, integration tests, CDK Nagdon't have the plugin yet? install it then click "run inline in claude" again.