1 Comment

How to Statically Type CDK Configurations for Multiple Environments

As far as I’m aware, there isn’t a built-in solution for statically typing a configuration for the AWS Cloud Development Kit (CDK). So after a bit of research, I created a way of doing it!

CDK Context

Context is a way to pass in runtime values to our CDK application. You can do this several ways. In this example, we’ll use a combination of the cdk.json file and the —context or -c cli flag to pass in our runtime configuration.

Configuration File

The first step is to add your configuration values to the context key in the cdk.json file for each environment.

{
    ...
  "context": {
      ...
    "dev": {
      "region": "us-east-1",
      "appPrefix": "cc"
    },
    "prod": {
      "region": "us-east-1",
      "appPrefix": "cc"
    },
    "sandbox": {
      "region": "us-west-2",
      "appPrefix": "cc",
    }
    ...
  }
}

This example is using dev, sandbox, and prod as environment names, but you can call them any name you want.

Specify an Environment

The next step is to come up with a way to specify which environment to deploy to when your CDK app is deployed. You do this with the cli flag mentioned above: --context or -c. This flag allows you to pass in a value to CDK from the command line. In this example, we are going to pass in a config value that will specify which environment we want to use:

cdk deploy -c config=dev|sandbox|prod

Build the Configuration

After you’ve specified your configuration and specified an environment to run, the next step is to ingest the configuration and type it. Paste the following contents into a file named build-config.ts in your CDK project:

import * as cdk from "aws-cdk-lib";

// These values map to the keys in our cdk.json and make sure
// no values that aren't supported are passed in with the
// -c config=env flag
const supportedEnvironments = ["prod", "dev", "sandbox"] as const;

type SupportedEnvironments = typeof supportedEnvironments[number];

// This maps to the values you specified in your cdk.json file
// if you add any values to your cdk.json file, also add them here!
export type BuildConfig = {
  environment: SupportedEnvironments;
  appPrefix: string;
  region: string;
};

// This function is used by your CDK app and pulls your config values
// from the context
export const getConfig = (app: cdk.App): BuildConfig => {
  const env = app.node.tryGetContext("config");
  if (!env) {
    throw new Error(
      "Environment variable must be passed to cdk: `yarn cdk -c config=XXX`"
    );
  }
  if (!supportedEnvironments.includes(env)) {
    throw new Error(
      `${env} is not in supported environments: ${supportedEnvironments.join(
        ", "
      )}`
    );
  }
  // this contains the values in the context without being
  // validated
  const unparsedEnv = app.node.tryGetContext(env);

  return {
    environment: env,
    appPrefix: ensureString(unparsedEnv, "appPrefix"),
    region: ensureString(unparsedEnv, "region"),
  };
};


// this function ensures that the value from the config is
// the correct type. If you have any types other than
// strings be sure to create a new validation function
function ensureString(
  object: { [name: string]: any },
  key: keyof BuildConfig
): string {
  if (
    !object[key] ||
    typeof object[key] !== "string" ||
    object[key].trim().length === 0
  ) {
    throw new Error(key + " does not exist in cdk config");
  }
  return object[key];
}

Wrap Up: Statically Typed CDK Configurations

And that’s it! You now have a statically typed config you can use in your CDK scripts. I hope this example is useful in your CDK project.