Intro to AWS CDK

This is originally written in 2023 in my old blog. I will update it post publish.
From the documentation, AWS CDK is described as a framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation.
As a visual person, I found this diagram very helpful to understand what AWS CDK does:
Ignoring the new terminologies such as Stack(s) and Construct, we can see that AWS CDK (the big orange box) allow us to write code (in TypeScript, JavaScript, etc) to produce CloudFormation template.
The AWS CDK documentation is very comprehensive and there are also many great resources to learn that is being added and updated regularly.
In this post, I will share what I learned so far about AWS CDK and what is great, what is promising, and what is not so great about it in relation to how we use AWS.
What is Great
Less verbosity
Defining AWS resources using AWS CDK is much less verbose. The side-by-side comparison below shows DynamoDB table definition
AWS CDK
new Table(this, 'mockserver-persistence', {
tableName: `${EnvironmentName}-mocks-persistence`,
timeToLiveAttribute: 'TTL',
partitionKey: {
name: 'primaryPk',
type: AttributeType.STRING
},
sortKey: {
name: 'primarySk',
type: AttributeType.STRING
},
billingMode: BillingMode.PAY_PER_REQUEST,
})
AWS CloudFormation
MocksPersistenceTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub '${EnvironmentName}-mocks-persistence'
TimeToLiveSpecification:
AttributeName: TTL
Enabled: true
AttributeDefinitions:
- AttributeName: primaryPk
AttributeType: S
- AttributeName: primarySk
AttributeType: S
KeySchema:
- AttributeName: primaryPk
KeyType: HASH
- AttributeName: primarySk
KeyType: RANGE
BillingMode: PAY_PER_REQUEST
Less Complexity
The verbosity will be even more pronounced when we add other resources and make sure they all have the right access and permissions.
AWS CDK
const table = new Table(this, 'mockserver-persistence', {...
...
const lambda = new Function(...)
...
table.grantReadData(lambda)
AWS CloudFormation
Type: AWS::IAM::Role
... 10 +lines
Type: AWS::IAM::Policy
... 10+ lines
From the above, we can see that the complexity of CloudFormation was hidden from us.
Access to Low Level CloudFormation Resources
In real life, the abstraction that is provided by AWS CDK might not fit our needs as we might have done some wrangling to our CloudFormation template for our need.
In these scenarios, we can use the CDK’s L1 (layer 1) constructs which is directly represent CloudFormation resources (these are CDK classes with “Cfn” prefix)
An example:
new CfnTable(this, 'id', {
tableName: `${EnvironmentName}-mocks-persistence`,
timeToLiveSpecification: {
attributeName: 'TTL',
enabled: true
},
attributeDefinitions: [
{
attributeName: 'primaryPk',
attributeType: AttributeType.STRING
},
{
attributeName: 'primarySk',
attributeType: AttributeType.STRING
}
],
keySchema: [
{
attributeName: 'primaryPk',
keyType: 'HASH'
},
{
attributeName: 'primarySk',
keyType: 'RANGE'
}
],
billingMode: BillingMode.PAY_PER_REQUEST
})
Another option is to use the CfnInclude class to include our CloudFormation resources either .yaml or .json (recommended) file.
An example:
CfnInclude(this, 'Template', {templateFile: 'template.yaml'});
The benefit of this approach is that it will preserve the resources’s original logical IDs.
Constructs - NodeJsFunction
There are many constructs that make life easier when working with AWS. Once such construct is NodeJsFunction that allow us to write Lambda function in TypeScript which will be bundled using esbuild.
This will the work of transpiling our TypeScript to JavaScript and bundling it with required dependencies will be taken care of for us.
What is Promising
One of the promise of AWS CDK is that our infrastructure code (written in TypeScript, etc) can be as testable as any other code we write.
There are 2 types of tests which are Fine-grained assertions and Snapshot tests.
Fine-grained assertions are most common and useful to catch regressions which is the main purpose of writing infrastructure tests.
An example:
// assemble
... boilerplace code to get template object to assert ...
// assert
template.hasResourceProperties('AWS::DynamoDB::Table', Match.objectLike({
BillingMode: 'PAY_PER_REQUEST',
TimeToLiveSpecification: {
AttributeName: 'TTL',
Enabled: true,
},
KeySchema: [
{ AttributeName: 'primaryPk', KeyType: 'HASH' },
{ AttributeName: 'primarySk', KeyType: 'RANGE' },
],
AttributeDefinitions: [
{ AttributeName: 'primaryPk', AttributeType: 'S' },
{ AttributeName: 'primarySk', AttributeType: 'S' },
]
}))
While great and useful, after writing tests for awhile, it feels that the tests is just a representation of the CloudFormation template so good knowledge of CloudFormation is necessary.
More to come:
Live Coding AWS CDK app
Migration of our Mocks API (replacement of MockLab) to use AWS CDK
Subscribe to my newsletter
Read articles from EJ directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
