How to use Context in AWS CDK

avatar

Borislav Hadzhiev

Fri Apr 23 20214 min read

Updated on Fri Apr 23 2021

Context is a combination of key-value pairs we can set in our CDK application. Context is similar to environment variables, in that both are accessible at synthesis time.

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/core';

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 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/core';

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 synthesize our stack with:

shell
npx 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 something like:

cdk.json
{
  "app": "npx ts-node --prefer-ts-exts infra/app.ts",
  "context": {
    "@aws-cdk/core:enableStackNameDuplicates": "true",
    "aws-cdk:enableDiffNoFail": "true",
    "@aws-cdk/core:stackRelativeExports": "true",
    "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
    "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
    "@aws-cdk/aws-kms:defaultKeyPolicies": true
  }
}

Where the app key indicates to the CDK CLI how to execute 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/core';

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 cdk synth

The output from the console.log call looks like:

from cdk json

Setting Context using the CDK CLI #

The next option we have to set context in our CDK app is using 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/core';

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 cdk synth \
  --context bucketName=myBucket \
  --context region=us-east-1

The output from our console.log statements looks like:

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 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 context at the CDK App level, it's globally available, it's intuitive and I can pass in 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.

Further Reading #

Join my newsletter

I'll send you 1 email a week with links to all of the articles I've written that week

Buy Me A Coffee