AWS CDK in Practice: Components, Commands, and Versions
AWS CDK in Practice: Components, Commands, and Versions In the first part of this series, we covered the fundamentals of AWS CDK and why it's transforming how developers approach infrastructure as code. Now, let's dive deeper into the practical aspects of working with CDK by exploring its core components, essential commands, and version differences. Core Components of AWS CDK AWS CDK has three main building blocks that structure your infrastructure code: Source: AWS Black Belt Online Seminar App The App represents the top-level container in a CDK project. It serves as the entry point for defining stacks and their dependencies, even across multiple AWS regions. #!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; // Import stacks import { Stack1 } from '../lib/stacks/stack1'; import { Stack2 } from '../lib/stacks/stack2'; const app = new cdk.App(); // Default region stack const stack1 = new Stack1(app, 'Stack1', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION, } }); // Tokyo region stack const stack2 = new Stack2(app, 'Stack2', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: "ap-northeast-1", } }); stack2.addDependency(stack1); // Define dependency relationship // US East region stack const stack3 = new Stack2(app, 'Stack3', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: "us-east-1", } }); stack3.addDependency(stack1); // Define dependency relationship The App is responsible for: Orchestrating multiple stacks Managing cross-stack dependencies Synthesizing CloudFormation templates Coordinating deployments across regions and accounts Stack A Stack corresponds directly to a CloudFormation stack and contains the resources you want to deploy together as a unit. Each CDK stack will become a separate CloudFormation stack in your AWS account. import { StackProps } from 'aws-cdk-lib'; import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import { aws_s3 as s3, aws_ec2 as ec2, aws_iam as iam, aws_kms as kms, } from 'aws-cdk-lib'; interface Stack1Props extends StackProps { readonly param1: string; readonly param2: string; } export class Stack1 extends cdk.Stack { constructor(scope: Construct, id: string, props: Stack1Props) { super(scope, id, props); // Stack resources are defined here // ... } } Stacks allow you to: Group related resources Manage deployment boundaries Control access and permissions Handle resource dependencies Construct The Construct is where AWS resources are defined within a Stack. This is where the real magic of AWS CDK happens, as it provides three different levels of abstraction: Source: AWS Black Belt Online Seminar L1 Constructs (Low-Level) L1 Constructs, also called CloudFormation Resource Constructs, have a one-to-one mapping with CloudFormation resources. They always begin with "Cfn" and provide the closest representation to raw CloudFormation. const bucket = new s3.CfnBucket(this, "MyBucket", { bucketName: "my-specific-bucket-name", }); While L1 constructs give you complete control, they require more verbose configuration and lack the built-in best practices of higher-level constructs. L2 Constructs (High-Level) L2 Constructs wrap L1 Constructs and provide intelligent defaults and higher-level methods. They significantly reduce the amount of code you need to write by generating reasonable defaults for properties you don't explicitly specify. const bucket = new s3.Bucket(this, "MyBucket", { versioned: true, // BucketName not required - CDK will generate a unique name }); L2 Constructs provide: Sensible defaults Methods that simplify common tasks Built-in best practices Integration with other AWS services This is the level at which you'll spend most of your time working with CDK. L3 Constructs (Patterns) L3 Constructs are high-level abstractions that can create multiple related resources to implement common architectural patterns. For example, the ECS Patterns module can create all the resources needed for a load-balanced Fargate service with just a few lines of code. // Create a cluster const cluster = new ecs.Cluster(this, "MyCluster", { vpc: props.vpc, }); // Deploy a load-balanced Fargate service with a single command const loadBalancedFargateService = new ecs_patterns.ApplicationLoadBalancedFargateService( this, "MyFargateService", { cluster: cluster, cpu: 4096, memoryLimitMiB: 30720, publicLoadBalancer: true, desiredCount: 6, taskImageOptions: { image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"), }, } ); This single construct creates an ECS service, ALB, security groups, IAM roles, and more--typically equivalent to 200-300 lines of Clo

