Introduction
The other day, I had an interview with a bank for a Platform Engineer role, and one of the main tools they use is AWS CDK. Up until then, my go-to Infrastructure as Code tool had always been Terraform, so I was curious: How does CDK really work, and what should someone know before using it in the real world?
That curiosity gave me the idea for this blog. Instead of just keeping my learnings to myself, I wanted to share them with you in a beginner-friendly way. Throughout this series, I’ll be creating multiple small projects to break down the concepts step by step, so it’s easier to follow along and connect the dots. My goal is to give you a clear picture of what you need to know before using AWS CDK in any company or environment—so that when the time comes, you’ll be ready to confidently create and manage AWS resources.
What is CDK?
Before diving into the details, let’s start with the basics—what exactly is AWS CDK?
AWS CDK (Cloud Development Kit) is an open-source Infrastructure as Code (IaC) framework that allows you to define your cloud resources using familiar programming languages like Python, TypeScript, Java, or C#. Instead of writing long YAML or JSON templates, you can write actual code to describe your infrastructure.
Think of it like this:
- With Terraform/CloudFormation, you usually declare what you want in a static way.
- With AWS CDK, you get the power of a full programming language to describe what you want, along with the flexibility to use loops, conditions, and abstractions to make it reusable.
In short, AWS CDK lets you bring your developer mindset into infrastructure management.
Understanding Important AWS CDK Core Concepts
Projects
An AWS CDK project is just the folder where all your infrastructure code lives.
When you create a CDK project, some files are always there no matter which language you choose, while others are specific to the language you’re using.
-
Universal Files & Folders (common in all CDK projects)
-
.git/
→ Git repo for version control (if Git is installed). -
.gitignore
→ Tells Git which files to ignore. -
README.md
→ Basic instructions for your project (you can update it). -
cdk.json
→ Configuration for CDK (how to run the app).
-
-
Python-Specific Files & Folders
-
.venv/
→ Python virtual environment (for dependencies). -
app.py
→ The entry point of your CDK app (like main() in a program). -
my_cdk_project/
→ Folder with your stack files (where you define resources).-
__init__.py
→ Makes it a Python package. -
*_stack.py
→ Defines your AWS resources.
-
-
requirements.txt
→ Lists your Python dependencies. -
requirements-dev.txt
→ Extra dependencies for development only. -
source.bat
→ Helper script for Windows users. -
tests/
→ Python unit tests for your stacks.
-
Constructs
Constructs are the basic building blocks of an AWS CDK application.
Think of them as Lego blocks each block represents one or more AWS resources (like an S3 bucket, a Lambda function, or a VPC) along with its configuration.
When you build a CDK app, you’re really just putting together constructs piece by piece to describe your infrastructure.
Types / Levels of Constructs
Level | Definition | Example | When to Use |
---|---|---|---|
L1 | Direct CloudFormation resources (lowest-level) | s3.CfnBucket |
For full control or new AWS features not yet in L2/L3 |
L2 | AWS CDK abstractions with defaults and helpers | s3.Bucket |
Everyday usage (balance of flexibility and ease) |
L3 | Pre-built patterns (multiple resources together) | apigateway.LambdaRestApi |
Quick solutions for common use cases |
Apps
An App in AWS CDK is like the top-level container for everything you build.
- An App can have one or many Stacks.
- Each Stack is a group of AWS resources.
- Each resource is created using Constructs.
So the structure looks like this:
App → Stack(s) → Construct(s) → AWS Resource(s)
Think of it like a house:
- The App = the whole house.
- The Stacks = rooms in the house (living room, kitchen, bedroom).
- The Constructs = furniture in each room (sofa, fridge, bed).
Example: Simple CDK App in Python
#!/usr/bin/env python3
import aws_cdk as cdk
from aws_cdk import aws_s3 as s3
class MyFirstStack(cdk.Stack):
def __init__(self, scope: cdk.App, id: str, **kwargs):
super().__init__(scope, id, **kwargs)
# Construct: An S3 bucket
s3.Bucket(self, "MyFirstBucket",
versioned=True,
removal_policy=cdk.RemovalPolicy.DESTROY)
app = cdk.App() # App (the whole house)
MyFirstStack(app, "MyFirstStack") # Stack (a room in the house)
app.synth()
What’s Happening Here?
-
App →
cdk.App()
is the root of your project. -
Stack →
MyFirstStack
groups resources into one deployable unit. -
Construct →
s3.Bucket
is the actual AWS resource created inside the stack.
When you deploy this app, AWS CDK creates a CloudFormation stack with one S3 bucket.
AWS CDK Stacks
A Stack in AWS CDK is the smallest unit you can deploy.
It’s basically a box that holds a group of AWS resources (like an S3 bucket, a Lambda function, or a VPC).
When you deploy a stack, all the resources inside it are created together in AWS as a CloudFormation stack.
- If you delete the stack → all the resources inside it are deleted.
- If you update the stack → AWS updates those resources as a group.
Example: A Simple CDK Stack (Python)
#!/usr/bin/env python3
import aws_cdk as cdk
from aws_cdk import aws_s3 as s3
class MyStorageStack(cdk.Stack):
def __init__(self, scope: cdk.App, id: str, **kwargs):
super().__init__(scope, id, **kwargs)
# Construct: An S3 bucket
s3.Bucket(self, "MyStorageBucket",
versioned=True,
removal_policy=cdk.RemovalPolicy.DESTROY)
app = cdk.App()
MyStorageStack(app, "MyStorageStack")
app.synth()
AWS CDK Stages
A Stage in AWS CDK is a way to group one or more stacks together and treat them as a single deployment unit.
Stages are especially useful when you want to promote your infrastructure through different environments like Dev → Test → Prod. Instead of deploying stacks one by one, you wrap them inside a stage and deploy the stage as a whole.
Example: Stage with Multiple Stacks (Python)
stacks/app_stack.py
from aws_cdk import Stack
from constructs import Construct
# Define the app stack
class AppStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# The code that defines your application goes here
stacks/database_stack.py
from aws_cdk import Stack
from constructs import Construct
# Define the database stack
class DatabaseStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
# The code that defines your database goes here
my_stage.py
from aws_cdk import Stage
from constructs import Construct
from .app_stack import AppStack
from .database_stack import DatabaseStack
# Define the stage
class MyAppStage(Stage):
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# Add both stacks to the stage
AppStack(self, "AppStack")
DatabaseStack(self, "DatabaseStack")
app.py
#!/usr/bin/env python3
import os
import aws_cdk as cdk
from cdk_demo_app.my_stage import MyAppStage
# Create a CDK app
app = cdk.App()
# Create the development stage
MyAppStage(app, 'Dev',
env=cdk.Environment(account='123456789012', region='us-east-1'),
)
# Create the production stage
MyAppStage(app, 'Prod',
env=cdk.Environment(account='098765432109', region='us-east-1'),
)
app.synth()
Conclusion
That’s a wrap on the beginner’s guide to AWS CDK—we covered what CDK is, how projects are structured, and the core pieces (apps → stacks → constructs → resources), with simple examples to get you moving.
Next up, I’ll dive into real-world setups: building and deploying multi-stack apps across multiple environments (Dev/Test/Prod) and setting up a pipeline using AWS CDK with Python. We’ll also touch on cross-stack references, environment configs, and CDK Pipelines basics.