What is Context and its Significance in AWS CDK

What is Context and its Significance in AWS CDK

Amazon Web Services Cloud Development Kit (AWS CDK) is a powerful infrastructure-as-code (IaC) framework that allows developers to define cloud infrastructure using familiar programming languages. One essential concept within AWS CDK that plays a crucial role in configuring and customizing your infrastructure is "context." In this blog post, we will delve into the significance of context in AWS CDK and how it contributes to the flexibility and scalability of your cloud applications.

Context in AWS CDK

In AWS CDK, context refers to the external input provided during the synthesis phase of your application. This external input can be environmental variables, resource-specific configurations, or any changing parameter based on the deployment environment. Context allows you to dynamically adjust certain values in your CDK application without modifying the source code.

Using Context at the App Level

In this example we will set the context at our CDK App level:

import { Stack, StackProps, App } from 'aws-cdk-lib';

export class MyCdkStack extends Stack {
  constructor(scope: App, id: string, props?: StackProps) {
    super(scope, id, props);

    console.log('accessing context here ', this.node.tryGetContext('fromApp'));
  }
}

const app = new App({
  // setting context in our cdk.App
  context: {
    fromApp: {name: 'Mikaeel', country: 'BE'},
  },
});

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:

cdk synth

We can see the output from the console.log:

{ name: 'Mikaeel', country: 'BE' }

Context Managed by CDK

Now 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.

{}

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

import { Stack, StackProps, App } from 'aws-cdk-lib';

export class MyCdkStack extends Stack {
  constructor(scope: App, id: string, props?: StackProps) {
    super(scope, id, props);

    console.log('context passed in App here ', this.node.tryGetContext('fromApp'));

    // printing the availability zones
    console.log(Stack.of(this).availabilityZones);
  }
}

Now synthesize our stack with:

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:

{
  "availability-zones:account=123456789:region=us-east-1": [
    "us-east-1a",
    "us-east-1b",
    "us-east-1c",
    "us-east-1d",
    "us-east-1e"
  ]
}

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:

{
  "app": "npx ts-node --prefer-ts-exts bin/aws-cdk-group-chat-app.ts",
  "context": {
    "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
    "@aws-cdk/core:checkSecretUsage": true,
    "@aws-cdk/core:target-partitions": [
      "aws",
      "aws-cn"
    ]
  }
}

Where the app key indicates to the CDK CLI how to run our CDK code, which is mostly 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-lambda.

💡
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:

{
  "app": "npx ts-node --prefer-ts-exts bin/aws-cdk-group-chat-app.ts",
  "context": {
    "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
    "@aws-cdk/core:checkSecretUsage": true,
    "@aws-cdk/core:target-partitions": [
      "aws",
      "aws-cn"
    ],
    // adding a bucket property as context
    "bucket": {
      "region": "us-east-1",
      "name": "my-s3-bucket"
    }
  }
}

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

import { Stack, StackProps, App } from 'aws-cdk-lib';

export class MyCdkStack extends Stack {
  constructor(scope: App, id: string, props?: StackProps) {
    super(scope, id, props);

    console.log('from cdk.json ', this.node.tryGetContext('bucket'));
  }
}

Let's synth our cdk stack:

cdk synth

The output from the console.log will look like this:

 { "region": "us-east-1", "name": "my-s3-bucket" }

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:

import { Stack, StackProps, App } from 'aws-cdk-lib';

export class MyCdkStack extends Stack {
  constructor(scope: App, id: string, props?: StackProps) {
    super(scope, id, props);

    console.log('bucketName ', this.node.tryGetContext('bucketName'));
    console.log('region ', this.node.tryGetContext('region'));
  }
}

Now we'll synth the CDK application passing in the context values using the CLI:

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

The output from our console.log statements will look like this.

bucketName myBucket
region us-east-1
💡
We can only pass string values when we set the 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.:

cdk synth \
  --context stack-name1:bucketName=myBucket \
  --context stack-name2: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 tothis.node.tryGetContext('keyName').

💡
I believe it's good to set the context at the CDK App level, it's globally available, it's intuitive and we can 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.

Did you find this article valuable?

Support Mikaeel Khalid by becoming a sponsor. Any amount is appreciated!