CDK context values are not just for parameterizing deployments; they are the primary mechanism for structuring your multi-environment CDK application.

Let’s see this in action. Imagine a simple CDK app that deploys a S3 bucket. We want to give it a different name in dev and prod.

# app.py
from aws_cdk import App, Environment
from my_s3_stack import MyS3Stack

app = App()

# Dev environment
MyS3Stack(app, "MyS3Stack-dev",
          env=Environment(account="111111111111", region="us-east-1"),
          context={
              "environment": "dev",
              "bucket_name_suffix": "-dev"
          })

# Prod environment
MyS3Stack(app, "MyS3Stack-prod",
          env=Environment(account="222222222222", region="us-east-1"),
          context={
              "environment": "prod",
              "bucket_name_suffix": "-prod"
          })

app.synth()

And the stack itself:

# my_s3_stack.py
from aws_cdk import Stack, RemovalPolicy
from aws_cdk.aws_s3 import Bucket, BucketEncryption, BlockPublicAccess
from constructs import Construct

class MyS3Stack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        bucket_name_suffix = self.node.try_get_context("bucket_name_suffix")
        bucket_name = f"my-app-bucket{bucket_name_suffix}"

        Bucket(self, "MyBucket",
               bucket_name=bucket_name,
               encryption=BucketEncryption.S3_MANAGED,
               block_public_access=BlockPublicAccess.BLOCK_ALL,
               removal_policy=RemovalPolicy.RETAIN)

When you run cdk synth, you’ll see two CloudFormation templates generated, each with a distinct S3 bucket name. The app.py file is explicitly defining the context for each deployment.

The problem this solves is managing configuration drift between environments. Instead of relying on environment variables or passing long lists of parameters during deployment, you define the environment-specific configuration directly within your CDK app’s structure. This makes the configuration declarative and version-controlled alongside your infrastructure code.

Internally, self.node.try_get_context("key") is a method on the Node object associated with each CDK construct. This Node object acts as a parent-child relationship manager and also holds context values. When try_get_context is called, CDK walks up the construct tree from the current construct to the root App object, looking for the specified context key. If found, it returns the value; otherwise, it returns None. This hierarchical lookup allows for both global context values (set on the App itself) and specific context values (set on individual stacks or constructs).

The env parameter on Stack is not context. It’s a direct configuration for the AWS environment (account and region) where the stack will be deployed. Context values, on the other hand, are arbitrary key-value pairs that you can use for anything within your CDK application’s logic, such as naming conventions, feature flags, or resource sizing.

You can also pass context from the command line using the -c or --context flag. For instance, cdk deploy -c environment=prod -c bucket_name_suffix=-prod. However, defining context directly in app.py as shown above is generally preferred for multi-environment setups because it keeps the configuration self-contained and explicit within the codebase, rather than relying on external command-line arguments that can be easily forgotten or mistyped. This approach also allows you to easily programmatically generate these context dictionaries if your environment setup is highly dynamic.

The RemovalPolicy.RETAIN is crucial here. When you’re experimenting with different bucket names and configurations across environments, accidentally deleting a production bucket (even if it’s just the name that changes) can be catastrophic. By setting RETAIN, you ensure that even if you delete the stack, the S3 bucket itself will not be deleted by CloudFormation, giving you a chance to recover or manually clean up. This is a common safeguard when dealing with stateful resources like S3 buckets, especially during development and testing phases.

The BlockPublicAccess.BLOCK_ALL setting ensures that your S3 bucket is not accidentally exposed to the public internet, a critical security best practice. This is often a default in newer CDK versions, but explicitly setting it makes your security posture clear and consistent.

When you deploy an environment, CDK synthesizes the CloudFormation template. The context values are resolved during synthesis. This means that the bucket_name variable in your MyS3Stack will be evaluated to a concrete string like "my-app-bucket-dev" or "my-app-bucket-prod" before the CloudFormation template is generated. The resulting template will then contain the literal bucket name, which is what CloudFormation uses to create the resource.

The next thing you’ll encounter is managing secrets that need to be environment-specific, such as database passwords or API keys.

Want structured learning?

Take the full Cdk course →