AWS CDK in Practice: Components, Commands, and Versions
In the first part of this series, we covered the fundamentals of AWS CDK and why it's transforming how developers approach infrastructure as code. Now, let's dive deeper into the practical aspects of working with CDK by exploring its core components, essential commands, and version differences.
Core Components of AWS CDK
AWS CDK has three main building blocks that structure your infrastructure code:
- Source: AWS Black Belt Online Seminar
App
The App represents the top-level container in a CDK project. It serves as the entry point for defining stacks and their dependencies, even across multiple AWS regions.
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
// Import stacks
import { Stack1 } from '../lib/stacks/stack1';
import { Stack2 } from '../lib/stacks/stack2';
const app = new cdk.App();
// Default region stack
const stack1 = new Stack1(app, 'Stack1', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
}
});
// Tokyo region stack
const stack2 = new Stack2(app, 'Stack2', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: "ap-northeast-1",
}
});
stack2.addDependency(stack1); // Define dependency relationship
// US East region stack
const stack3 = new Stack2(app, 'Stack3', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: "us-east-1",
}
});
stack3.addDependency(stack1); // Define dependency relationship
The App is responsible for:
- Orchestrating multiple stacks
- Managing cross-stack dependencies
- Synthesizing CloudFormation templates
- Coordinating deployments across regions and accounts
Stack
A Stack corresponds directly to a CloudFormation stack and contains the resources you want to deploy together as a unit. Each CDK stack will become a separate CloudFormation stack in your AWS account.
import { StackProps } from 'aws-cdk-lib';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import {
aws_s3 as s3,
aws_ec2 as ec2,
aws_iam as iam,
aws_kms as kms,
} from 'aws-cdk-lib';
interface Stack1Props extends StackProps {
readonly param1: string;
readonly param2: string;
}
export class Stack1 extends cdk.Stack {
constructor(scope: Construct, id: string, props: Stack1Props) {
super(scope, id, props);
// Stack resources are defined here
// ...
}
}
Stacks allow you to:
- Group related resources
- Manage deployment boundaries
- Control access and permissions
- Handle resource dependencies
Construct
The Construct is where AWS resources are defined within a Stack. This is where the real magic of AWS CDK happens, as it provides three different levels of abstraction:
- Source: AWS Black Belt Online Seminar
L1 Constructs (Low-Level)
L1 Constructs, also called CloudFormation Resource Constructs, have a one-to-one mapping with CloudFormation resources. They always begin with "Cfn" and provide the closest representation to raw CloudFormation.
const bucket = new s3.CfnBucket(this, "MyBucket", {
bucketName: "my-specific-bucket-name",
});
While L1 constructs give you complete control, they require more verbose configuration and lack the built-in best practices of higher-level constructs.
L2 Constructs (High-Level)
L2 Constructs wrap L1 Constructs and provide intelligent defaults and higher-level methods. They significantly reduce the amount of code you need to write by generating reasonable defaults for properties you don't explicitly specify.
const bucket = new s3.Bucket(this, "MyBucket", {
versioned: true,
// BucketName not required - CDK will generate a unique name
});
L2 Constructs provide:
- Sensible defaults
- Methods that simplify common tasks
- Built-in best practices
- Integration with other AWS services
This is the level at which you'll spend most of your time working with CDK.
L3 Constructs (Patterns)
L3 Constructs are high-level abstractions that can create multiple related resources to implement common architectural patterns. For example, the ECS Patterns module can create all the resources needed for a load-balanced Fargate service with just a few lines of code.
// Create a cluster
const cluster = new ecs.Cluster(this, "MyCluster", {
vpc: props.vpc,
});
// Deploy a load-balanced Fargate service with a single command
const loadBalancedFargateService =
new ecs_patterns.ApplicationLoadBalancedFargateService(
this,
"MyFargateService",
{
cluster: cluster,
cpu: 4096,
memoryLimitMiB: 30720,
publicLoadBalancer: true,
desiredCount: 6,
taskImageOptions: {
image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
},
}
);
This single construct creates an ECS service, ALB, security groups, IAM roles, and more--typically equivalent to 200-300 lines of CloudFormation code.
L3 Constructs are perfect when they align with your requirements, but they're less customizable and fewer are available, so most CDK development will primarily use L2 Constructs.
Essential CDK Commands
Here are the key commands you'll use when working with AWS CDK:
cdk init
This command initializes a new CDK project in your current directory. You need to specify the language you want to use:
cdk init app --language typescript
This creates the basic folder structure and configuration files needed for a CDK project.
cdk bootstrap
Before deploying CDK applications to an AWS environment for the first time, you need to bootstrap it:
cdk bootstrap
Bootstrapping creates a CloudFormation stack called "CDKToolkit" in your AWS account, which includes resources that CDK needs for deployment:
- An S3 bucket for storing templates and assets
- IAM roles for deployment operations
- ECR repositories for container images
- SSM parameters for version tracking
This only needs to be done once per account/region combination.
cdk deploy
This command deploys your CDK stacks to AWS:
cdk deploy --all # Deploy all stacks
# OR
cdk deploy Stack1 Stack2 # Deploy specific stacks
Behind the scenes, cdk deploy
:
- Synthesizes CloudFormation templates
- Uploads assets to the bootstrap bucket
- Initiates CloudFormation stack deployments
- Monitors deployment progress
cdk diff
This command compares your current CDK code with the deployed stacks:
cdk diff --all # Check differences for all stacks
# OR
cdk diff Stack1 Stack2 # Check specific stacks
This is invaluable for understanding what will change before you deploy.
cdk synth
This command synthesizes CloudFormation templates from your CDK code:
cdk synth # Generate CloudFormation templates
# OR
cdk synth --path-metadata false # Generate without metadata
# OR
cdk synth -q # Quiet mode (only show console.log output)
This is useful for:
- Checking the generated CloudFormation before deployment
- Creating templates for review or manual deployment
- Debugging resource configurations
cdk destroy
This command removes deployed stacks:
cdk destroy --all # Destroy all stacks
# OR
cdk destroy Stack1 Stack2 # Destroy specific stacks
# OR
cdk destroy --force # Force destruction (ignore dependencies)
Always use this command with caution, especially in production environments.
cdk list / cdk ls
This command lists all stacks in your CDK application:
cdk list
# OR
cdk ls
CDK Versions
CDK v2
The current version of AWS CDK is v2, which became generally available in December 2021. It introduced significant improvements over v1, most notably the consolidation of all construct libraries into a single package: aws-cdk-lib
.
In CDK v1, you had to install separate packages for each AWS service:
// CDK v1 approach
npm install @aws-cdk/aws-lambda
npm install @aws-cdk/aws-cloudfront
npm install @aws-cdk/aws-iam
npm install @aws-cdk/aws-s3
import * as cdk from "@aws-cdk/core";
import * as cloudfront from "@aws-cdk/aws-cloudfront";
import * as s3 from "@aws-cdk/aws-s3";
import * as iam from "@aws-cdk/aws-iam";
This created version compatibility challenges when packages were installed at different times.
CDK v2 simplifies things considerably:
// CDK v2 approach
npm install aws-cdk-lib
import {
aws_s3 as s3,
aws_iam as iam,
aws_cloudfront as cloudfront,
} from "aws-cdk-lib";
Recent Change: CLI and Library Separation
As of February 2025, AWS has separated the CDK CLI from the construct libraries:
// Before the split
"devDependencies": {
"aws-cdk": "2.174.0"
},
"dependencies": {
"aws-cdk-lib": "2.174.0"
}
// After the split
"devDependencies": {
"aws-cdk": "2.1000.0" // CLI now follows a new versioning scheme
},
"dependencies": {
"aws-cdk-lib": "2.180.0" // Construct library continues with the original versioning
}
To minimize the impact of this separation:
- Pin specific versions in CI/CD environments
- Manage dependencies carefully
- Use peerDependencies to manage relationships
Construct IDs: An Important Detail
When defining resources in CDK, you assign each construct a logical ID, which becomes part of the CloudFormation logical ID in the generated template:
const bucket = new s3.Bucket(this, "LogsBucket", {});
In this example, "LogsBucket" is the Construct ID. This ID:
- Must be unique within its scope
- Is used to generate the CloudFormation logical ID (e.g., "LogsBucket9C4D8843")
- Should follow PascalCase convention
- Should be simple and descriptive
- Should avoid unnecessary repetition in nested constructs
Conclusion
Understanding the core components, commands, and versioning of AWS CDK is essential for effective infrastructure development. The App, Stack, and Construct hierarchy provides a flexible framework for organizing your infrastructure code, while the different construct levels let you choose the right abstraction for each task.
CDK v2's unified package approach simplifies dependency management, and the essential commands give you fine-grained control over the development and deployment lifecycle.
In the next article, we'll explore AWS CDK development workflow, best practices for project structure, and strategies for organizing your CDK code.
Have you migrated from CloudFormation to CDK or from CDK v1 to v2? What challenges did you face? Share your experiences in the comments!
This article is part of the "Mastering AWS CDK" series, where we explore all aspects of using the AWS Cloud Development Kit for infrastructure as code.