CDK is a software library published and maintained by AWS. It is used to write infrastructure as code within AWS. It shares many similarities with CloudFormation templates which it actually uses ‘under the hood’. Through usage it may seem as if CDK is its own solution for IaC, but it is essentially a CloudFormation template generator with programming language abstractions on top to make writing architectures less tedious.
While knowledge of writing CloudFormation templates is not necessary to write most CDK code, it can be extremely useful when debugging certain error messages. Those with experience in writing CloudFormation templates will find CDK very familiar and much easier to learn as many concepts are very similar, if not the same.
Before going further, make sure you have the following command line tools installed:
At the time of writing the versions used are 2.7.0 for aws, 2.24.1 for cdk, and 3.8.12 for python.
Supported programming languages
AWS CDK is currently supported in the following languages:
AWS CDK is natively written in TypeScript. While it does also support other languages, those implementations aren’t native and instead make API calls (locally on your computer) to the TypeScript implementation. While this usually doesn’t matter there are some advanced (metaprogramming/custom libraries) features that are only available within TypeScript.
Constructs
A typical CDK application is comprised of the App, the Stacks within it and AWS Resources within the Stacks. Each of these is a type of Construct and are implemented from the Construct base class. Because of that, all Constructs have 3 base parameters:
App
The App is the root construct, it is what contains the Stack constructs. There are configurations that can be applied to it but for common usage it is unnecessary. Within CDK code this will often be a bare object that is simply used as the root that connects all the stacks within your application together.
Stack
Analogous to the stacks present in CloudFormation, Stacks in CDK are constructs/objects that contain AWS resources. AWS resources can only be created/defined within the scope of a Stack. By default stacks are deployed within account and region of the AWS credentials used for deployment, by passing the env parameter when instantiating a Stack it is possible to fixate the stack to a specific account and region.
Resources
Resources in CDK represent AWS resources that will be deployed. In some cases creating a resource will automatically also create subservient resources (e.g. creating a lambda function will also automatically create its role in the background). Pretty much any configuration that is possible via CloudFormation templates/AWS console is available, though it might different slightly in layout.
Within the CDK library resources can be defined in various levels of complexity. Generally the lower level constructs are more complex to configure but much more customizable and the higher level constructs are the opposite.
Low level constructs (L1)
CDK is developed downstream from CloudFormation. Low level constructs have exactly the same parameters as resources in CloudFormation templates. Their usage is generally avoided since they don’t have a robust type checking or autocomplete, heavily rely on external documentation to use, and are missing many useful bells and whistles. Any CDK Class that is prefixed with Cfn* represents an L1 construct.
Configuration of L1 constructs tends to be more verbose as no resources are created automatically, everything is manual. New AWS services/features are implemented starting from the lower level constructs. If you are using a cutting edge features, low level constructs may be the only ones available at the time. Here is a simple L1 implementation of a DynamoDB table.
High level constructs (L2)
L2 constructs are the most commonly used type when writing architecture in CDK. They have more features than L1 constructs and automatically create subservient resources. Parameters are fully typed and most have sane defaults, you will rarely have to reference external documentation. When a resource you create is used in another resource, e.g., assigning an existing S3 bucket to a CodePipeline, CDK will automatically apply minimal permission policies thus greatly reducing repetitive work with IAM. this also applies to networking configurations in most cases.
Here is a simple L2 implementation of a DynamoDB table.
Pattern level constructs (L3)
Pattern constructs are a step even further beyond L2 constructs. Rather than representing a single resource they represent a concept, a small scale generic solution that incorporates multiple resources for common use cases. Patterns are used in cases where you need a simple solution for a common problem when implementing architecture. They are often hit or miss in practice. If a pattern happens to match your use case perfectly you will find it very useful, but know that configuration of patterns is often limited and they sometimes don’t play nice with other constructs (L2 or other L3). If you need a more configurable version of a certain pattern it is often easier to just recreate what you need with L2 constructs.
Here is a simple L3 pattern that integrates an AWS ECS Fargate task with an AWS Events Event Rule to create a scheduled Fargate task.
To create (initialize) a CDK project we use the cdk init command.
First we create a directory for our project and enter it:
Note: CDK will autogenerate project files based on the current directory name.
In our case, we are creating a cdk app in python, so we are going to use the following:
Note: this command automatically creates a python virtual environment. If you are using pyenv, or another method of configuring python runtimes for projects do it before running the above command.
We activate our virtual environment:
It is a good idea to double check that we are using the correct virtual environment (which python3 command) and that we are using the correct python version (python3 --version command)
We install the python3 dependencies using the requirements.txt file that was autogenerated when we ran cdk init:
The initialization is complete, to make sure everything has been set up correctly, run the following command:
This should output a list of all stacks within our app, which in our case is just the autogenerated “CdkBasicsStack” stack.
In this section we will go trough the process of creating an example CDK application utilizing an S3 Bucket, AWS Events Event Rule, AWS Lambda function, and a DynamoDB table.
Architecture
The architecture of the example application is very simple. When a file is created on the S3 bucket an AWS Lambda will be triggered. The Lambda will fetch the file and write some information about it in a DynamoDB table using the AWS API. Structure being:
The application will work as follows:
1 A file is uploaded to the S3 Bucket.
2 Event rule is triggered and invokes the Lambda function.
3 Lambda function reads the contents of the file and writes it to the DynamoDB table.
Lambda function code
Since we are using an AWS Lambda we need a place to store its code. Create the inline_lambda_functions/ directory and contents so that is matches the structure below:
Note: This implementation is intended for simple functions only. If your lambda_function.py file exceeds 4KiB, or you need additional files/libraries within your deployment package you will need a form of CI/CD that will build and provide the deployment package for you.
Writing code
Most of the files do not need to be modified, we will only alter the CDK code of the architecture (cdk_basics_stack.py) and code of the lambda function (lambda_function.py).
cdk_basics/cdk_basics_stack.py
inline_lambda_functions/new_s3_object_handler/lambda_function.py
Deployment
Configure your AWS CLI so that it is using AWS credentials and region for the account you want to deploy to. CDK CLI also supports --profile flags which allow you to specify the AWS profile you would like to use when running cdk commands.
Note: If you use AWS SSO login via CLI note that cdk does not yet support AWS SSO login via CLI and as such credentials will have to be provided via environment variables or config files stored locally.
Bootstrap
You will probably need to create the CDK bootstrap on the target account. You can do so by simply running the following command. The bootstrap is a stack that is used as temporary storage for templates and artifacts used for deployment.
Deploying solution
Run the following command to deploy.
You should see status updates as the resources within the stack are being deployed and a satisfying finished message once it’s done. Any changes done should also be visible as Stack updates within the AWS CloudFormation service.
Relevant resources
This guide covers the absolute minimum to get a very simple solution up and running, here are some commonly used resources/websites to learn more or reference.