What do CDK Bootstrap, CDK Synth and CDK Diff do

avatar
Borislav Hadzhiev

Last updated: Jan 27, 2024
8 min

banner

# Table of Contents

  1. What is CDK bootstrap
  2. Generating CloudFormation Templates in AWS CDK
  3. CDK synth - introduction
  4. CDK diff - introduction

# What is CDK bootstrap

CDK bootstrap is a command we can access using the CDK CLI.

shell
npx aws-cdk bootstrap

The cdk bootstrap command provisions a CloudFormation stack called CDKToolkit. This CloudFormation stack is specific to the environment (region and account) our CDK stack is configured for.

Every CDK application belongs to a specific environment.

An environment consists of an account number and a region, where the CDK app is going to be deployed.

We set the environment for a CDK project when instantiating a CDK stack.

const app = new cdk.App(); const myStack = new MyCdkStack(app, 'my-cdk-stack', { stackName: `my-cdk-stack`, // ๐Ÿ‘‡ set the environment env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, });

The initialization process of the environment consists of:

  • Provisioning an S3 bucket, where our assets (i.e. Lambda function code, CloudFormation template) will be stored.
  • Provisioning IAM roles the CDK CLI needs in order to perform a deployment in the environment In order to deploy to multiple environments we have to bootstrap each environment.

The CDKToolkit stack provisions an S3 bucket that stores the assets(i.e. Lambda code, CloudFormation template, etc), required for a CDK deployment.

cdk toolkit stack

In the S3 bucket we will have an assets "directory".

assets directory

The assets directory is going to contain our zipped file assets.

zipped file assets

For a simple CDK project with a single lambda function, my file assets consist of the index.js handler code for the Lambda.

# Using CDK bootstrap

In order to bootstrap our default account and region we can run the cdk bootstrap command.

shell
npx aws-cdk bootstrap

We only need to use the bootstrap command once for every environment(region and account).

If we use the command more than once, the CDK CLI will check if our CDKToolkit stack has to be updated. If necessary, the stack will be updated. If not, running the bootstrap command does nothing.

We can also specify other environments to bootstrap.

shell
npx aws-cdk bootstrap --profile my-profile npx aws-cdk bootstrap ACCOUNT_NUMBER/REGION npx aws-cdk bootstrap 123456789/us-east-1

# Conclusion

The CDK bootstrap command provisions a CloudFormation stack called CDKToolkit.

The stack consists of an S3 bucket that stores file assets (i.e. Lambda function code, CloudFormation templates), required for deployments.

We have to bootstrap each environment (account and region) separately.

# Table of Contents

  1. Generating CloudFormation Templates in AWS CDK
  2. CDK synth - introduction
  3. CDK diff - introduction

# Generating CloudFormation Templates in AWS CDK

To generate a CloudFormation template in AWS CDK we have to use the cdk synth command.

The cdk synth command does a couple of things for us:

  • it runs our CDK code, so we would get notified if we had any syntax, type or cdk validation errors
  • it generates the CloudFormation equivalent of our CDK stack and stores it in the cdk.out directory
The code for this article is available on GitHub

In order to demo the process, I'll create a small CDK application that consists of a single S3 bucket.

lib/cdk-starter-stack.ts
import * as s3 from 'aws-cdk-lib/aws-s3'; import * as cdk from 'aws-cdk-lib'; export class MyCdkStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: cdk.StackProps) { super(scope, id, props); // ๐Ÿ‘‡ create our Bucket resource const s3Bucket = new s3.Bucket(this, 'avatars-bucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, }); } } const app = new cdk.App(); new MyCdkStack(app, 'my-cdk-stack', { stackName: 'my-cdk-stack', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, });

In the code sample:

  1. We defined our CDK stack and used the Bucket construct to create a bucket resource.

  2. We instantiated our CDK stack in the scope of the CDK app.

To generate the CloudFormation template equivalent of our CDK stack, I have to run the synth command.

shell
npx aws-cdk synth

The command outputs the complete CloudFormation equivalent of our CDK stack, in our case the S3 bucket.

synth output

If we take a look at the cdk.out directory, we can see that our CloudFormation template has been generated and is ready for deployment.

cdk out template

When we run the cdk deploy command, the CDK CLI automatically runs the cdk synth command for us and generates the CloudFormation template.

At this point, we've generated the CloudFormation equivalent of our CDK stack and we're ready to deploy using the cdk deploy command.

# CDK synth - introduction

The cdk synth command from the CDK CLI generates and prints the CloudFormation equivalent of the CDK stack we've defined.

The code we write in our CDK applications gets compiled down to CloudFormation before it gets deployed.

CDK is just an abstraction level above CloudFormation that aims to improve developer experience. Our CDK code eventually ends up as CloudFormation before it gets deployed.

# CDK synth - diving deep

In order to synthesize a CloudFormation template, the CDK CLI has to first execute our CDK app.

I often run cdk synth to execute my app and see if I get any errors.

The cdk synth command knows how to execute our app because we've specified the command as the value of the app key in our cdk.json file. This command is different for the different programming languages CDK supports.

The code for this article is available on GitHub

CDK projects written in TypeScript have to be compiled down to JavaScript, which is done by executing the value of the app key in the cdk.json file:

cdk.json
{ "app": "npx ts-node --prefer-ts-exts infra/app.ts", "context": { // ... } }

In order to demo the cdk synth command, I'll create a small CDK app, consisting of a single s3 bucket.

lib/cdk-starter-stack.ts
import * as s3 from 'aws-cdk-lib/aws-s3'; import * as cdk from 'aws-cdk-lib'; export class MyCdkStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: cdk.StackProps) { super(scope, id, props); new s3.Bucket(this, 'avatars-bucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, }); } } const app = new cdk.App(); new MyCdkStack(app, 'my-cdk-stack', { stackName: 'my-cdk-stack', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, });

I'll now run the synth command:

shell
npx aws-cdk synth

The output I get is the CloudFormation equivalent to my CDK stack (if we ignore the metadata section).

Resources: avatarsbucketE3043F49: Type: AWS::S3::Bucket UpdateReplacePolicy: Delete DeletionPolicy: Delete Metadata: aws:cdk:path: my-cdk-stack/avatars-bucket/Resource CDKMetadata: Type: AWS::CDK::Metadata Properties: Analytics: v2:deflate64:H... Metadata: aws:cdk:path: my-cdk-stack/CDKMetadata/Default

After I ran the cdk synth command the CDK CLI created a cdk.out folder in the root directory of my project.

cdk out folder

The cdk.out folder contains the generated cloudformation template(s) and asset files if our application requires any assets.

An example of asset files would be the code for a Lambda function if our CDK application provisioned one.

Because a CDK app can consist of more than one CDK stack, we might end up having multiple CloudFormation templates in the cdk.out directory.

Let's test the behavior of cdk synth if we have more than one stack instantiated in our CDK App:

bin/cdk-starter.ts
import * as cdk from 'aws-cdk-lib'; import {MyCdkStack} from '../lib/cdk-starter-stack'; const app = new cdk.App(); new MyCdkStack(app, 'my-cdk-stack-dev', { stackName: 'my-cdk-stack-dev', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, }); new MyCdkStack(app, 'my-cdk-stack-prod', { stackName: 'my-cdk-stack-prod', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, });

I instantiated our CDK stack 2 times.

If I synthesize the stack now with:

shell
npx aws-cdk synth

I get the following output:

cdk synth multiple stacks

The output states that both stacks have been synthesized in the cdk.out directory.

If we look at the contents of the cdk.out directory, we can see that both of our CloudFormation stacks have been generated.

cdk out two stacks

When working with multiple stacks it's better to explicitly name the stacks.

shell
npx aws-cdk synth \ my-cdk-stack-dev \ my-cdk-stack-prod

The printed output of the cdk synth command is in YAML, however, the stored output in our cdk.out directory is in JSON.

If you want to print the output in JSON when running cdk synth you can pass the --json flag.

shell
npx aws-cdk synth --json my-cdk-stack-dev

# Conclusion

The cdk synth command executes our CDK app, generates the equivalent of our CDK code as a CloudFormation template and stores it in the cdk.out directory.

We can look at the command as a preparation step before a stack deployment.

Note that running the cdk synth command before we run cdk deploy is optional, because CDK will run synth for us before each deployment automatically.

# CDK diff - introduction

The cdk diff command outputs the difference between the already deployed CloudFormation template and the CloudFormation template equivalent of our current CDK code.

CDK is just a wrapper around CloudFormation that enables us to write our infrastructure as code using a programming language (TypeScript, Python, Java ...), rather than a configuration language (YAML, JSON).

Eventually, our CDK code gets compiled down to CloudFormation before it gets deployed.

# CDK diff - in depth

The code for this article is available on GitHub

To demo how the CDK diff command works, I'll create a small CDK app, consisting of a single S3 bucket.

lib/cdk-starter-stack.ts
import * as s3 from 'aws-cdk-lib/aws-s3'; import * as cdk from 'aws-cdk-lib'; export class MyCdkStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: cdk.StackProps) { super(scope, id, props); new s3.Bucket(this, 'avatars-bucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, }); } } const app = new cdk.App(); new MyCdkStack(app, 'my-cdk-stack', { stackName: 'my-cdk-stack', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, });

We used the Bucket construct to create an S3 bucket.

Let's run the diff command before even deploying:

shell
npx aws-cdk diff

The output looks as follows:

diff before deploy

The output shows us that if we were to deploy our current CDK code, we'd create an S3 bucket.

When we ran the cdk diff command a couple of things happened behind the scenes:

The CDK CLI ran the cdk synth command.

The cdk synth command first runs the code of our CDK app. It then generates the CloudFormation equivalent of the CDK stack we've defined, and stores the output in the cdk.out directory.

If we look at the contents of the cdk.out directory we can see the CloudFormation equivalent of our CDK stack: CDK out directory

Then the cdk diff command printed the changeset between the template from the cdk.out directory and the deployed CloudFormation template.

Since we haven't deployed our stack yet, it just printed what we'd provision if we deployed the stack.

I'll now deploy the stack by running npx aws-cdk deploy. Then I'll make a small change to the application and run npx aws-cdk diff again.

lib/cdk-starter-stack.ts
- new s3.Bucket(this, 'avatars-bucket', { + new s3.Bucket(this, 'another-bucket', { removalPolicy: cdk.RemovalPolicy.DESTROY, });

I've just changed the id parameter I'm passing to the Bucket construct.

Let's run the cdk diff command again:

shell
npx aws-cdk diff

And look at the output:

diff after change

From the output, we see that our bucket will be destroyed and a new one will be created.

By changing the id parameter, we've changed the CloudFormation logical ID of the resource.

We can look at the current Logical ID of our Bucket in the CloudFormation console: cloudformation stack

This is one of the more confusing aspects of CDK and can be quite the foot gun, so I've written another article on the topic - Identifiers in AWS CDK.

Running the cdk diff command before deploying is a very good practice, especially when updating stateful resources like databases.

As a side note, we can also run the cdk diff command to compare between a CloudFormation template on our local file system and our current CDK code:

shell
npx aws-cdk diff \ --template cdk.out/my-cdk-stack.template.json \ my-cdk-stack

# Conclusion

We use the cdk diff command to compare between the already deployed CloudFormation template and the CloudFormation template that has been generated by running the cdk synth command and is equivalent to the current state of our CDK app.

It's always a good practice to run the cdk diff command before we deploy, to avoid surprises with resources getting deleted due to a change in logical IDs.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev