How to manage Package Versions in AWS CDK


Borislav Hadzhiev

Last updated: Apr 13, 2022


Check out my new book

The Problem #

Note that the information in this article refers to CDK version 1. Fortunately, CDKv2 has a monolithic package that only contains stable APIs.

When there is a mismatch between versions of the AWS CDK Constructs Library packages we have installed, we often get cryptic errors.

Since all of the modules in the CDK Constructs library are developed and released together with the same version number, they are intended to be used together with matching versions.

An error message we get due to version mismatch looks something like:

Unable to determine cloud assembly output directory. Assets must be defined indirectly within a "Stage" or an "App" scope

The different versions of packages in the construct library often have dependencies of different versions which is the main reason for compatibility issues.

Many of the Constructs provided by the CDK library are at an experimental stage, which means these modules are actively being developed and are subject to breaking changes.

These packages don't follow semantic versioning and often introduce breaking changes with minor or patch releases.

Updating the version of a CDK package, especially one that's marked - experimental, often means we have to update the associated code in our application.

The Solution #

We have to lock down our CDK package versions and make sure all of our installed construct packages are the same version.

Npm packages follow semantic versioning.

The packages are published in form of major.minor.patch version, i.e. 1.91.1.

The dependencies in a package.json file could look something like:

{ "dependencies": { "@aws-cdk/assert": "^1.94.1", "@aws-cdk/aws-apigatewayv2": "^1.94.1", "@aws-cdk/aws-s3": "^1.94.1", "@aws-cdk/core": "^1.94.1", "aws-cdk": "^1.94.1" } }

Notice how there is a ^ (caret) symbol infront of the version number, this means that we indicate to the npm registry to install the version 1.94.1 or the latest minor or patch version, i.e. 1.95.0.

Since we already know that some packages in the CDK Constructs library are marked experimental and don't follow semantic versioning, we should keep all our CDK packages the same version, we should remove the ^ symbol, and only install a specific concrete version:

{ "dependencies": { "@aws-cdk/assert": "1.94.1", "@aws-cdk/aws-apigatewayv2": "1.94.1", "@aws-cdk/aws-s3": "1.94.1", "@aws-cdk/core": "1.94.1", "aws-cdk": "1.94.1" } }

This way we are able to lock our CDK versions down and prevent some cryptic errors and incompatibility issues in the future.

The same would count if you see a tilde ~ symbol in front of your package version:

{ "dependencies": { "@aws-cdk/assert": "~1.94.1", "@aws-cdk/aws-apigatewayv2": "~1.94.1", "@aws-cdk/aws-s3": "~1.94.1", "@aws-cdk/core": "~1.94.1", "aws-cdk": "~1.94.1" } }

In the npm ecosystem this means install the version 1.94.1 or install the latest patch version, i.e. 1.94.9.

To avoid inconsistency between versions of dependencies we should again remove the ~ symbol and just lock down the version.

A common gotcha with npm is when you run a command to install a specific version:

npm install @aws-cdk/aws-s3@1.94.1

Npm adds the ^ symbol by default so your versions look like:

{ "dependencies": { "@aws-cdk/aws-s3": "^1.94.1" } }

This means that the next time we run npm install we could possibly install the latest minor version, i.e. 1.99.1 and introduce breaking changes.

The solution is to use the --save-exact flag:

npm install --save-exact @aws-cdk/aws-s3@1.94.1

This way we only install the specific, locked down version of the package:

{ "dependencies": { "@aws-cdk/aws-s3": "1.94.1" } }

Don't install the CDK CLI globally #

The recommendation in the AWS docs is to install the aws-cdk cli globally.

The CDK CLI installation on your machine must be an equal or higher version than the versions of the CDK constructs installed in your application.

By installing the CDK CLI globally, you have one more thing to think about and remember to update, otherwise you get cryptic errors and incompatibility issues.

When you have multiple projects with different CDK Constructs versions, it's hard to remember to keep your CDK CLI global installation's version higher than all of them.

The solution is to not install the CDK CLI globally and rather use a local installation with npx cdk.

Npx by default checks whether a command exists in our local project, if it does it executes it. If the command doesn't exist locally it checks for a globally installed version and runs that.

If we don't have the module globally installed it installs the module prior to executing the command.

Running npx cdk will look for a local version of the aws cdk module and if one exists it will use that one. If a local version isn't installed, it will fall back to the globally installed version.

Your package.json with npx would look something like:

{ "scripts": { "cdk-bootstrap": "npx cdk bootstrap", "cdk-synth": "npx cdk synth my-stack", "cdk-deploy": "npx cdk deploy my-stack --outputs-file ./cdk-exports-dev.json", "cdk-create-stack": "npm run cdk-bootstrap && npm run cdk-deploy", "cdk-destroy": "npx cdk destroy my-stack", }, "dependencies": { "aws-cdk": "1.94.1" } }

By prefixing our cdk commands with npx we don't have to worry about maintaining a globally installed and up to date CDK CLI version.

Relying on globally installed versions of packages is a terrible idea when you collaborate on projects with other developers, it leads to problems of the sort:

"Well, it works on my machine, so I don't know, you must be doing something wrong".

Summary #

To avoid version compatibility issues, we have to lock down our CDK constructs versions.

We have to keep in mind that many of the constructs are marked as experimental, don't follow semantic versioning and might introduce breaking changes with minor or patch version updates.

Never rely on global installations of packages when collaborating with others - use npx and rely on a locally installed version of the CDK CLI.

Further Reading #

Use the search field on my Home Page to filter through my more than 3,000 articles.