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 Nag
1---2name: aws-cdk-33description: Provides AWS CDK TypeScript patterns for defining, validating, and deploying AWS infrastructure as code. Use when creating CDK apps, stacks, and reusable constructs, modeling serverless or VPC-based architectures, applying IAM and encryption defaults, or testing and reviewing `cdk synth`, `cdk diff`, and `cdk deploy` changes. Triggers include "aws cdk typescript", "create cdk app", "cdk stack", "cdk construct", "cdk deploy", and "cdk test".4---5
6# AWS CDK TypeScript
7
8## Overview
9
10Use this skill to build AWS infrastructure in TypeScript with reusable constructs, safe defaults, and a validation-first delivery loop.
11
12## When to Use
13
14Use this skill when:
15
16- Creating or refactoring a CDK app, stack, or reusable construct in TypeScript
17- Choosing between L1, L2, and L3 constructs
18- Building serverless, networking, or security-focused AWS infrastructure
19- Wiring multi-stack applications and environment-aware deployments
20- Validating infrastructure changes with `cdk synth`, tests, `cdk diff`, and `cdk deploy`
21
22## Instructions
23
24### 1. Project Initialization
25
26```bash
27# Create a new CDK app
28npx cdk init app --language typescript
29
30# Project structure
31my-cdk-app/
32├── bin/
33│ └── my-cdk-app.ts # App entry point (instantiates stacks)
34├── lib/
35│ └── my-cdk-app-stack.ts # Stack definition
36├── test/
37│ └── my-cdk-app.test.ts # Tests
38├── cdk.json # CDK configuration
39├── tsconfig.json
40└── package.json
41```
42
43### 2. Core Architecture
44
45```typescript
46import { App, Stack, StackProps, CfnOutput, RemovalPolicy } from 'aws-cdk-lib';
47import { Construct } from 'constructs';
48import * as s3 from 'aws-cdk-lib/aws-s3';
49
50// Define a reusable stack
51class StorageStack extends Stack {
52 public readonly bucketArn: string;
53
54 constructor(scope: Construct, id: string, props?: StackProps) {
55 super(scope, id, props);
56
57 const bucket = new s3.Bucket(this, 'DataBucket', {
58 versioned: true,
59 encryption: s3.BucketEncryption.S3_MANAGED,
60 removalPolicy: RemovalPolicy.RETAIN,
61 blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
62 });
63
64 this.bucketArn = bucket.bucketArn;
65 new CfnOutput(this, 'BucketName', { value: bucket.bucketName });
66 }
67}
68
69// App entry point
70const app = new App();
71
72new StorageStack(app, 'DevStorage', {
73 env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'us-east-1' },
74 tags: { Environment: 'dev' },
75});
76
77new StorageStack(app, 'ProdStorage', {
78 env: { account: '123456789012', region: 'eu-west-1' },
79 tags: { Environment: 'prod' },
80 terminationProtection: true,
81});
82
83app.synth();
84```
85
86### 3. Construct Levels
87
88| Level | Description | Use When |
89|-------|-------------|----------|
90| **L1** (`Cfn*`) | Direct CloudFormation mapping, full control | Need properties not exposed by L2 |
91| **L2** | Curated with sensible defaults and helper methods | Standard resource provisioning (recommended) |
92| **L3** (Patterns) | Multi-resource architectures | Common patterns like `LambdaRestApi` |
93
94```typescript
95// L1 — Raw CloudFormation
96new s3.CfnBucket(this, 'L1Bucket', { bucketName: 'my-l1-bucket' });
97
98// L2 — Sensible defaults + grant helpers
99const bucket = new s3.Bucket(this, 'L2Bucket', { versioned: true });
100bucket.grantRead(myLambda);
101
102// L3 — Multi-resource pattern
103new apigateway.LambdaRestApi(this, 'Api', { handler: myLambda });
104```
105
106### 4. CDK Lifecycle Commands
107
108```bash
109cdk synth # Synthesize CloudFormation template
110cdk diff # Compare deployed vs local changes
111cdk deploy # Deploy stack(s) to AWS
112cdk deploy --all # Deploy all stacks
113cdk destroy # Tear down stack(s)
114cdk ls # List all stacks in the app
115cdk doctor # Check environment setup
116```
117
118### 5. Recommended Delivery Loop
119
1201. **Model the stack**
121 - Start with L2 constructs and extract repeated logic into custom constructs.
122
1232. **Run `cdk synth`**
124 - Checkpoint: synthesis succeeds with no missing imports, invalid props, missing context, or unresolved references.
125 - If it fails: fix the construct configuration or context values, then rerun `cdk synth`.
126
1273. **Run infrastructure tests**
128 - Checkpoint: assertions cover IAM scope, stateful resources, and critical outputs.
129 - If tests fail: update the stack or test expectations, then rerun the test suite.
130
1314. **Run `cdk diff`**
132 - Checkpoint: review IAM broadening, resource replacement, export changes, and deletes on stateful resources.
133 - If the diff is risky: adjust names, dependencies, or `RemovalPolicy`, then rerun `cdk diff`.
134
1355. **Run `cdk deploy`**
136 - Checkpoint: the stack reaches `CREATE_COMPLETE` or `UPDATE_COMPLETE`.
137 - If deploy fails: inspect CloudFormation events, fix quotas, permissions, export conflicts, or bootstrap issues, then retry `cdk deploy`.
138
1396. **Verify runtime outcomes**
140 - Confirm stack outputs, endpoints, alarms, and integrations behave as expected before moving on.
141
142### 6. Cross-Stack References
143
144```typescript
145// Stack A exports a value
146class NetworkStack extends Stack {
147 public readonly vpc: ec2.Vpc;
148 constructor(scope: Construct, id: string, props?: StackProps) {
149 super(scope, id, props);
150 this.vpc = new ec2.Vpc(this, 'Vpc', { maxAzs: 2 });
151 }
152}
153
154// Stack B imports it via props
155interface AppStackProps extends StackProps {
156 vpc: ec2.Vpc;
157}
158class AppStack extends Stack {
159 constructor(scope: Construct, id: string, props: AppStackProps) {
160 super(scope, id, props);
161 new lambda.Function(this, 'Fn', {
162 runtime: lambda.Runtime.NODEJS_20_X,
163 handler: 'index.handler',
164 code: lambda.Code.fromAsset('lambda'),
165 vpc: props.vpc,
166 });
167 }
168}
169
170// Wire them together
171const network = new NetworkStack(app, 'Network');
172new AppStack(app, 'App', { vpc: network.vpc });
173```
174
175## Examples
176
177### Example 1: Serverless API
178
179```typescript
180import * as cdk from 'aws-cdk-lib';
181import * as lambda from 'aws-cdk-lib/aws-lambda';
182import * as apigateway from 'aws-cdk-lib/aws-apigateway';
183import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
184
185class ServerlessApiStack extends cdk.Stack {
186 constructor(scope: Construct, id: string, props?: cdk.StackProps) {
187 super(scope, id, props);
188
189 const table = new dynamodb.Table(this, 'Items', {
190 partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
191 billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
192 removalPolicy: cdk.RemovalPolicy.DESTROY,
193 });
194
195 const fn = new lambda.Function(this, 'Handler', {
196 runtime: lambda.Runtime.NODEJS_20_X,
197 handler: 'index.handler',
198 code: lambda.Code.fromAsset('lambda'),
199 environment: { TABLE_NAME: table.tableName },
200 });
201
202 table.grantReadWriteData(fn);
203
204 new apigateway.LambdaRestApi(this, 'Api', { handler: fn });
205 }
206}
207```
208
209### Example 2: CDK Assertion Test
210
211```typescript
212import { Template } from 'aws-cdk-lib/assertions';
213import { App } from 'aws-cdk-lib';
214import { ServerlessApiStack } from '../lib/serverless-api-stack';
215
216test('creates DynamoDB table with PAY_PER_REQUEST', () => {
217 const app = new App();
218 const stack = new ServerlessApiStack(app, 'TestStack');
219 const template = Template.fromStack(stack);
220
221 template.hasResourceProperties('AWS::DynamoDB::Table', {
222 BillingMode: 'PAY_PER_REQUEST',
223 });
224
225 template.resourceCountIs('AWS::Lambda::Function', 1);
226});
227```
228
229## Best Practices
230
2311. **One concern per stack** — Separate network, compute, storage, and monitoring.
2322. **Prefer L2 constructs** — Drop to `Cfn*` only when you need unsupported properties.
2333. **Set explicit environments** — Pass `env` with account and region; avoid implicit production targets.
2344. **Use grant helpers** — Prefer `.grant*()` over handwritten IAM where possible.
2355. **Review the diff before deploy** — Treat IAM expansion, replacement, and deletes as mandatory checkpoints.
2366. **Test infrastructure** — Cover critical resources with fine-grained assertions.
2377. **Avoid hardcoded values** — Use context, parameters, or environment variables.
2388. **Use the right `RemovalPolicy`** — `RETAIN` for production data, `DESTROY` only for disposable environments.
239
240## Constraints and Warnings
241
242- **CloudFormation limits** — Max 500 resources per stack; split large apps into multiple stacks
243- **Synthesis is not deployment** — `cdk synth` only generates templates; `cdk deploy` applies changes
244- **Cross-stack references** create CloudFormation exports; removing them requires careful ordering
245- **Stateful resources** (RDS, DynamoDB, S3 with data) — Always set `removalPolicy: RETAIN` in production
246- **Bootstrap required** — Run `cdk bootstrap` once per account/region before first deploy
247- **Asset bundling** — Lambda code and Docker images are uploaded to the CDK bootstrap bucket
248
249## References
250
251Detailed implementation guides are available in the `references/` directory:
252
253- [Core Concepts](references/core-concepts.md) — App lifecycle, stacks, constructs, environments, assets
254- [Serverless Patterns](references/serverless-patterns.md) — Lambda, API Gateway, DynamoDB, S3 events, Step Functions
255- [Networking & VPC](references/networking-vpc.md) — VPC design, subnets, NAT, security groups, VPC endpoints
256- [Security Hardening](references/security-hardening.md) — IAM, KMS, Secrets Manager, WAF, compliance
257- [Testing Strategies](references/testing-strategies.md) — Assertions, snapshots, integration tests, CDK Nag