All Articles
Infrastructure as Code with AWS CDK
5 min read

Infrastructure as Code with AWS CDK

Nicolai Lang

Nicolai Lang

AWS Serverless expert. Advises and supports teams in building scalable cloud architectures.

An API Gateway, a couple of Lambdas, DynamoDB, S3. IAM, CloudWatch Logs, a few alarms. Manageable enough without Infrastructure as Code. A few clicks in the Console and you're up and running.

But it never stays that simple. Next feature, next Lambda, new routes in the API Gateway. Then you need an SQS queue, an EventBridge rule gets added. The infrastructure keeps growing - and every time, you're back in the Console creating or modifying resources by hand. Wouldn't it be nice to automate and version all of this? To have a single source of truth for what the infrastructure should look like?

What changes with Infrastructure as Code

That's exactly what Infrastructure as Code (IaC) is for. Instead of clicking through the Console, you define your infrastructure in code - versioned in Git, reviewable through pull requests, and most importantly, reproducible.

Want to test in a sandbox account? Just another deploy. Staging needs a different config than production? One parameter. Someone changed the infrastructure and nobody knows what? Git blame. A deployment goes sideways? Roll back to the last working state. Everything that's opaque and error-prone in the Console becomes traceable and automatable.

That sounds like extra effort and a learning curve. And with traditional tools like CloudFormation or Terraform, it does feel clunky. The C in IaC isn't really code - it's YAML. Complex configuration files for a handful of resources, a custom syntax, and the documentation permanently open in another tab because you keep looking up property names and allowed values.

Sure, the infrastructure is reproducible now - but the real problems just shift. As projects grow, templates get unwieldy fast, and with that complexity comes the same error-proneness you were trying to escape. On top of that, reusability is limited: want to use a CloudFormation template for a proven setup in another project? Copy-paste, tweak, hope you didn't miss anything. That's not real modularity.

AWS CDK: Infrastructure as real code - and part of your software project

AWS CDK is an open-source framework that lets you define AWS infrastructure in a real programming language. No YAML, no JSON, no HCL. Instead, you write code that feels like a normal software project: with variables, functions, and types. Your IDE gives you autocompletion and catches errors before you deploy. That documentation tab you keep open for CloudFormation? With CDK, you mostly don't need it. Autocompletion and type information let you explore the API naturally.

CDK supports TypeScript, Python, Java, and Go. In this article - and in practice - we use TypeScript: it's the best-supported language in the CDK ecosystem, CDK itself is written in it, and most community examples and patterns are built around it.

Under the hood, CDK generates CloudFormation templates and deploys them. So you get the full stability and rollback behavior of CloudFormation - without having to write its templates by hand.

What does that look like? Let's take the example from the beginning - an API with Lambda and DynamoDB:

const table = new dynamodb.Table(this, "Items", {
  partitionKey: { name: "id", type: dynamodb.AttributeType.STRING },
});

const fn = new lambda.Function(this, "Handler", {
  runtime: lambda.Runtime.NODEJS_24_X,
  handler: "index.handler",
  code: lambda.Code.fromAsset("lambda"),
});

table.grantReadWriteData(fn);

new apigw.LambdaRestApi(this, "Api", { handler: fn });

That's all the infrastructure you need. Table, function, API - and grantReadWriteData wires up the IAM permissions between them. No hand-crafted roles, no policy documents to assemble. CDK generates most of that automatically behind the scenes.

Infrastructure and application grow together

What fundamentally changes is how a team works with its infrastructure. Pull requests, code reviews, tests, CI/CD - everything that's standard practice in software development now works for infrastructure too. Because it's just a regular TypeScript project.

But the real payoff goes deeper. In the traditional world, infrastructure and application exist as separate concerns. Someone creates a DynamoDB table, someone else writes the Lambda handler, and in between sits a table name as a string in an environment variable - hopefully named the same on both sides. If something changes, the mismatch only shows up at runtime.

CDK solves this. The table, the Lambda function, and the permissions between them live in the same project. You can easily define a shared interface for your environment variables and use it in both the stack and the handler:

// shared/env.ts - imported by both stack and handler
export interface AppEnv {
  TABLE_NAME: string;
  BUCKET_NAME: string;
}

// In the CDK stack
const environment: AppEnv = {
  TABLE_NAME: table.tableName,
  BUCKET_NAME: bucket.bucketName,
};

const fn = new lambda.Function(this, "Handler", {
  runtime: lambda.Runtime.NODEJS_24_X,
  handler: "index.handler",
  code: lambda.Code.fromAsset("lambda"),
  environment,
});

// In the Lambda handler
const { TABLE_NAME, BUCKET_NAME } = process.env as unknown as AppEnv;

When a table name changes, it changes in one place. When your frontend needs an S3 bucket deployment, it's part of the same stack. cdk diff shows you exactly what will change before deployment. cdk deploy rolls everything out together.

And the problem from the beginning - a second environment that should be identical - is a non-issue. Small differences are just handled through parameters, in code, with no extra syntax or limitations. Lower throttles in staging or skipping alarms in a developer sandbox? Just a condition in your code. Same codebase, different configuration, deployed to as many accounts as you need - no copy-paste, no loss of control.

How to get started

And the learning curve that seemed like a barrier at first? CDK is TypeScript (or Python, Java, Go)

  • a language your team probably already knows. The official CDK workshop from AWS walks you through the key concepts in under two hours. The API docs are solid, the community is active. That's enough to deploy your first stacks to production.

If you want to move even faster: AI-powered coding agents like Claude Code or Copilot can generate well-structured CDK code today - constructs, tests, best practices included. You describe what you need, the agent writes the stack, you review and deploy. Not a requirement, but a real accelerator.

CDK gives you a lot of freedom and flexibility - how you structure your stacks and constructs, what you extract into shared libraries, where you draw the line between infrastructure and application. It's worth establishing a few good patterns early that can grow with your project. We'll take a closer look at what that can look like in a dedicated article.

And if you'd like to talk about what a CDK setup could look like for your team - feel free to get in touch 😉

Let's talk

Whether it's a new initiative, an urgent bottleneck, or a second opinion on your current architecture. A quick conversation will show how we can help.