A good approach to any production-ready infrastructure is to separate out your applications into a develop, staging (or QA) and production environment. These environments help to separate out your testing environments from your live users. In between deployments to these environments you'll likely also run all kinds of tests through a CI pipeline of some sort. In AWS, it's also suggested to separate your environments into separate accounts for better isolation in the event of a security incident.

If you're using this multi-env architecture, you'll need a way to specify the differences in environments when you're deploying to each one. E.g. your production environment might have specific monitoring or alarms that your develop environment might not have. Or you may want to keep your log-retention to a minimum on your develop environment but store for longer in production. To handle the differences in each environment, you'll need to define configuration variables that you can read during deployment that will allow your deployment tools to update your infrastructure accordingly.

In this post, I'll be addressing a simple way to do this multi-env configuration using the AWS CDK.

The CDK Context

The CDK allows you to provide extra context to your code through CDK context variables. You can do this one of two ways

1. Specify variables under the "context" key in your cdk.json

"context": {
    "my_var": 1234
}

2. By passing the value through the cli with the --context (-c) flag:

cdk synth -c my_var=1234

When you pass a context parameter, you can get it's value through the CDK like so:

const my_var = this.node.tryGetContext('my_var');

This is all fine and dandy of course, but I'm not suggesting you pass in all of your environment variables through the CLI or in one big object in the cdk.json. Instead, a better approach is to use both the cdk.json and the CLI in tandem to allow for multiple configurations that can be specified during deployment.



Setting up for multiple envs

The first thing you'll want to do, is specify your environment variables in the cdk.json like so:

"context": {
    "dev": {
        "dbUsername": "devUser"
    },
    "staging": {
        "dbUsername": "stagingUser"
    },
    "prod": {
        "dbUsername": "prodUser"
    }
}

Here, we've specified a JSON object containing environment-specific variables, each a direct child of the "context" key.

Next, we want to be able to specify which of those objects we want to use when deploying. To do this, we'll specify which environment we want to use using the -c flag:

cdk deploy --all -c env=dev

By doing this, we're passing a new context variable during runtime that can be retrieved in the code. Now we'll use this new context variable to specify which of the other context variables we want to use. For example:


interface BuildConfig {
    dbUsername: string;
}

const env: string = app.node.tryGetContext('env');
const buildConfig: BuildConfig = app.node.tryGetContext(env);

We're now able to specify a new context variable at runtime, and then use that variable to specify which of the other context variables we want to retrieve. We can now set environment-specific variables in our cdk.json and specify which environment we want to deploy to with the -c env=<environment key> flag.

It may not be as easy and straightforward as other IAAC tools to configure multiple environments and I wouldn't be surprised if they do come out with better support for this use-case, but for now this is an easy-enough way to configure your CDK deployments to use multiple environment configurations.