You've probably heard about Infrastructure as Code. Maybe you've even written some CloudFormation YAML and felt your soul leave your body around line 847.
There's a better way.
AWS CDK (Cloud Development Kit) lets you define infrastructure using real programming languages - TypeScript, Python, Java, Go. No more YAML indentation nightmares. No more copy-pasting 200-line templates you found on Stack Overflow.
Let me show you how to go from zero to deploying real infrastructure in 10 minutes.
What is AWS CDK?
CDK is a framework that lets you define AWS resources using code. You write TypeScript (or Python, etc.), CDK converts it to CloudFormation, and deploys it.
Your Code (TypeScript) → CDK → CloudFormation → AWS Resources
The magic: you get loops, conditionals, functions, type checking, and IDE autocomplete. Things that are painful or impossible in YAML.
Setup (2 minutes)
You need Node.js and AWS CLI configured. Then:
npm install -g aws-cdk
mkdir my-first-cdk && cd my-first-cdk
cdk init app --language typescript
This creates a project structure:
my-first-cdk/
├── bin/
│ └── my-first-cdk.ts # Entry point
├── lib/
│ └── my-first-cdk-stack.ts # Your infrastructure
├── package.json
└── cdk.json
Your First Stack (3 minutes)
Open lib/my-first-cdk-stack.ts. Let's create an S3 bucket with some sensible defaults:
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
export class MyFirstCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Create an 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, // For dev only!
});
// Output the bucket name
new cdk.CfnOutput(this, 'BucketName', {
value: bucket.bucketName,
});
}
}
That's it. A secure, versioned S3 bucket in 15 lines of readable code.
The equivalent CloudFormation? Around 40 lines of YAML.
See What You're Deploying (1 minute)
Before deploying, you can see exactly what CloudFormation CDK will generate:
cdk synth
And see the diff against your current AWS state:
cdk diff
This shows you exactly what will be created, modified, or deleted. No surprises.
Deploy (2 minutes)
cdk bootstrap # First time only, sets up CDK in your account
cdk deploy
Watch your terminal. CDK creates a CloudFormation stack, provisions the resources, and shows you the outputs.
Done. You have a production-ready S3 bucket.
Why CDK Beats YAML
Let me show you the real power. Say you need 3 buckets for different environments:
const environments = ['dev', 'staging', 'prod'];
environments.forEach(env => {
new s3.Bucket(this, `DataBucket-${env}`, {
bucketName: `myapp-data-${env}-${this.account}`,
versioned: env === 'prod', // Only version prod
lifecycleRules: env !== 'prod' ? [{
expiration: cdk.Duration.days(30), // Auto-cleanup non-prod
}] : [],
});
});
Try doing that cleanly in YAML. I'll wait.
Real-World Pattern: Lambda + API Gateway
Here's something more practical - a serverless API:
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigw from 'aws-cdk-lib/aws-apigateway';
// Create Lambda function
const handler = new lambda.Function(this, 'ApiHandler', {
runtime: lambda.Runtime.NODEJS_18_X,
code: lambda.Code.fromAsset('lambda'),
handler: 'index.handler',
timeout: cdk.Duration.seconds(30),
memorySize: 256,
});
// Create API Gateway
const api = new apigw.RestApi(this, 'MyApi', {
restApiName: 'My Service',
});
// Connect them
api.root.addMethod('GET', new apigw.LambdaIntegration(handler));
Lambda function + API Gateway + all the IAM permissions, wired up correctly. In 20 lines.
Cost Optimization Patterns
One thing I've learned building CloudPruneAI: most cloud waste comes from missing simple configurations. CDK makes it easy to enforce good defaults:
// Always set log retention (CloudWatch logs grow forever by default!)
import * as logs from 'aws-cdk-lib/aws-logs';
const myFunction = new lambda.Function(this, 'MyFunction', {
// ... config
logRetention: logs.RetentionDays.TWO_WEEKS, // Don't pay for logs forever
});
// Always use lifecycle rules on S3
new s3.Bucket(this, 'LogsBucket', {
lifecycleRules: [{
transitions: [{
storageClass: s3.StorageClass.INTELLIGENT_TIERING,
transitionAfter: cdk.Duration.days(30),
}],
expiration: cdk.Duration.days(365),
}],
});
These patterns prevent the "silent cost creep" that hits most AWS accounts.
Destroying Resources
When you're done experimenting:
cdk destroy
CDK removes everything it created. Clean.
Common Gotchas
1. Bootstrap once per account/region
cdk bootstrap aws://ACCOUNT_ID/REGION
2. Stateful resources need care
By default, CDK protects databases and buckets from accidental deletion. For dev environments:
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true, // For S3 buckets
3. Use cdk diff before deploy
Always. It's saved me from many "oops" moments.
Next Steps
- Read constructs: Browse Construct Hub for pre-built patterns
-
Try L2 constructs: Higher-level abstractions like
ApplicationLoadBalancedFargateServicethat wire up 10+ resources in one line -
Organize with stages: Use
cdk.Stagefor multi-environment deployments
Resources
- AWS CDK Documentation
- CDK Patterns - Real-world examples
- Construct Hub - Community constructs
CDK changed how I think about infrastructure. It's not a config file you maintain - it's code you can test, refactor, and actually understand 6 months later.
Give it 10 minutes. Your future self will thank you.
What's your experience with IaC? Still using CloudFormation YAML, or have you moved to CDK/Terraform? Drop a comment below.
Top comments (0)