6 minutes

What is the AWS CDK?

Managing cloud resources at scale can quickly get out of hand, becoming a massive headache. Environments can become difficult to track (much less audit when it comes to compliance), misconfigurations end up taking down prod, and standards feel impossible to enforce across teams.

That’s where the AWS Cloud Development Kit (CDK) comes in, it’s a programming framework for defining your infrastructure as code itself. Unlike most Infra-as-Code solutions which tend to use a static templating language like YAML or JSON, the CDK enables you to use popular programming languages like Python or TypeScript instead.

What is Infrastructure-as-Code?

Infra-as-Code (IaC) is a modern way of managing resources in the cloud. Rather than writing clunky Bash scripts, or worse clicking around in the console to deploy things manually, IaC enables you to define your resources as declarative code and then deploy those resources from that code.

Thats ultimately what the CDK is, in two parts: the actual programming constructs that you use to define things like S3 Buckets, EC2 instances, or RDS databases, as well as the framework and tooling you use to then deploy those resources up into your AWS account.

The benefits?

  • Predictability: You get a consistent way of deploying resources, no more having to write complex depoyment automation scripts.
  • Versioning: Infrastructure code gets checked into version control and can be code reviewed before being deployed.
  • Self-documenting: Your infrastructure now has living documentation of exactly whats configured.
  • Modularity: Find yourself repeating common patterns in your architecture? Encapsulate those patterns the exact same way you do with your application code, via code modules!
  • Speed: Need to spin up an entirely new environment? Not a problem now that all of your infrastructure is captured as what is essentially a blueprint.
  • Standardization: Want to ensure S3 buckets have encryption enabled with your internal KMS key? Now your DevOps team can distribute a library of approved resource constructs that everyone in your organization can use!

This all sounds great, what are the downsides?

In my experience using the CDK, it does have some downsides. The first big one that makes a lot of people hesitate is that the CDK is built on top of CloudFormation (more on this in the next section).

The other big one is that it can be very tempting to start over-engineering or misusing the fact that you’re able to use a programming language to define infrastructure. Messy or unorganized code is still messy and organized code at the end of the day, no matter if its at the application or the infrastructure level.

Doesn’t AWS already have an Infra-as-Code service?

You’re right, the AWS CloudFormation service already enables you to write IaC, except it does it using YAML to define resources instead. While this works, writing lots of YAML templates to define your infrastructure can get reptative real fast.

That’s why the CDK enabling you to use a programming language is a real game-changer, as it gives you access to things like if conditions and for loops when creating resources. That might seem like a small thing but you’d be surprised how often you run into a situation when you need to conditionally or repeatedly do something in your infrastructure code.

Here’s the kicker: the way the CDK works is by running the code written in your preferred programming language and generating a CloudFormation template from it.

That’s right, when you deploy your CDK code, here’s what actually happens:

  1. The cdk tool “synthesizes” the code constructs that represent cloud resources into CloudFormation YAML templates (you can actually see the templates it generates by peeking into the cdk.out directory that gets created when you run the CDK).
  2. These generated templates are then deployed as Stacks in CloudFormation, which then handles the actual provisioning and deployment of those resources.

From an architecture standpoint, this is quite the smart move on AWS’ part as they essentially repurpose an existing service into an entirely new one, saving them a ton of engineering effort.

However often times this is the one thing that causes many people to take pause when using the CDK, as CloudFormation has a reputation of being quite finicky (especially when it comes to IAM permissions) when it deploys resources. If you’ve ever gotten stuck in the dreaded UPDATE_ROLLBACK_FAILED state, you know what I’m talking about.

It is worth mentioning here that the CDK actually allows you to (partially) opt out one of CloudFormation’s most common point points: automatic rollbacks. By passing the --no-rollback flag when deploying CDK code, the rollback feature of CloudFormation won’t activate if your infrastructure stack gets stuck in a failed state.

What about Terraform?

The main competitor when it comes to IaC tools is Terraform. The main difference between these two tools is Terraform’s use of HCL, a configuration language somewhat like a cross between YAML and JSON.

The biggest problem when it comes to HCL is that is also suffers the same problems as YAML templates do: overly verbose syntax with somewhat clunky code re-use. The Terraform module system helps alleviate this a lot, but what I’ve often found is that Terraform code often times becomes module spaghetti when its used at scale.

Now yes, tools like Terragrunt do exist to combat this very fact but they all come with their own tradeoffs, not to mention lock-in to how they want you to organize your Terraform code.

However the main upside of Terraform is that it’s management of infrastructure state is frankly better than CloudFormations. Terraform workspaces don’t every get “stuck” in odd deployment states the same way that CFN stacks do, which eliminates an entire class of potential issues all by itself.

The trade-off for this being that you can never be completely “sure” that a Terraform workspace is fully deployed or not. In my opinion, this just doesn’t really matter when no matter which tool you use, someone could have potentially gone in and tweaked something directly in the console and introduced what’s know as infrastructure drift all on their own.

By and large the rule of thumb when it comes to IaC in AWS for me has become: Is this team already using Terraform to manage it infra?

  • If yes, just stick with Terraform, its not worth the headache of trying to migrate resources from one tool to a new one.
  • If no, then prefer to use the CDK. While CloudFormation can be a bit finicky, I’ve found that tooling is generally good enough and the pure convenience of having a programming language at my fingertips to define infra is worth it.

Show me some code!

Not sure on if you want to really use the CDK yet? Let’s take a lot at some sample code!

The most basic CDK app looks something like this:

import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws_s3';

const app = new cdk.App();
const stage = new cdk.Stage(app, 'example-stage', {
  env: {
    account: '012345678901',
    region: 'us-east-1',
  },
});

const stack = new cdk.Stack(stage, 'example-stack');

new s3.Bucket(stack, 'example-bucket', {
  bucketName: 'my-s3-bucket',
  encryption: s3.BucketEncryption.S3_MANAGED,
  versioned: true,
});

We could then get a plan of what’s going to be deployed from this code using the CDK CLI to run a diff:

cdk diff example-stage/example-stack

In this example scenario, notice how I had to specify which stage and stack I wanted to run a diff against.

A stage typically represents your various environments by region (I usually call these something like ue1Prod for production, and just dev or qa for a lower environment as those tend to be single-region)

A stack is the smallest deployable unit of infrastructure. All resources in a stack are always deployed or destroyed together. You want to typically keep these small and organized.

It’s also worth mentioning now that you can reference resources from between stacks. Lets say you have a database in a db stack, you can then reference that for a Lambda in a function stack, and the cool thing is that the CDK is smart enough to implicitly understack these stack interdependencies. (Under the hood the CDK is creating CFN Exports automatically for you!)

This is especially useful when you actually want to deploy ALL stacks in a CDK app:

cdk deploy '*'

This will deploy in-order every CDK stack in your application. Typically you don’t want this if you have multiple environments defined using stages, so the more common approach is something like:

cdk deploy 'example-stage/*'

Which would deploy all the stacks for that given stage. You can of course also deploy a single stack via:

cdk deploy 'example-stage/example-stack'