You can pass CloudFormation parameters to CDK stacks at deploy time, but the way you’re likely thinking about it is wrong.

Here’s a CDK app that defines a simple S3 bucket. The bucket name is parameterized.

# app.py
from aws_cdk import App, Stack
from constructs import Construct
from aws_cdk import aws_s3 as s3

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

        s3.Bucket(self, "MyBucket",
            bucket_name=bucket_name_param,
            removal_policy=s3.RemovalPolicy.DESTROY
        )

app = App()
# For demonstration, we'll hardcode this.
# In a real scenario, this would come from a context provider or environment variable.
bucket_name_from_param = "my-unique-cdk-bucket-12345"
MyBucketStack(app, "MyBucketStack", bucket_name_param=bucket_name_from_param)

app.synth()

When you run cdk synth, it generates CloudFormation. Notice how bucket_name_from_param is directly embedded into the template.yaml.

# snippet from template.yaml
Resources:
  MyBucketCDKMyBucketB2A7E24C:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: my-unique-cdk-bucket-12345 # <--- Directly embedded
      RemovalPolicy: Destroy
# ...

The common mistake is trying to define a CloudFormation parameter in your CDK code and then referencing that parameter within the CDK stack definition. CDK is a compiler to CloudFormation, not a direct Passthrough. You don’t define CloudFormation parameters and then bind them to CDK construct properties in the same way you might imagine.

Instead, you pass values into the CDK synthesis process, and those values become hardcoded (or parameterized in the generated CloudFormation) in the synthesized template.

Here’s how you’d do it for real, using CDK context. First, modify cdk.json to include a context value:

{
  "app": "python app.py",
  "context": {
    "bucketName": "my-special-bucket-from-context-67890"
  }
}

Then, update your app.py to read this context:

# app.py
from aws_cdk import App, Stack, CfnParameter
from constructs import Construct
from aws_cdk import aws_s3 as s3

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

        # Retrieve the bucket name from CDK context
        bucket_name_from_context = self.node.try_get_context("bucketName")

        if not bucket_name_from_context:
            raise ValueError("Bucket name not provided via CDK context. Use 'cdk deploy -c bucketName=your-bucket-name'")

        s3.Bucket(self, "MyBucket",
            bucket_name=bucket_name_from_context,
            removal_policy=s3.RemovalPolicy.DESTROY
        )

app = App()
MyBucketStack(app, "MyBucketStack")
app.synth()

Now, when you run cdk synth, the my-special-bucket-from-context-67890 value will be hardcoded in template.yaml.

If you want the generated CloudFormation to have a parameter, you can use CfnParameter. This is for when you want the CloudFormation deployer (not the CDK synth process) to provide the value.

Modify app.py again:

# app.py
from aws_cdk import App, Stack, CfnParameter
from constructs import Construct
from aws_cdk import aws_s3 as s3

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

        # Define a CloudFormation parameter
        cfn_bucket_name_param = CfnParameter(self, "BucketNameParam",
            type="String",
            description="Name for the S3 bucket",
            default="default-cdk-cfn-bucket-name"
        )

        s3.Bucket(self, "MyBucket",
            bucket_name=cfn_bucket_name_param.value_as_string, # Reference the parameter's value
            removal_policy=s3.RemovalPolicy.DESTROY
        )

app = App()
MyBucketStack(app, "MyBucketStack")
app.synth()

After cdk synth, template.yaml will now include a CloudFormation parameter definition:

# snippet from template.yaml
Parameters:
  BucketNameParam:
    Type: String
    Description: Name for the S3 bucket
    Default: default-cdk-cfn-bucket-name

Resources:
  MyBucketCDKMyBucketB2A7E24C:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketNameParam # <--- Refers to the CFN parameter
      RemovalPolicy: Destroy
# ...

You can then provide a value for this BucketNameParam during cdk deploy using the -c flag, which CDK translates into --parameters for the CloudFormation deploy command.

cdk deploy -c BucketNameParam=my-specific-bucket-for-deploy-112233

This command tells CDK to synthesize the template, and then when deploying that template to CloudFormation, pass BucketNameParam=my-specific-bucket-for-deploy-112233 to the CloudFormation deploy operation.

The key is understanding that CDK parameters (via context) and CloudFormation parameters are distinct. CDK context is for values known at synthesis time, directly embedding them into the template. CloudFormation parameters are for values provided at deployment time to the CloudFormation service itself, and CDK needs to explicitly define these using CfnParameter and then reference their .value_as_string or similar.

The next thing you’ll run into is managing multiple parameters and their dependencies across different stacks within a CDK app.

Want structured learning?

Take the full Cdk course →