How to use Context in AWS CDK

avatar
Borislav Hadzhiev

Last updated: Jan 27, 2024
5 min

banner

# What is Context in AWS CDK

Context in CDK is a combination of key-value pairs we can set in our CDK application.

These key-value pairs are going to be available at synthesis time, which means that we can use them in our code, i.e. in conditional statements.

The CDK library uses context to:

  • cache information about the deployment environment - i.e. Availability zones
  • keep track of feature flags. Feature flags provide a way to opt in or out of new functionality that introduces breaking changes, outside of a major CDK version release.

We use context to set key-value pairs we can then access in our CDK code, in a similar way to environment variables.

# Using context at the App Level

We are going to create a simple CDK application to demo using context.

The code for this article is available on GitHub

First, let's look at an example where we set the context at our CDK App level.

lib/cdk-starter-stack.ts
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); console.log('accessing context ๐Ÿ‘‰', this.node.tryGetContext('fromApp')); } } const app = new cdk.App({ // ๐Ÿ‘‡ setting context in our cdk.App context: { fromApp: {name: 'John', age: 28}, }, }); const myStack = new MyCdkStack(app, 'my-cdk-stack', { stackName: `my-cdk-stack`, env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, });

Let's synthesize our stack and look at the output.

shell
npx aws-cdk synth

We can see the output from the console.log call we made in our CDK stack.

context from app

# Context Managed by CDK

Let's take a quick look at how CDK uses context to cache certain values that belong to our deployment environment.

If we open the cdk.context.json file in the root directory of our CDK project, we can see that it's empty.

cdk.context.json
{}

Let's add a simple console.log call and print the availability zones that belong to the region our stack is configured for:

lib/cdk-starter-stack.ts
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); console.log('context passed in App ๐Ÿ‘‰', this.node.tryGetContext('fromApp')); // ๐Ÿ‘‡ printing the availabilityZones console.log(cdk.Stack.of(this).availabilityZones); } }

Now, let's synthesize our stack with the synth command.

shell
npx aws-cdk synth

If we now open the cdk.context.json file in the root of our project we'll see that CDK has cached the availability zones that correspond to our stack's region. In my case that's the eu-central-1 region.

cdk.context.json
{ "availability-zones:account=123456789:region=eu-central-1": [ "eu-central-1a", "eu-central-1b", "eu-central-1c" ] }

The cdk.context.json file is where CDK caches values related to our deployment environment.

There is also a cdk.json file in the root directory of a CDK project.

The cdk.json file looks similar to the following:

cdk.json
{ "app": "npx ts-node --prefer-ts-exts infra/app.ts", "context": { "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": false, } }

Where the app key indicates to the CDK CLI how to run our CDK code. In my case it's a compilation step that compiles the typescript code to javascript.

Then we have the context key, which is composed of feature flags.

These feature flags enable us to opt in or out of breaking changes. Their names start with the name of the package that introduces the breaking change - i.e. @aws-cdk/aws-kms.

If we want to roll back to the previous behavior of AWS CDK what certain feature is concerned, we have to set the feature flag to false.

We can also use the cdk.json file to pass in context into our CDK app, let's add another property to it:

cdk.json
{ "app": "npx ts-node --prefer-ts-exts infra/app.ts", "context": { // ๐Ÿ‘‡ adding a bucket property "bucket": { "region": "us-east-1", "name": "my-bucket" } } }

Now let's access the bucket context in our CDK stack:

lib/cdk-starter-stack.ts
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); console.log('from cdk.json ๐Ÿ‘‰', this.node.tryGetContext('bucket')); } }

Let's synth our CDK stack.

shell
npx aws-cdk synth

The output from the console.log call looks like:

from cdk json

# Setting Context using the CDK CLI

The next option used for setting context in our CDK app is to use the CDK CLI.

We can set context key-value pairs during synthesis.

First, we'll access context values that we haven't yet set:

lib/cdk-starter-stack.ts
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); console.log('bucketName ๐Ÿ‘‰', this.node.tryGetContext('bucketName')); console.log('region ๐Ÿ‘‰', this.node.tryGetContext('region')); } }

And now we'll synth the CDK application passing in the context values using the CLI.

shell
npx aws-cdk synth \ --context bucketName=myBucket \ --context region=us-east-1

The output from our console.log statements looks as follows.

from CDK CLI

We can only pass string values when we set context using the CDK CLI. If we have to pass an object, we have to pass it as a string and parse it in our application code.

If we have multiple CDK stacks and want to set different context values using the CDK CLI, we can prefix the key-value pairs with the stack name, i.e.:

shell
npx aws-cdk synth \ --context my-stack:bucketName=myBucket \ --context your-stack:bucketName=yourBucket

# Conclusion

Context is a combination of key-value pairs we can set and make available in our CDK application.

Context is similar to environment variables, in that both are accessible in synthesis time, as opposed to CDK parameters, which are only set at deployment time.

The most common ways to set context are:

  • At the CDK App level using the context property.
  • In our cdk.json file adding key-value pairs to the context object.
  • Using the CDK CLI with the --context flag.

In our CDK applications, we can access the context values with a call to this.node.tryGetContext('keyName').

My personal preference is to set the context at the CDK App level. It's globally available, it's intuitive and we are able to set context values of any type.

Setting context using the cdk.json file is too implicit and hidden.

An alternative is using the CDK CLI, however, the CLI only allows us to pass in string values, so the most intuitive way is setting context at the cdk.App level.

# 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