This post is #1 of a multi-part series on the AWS CDK with TypeScript written during Melbourne lockdown #6.
This post will be an introduction to working with the AWS CDK in TypeScript.
It will lay the foundations for future posts in this series by introducing a simply example of a simple CDK application and speaking to some useful resources and definitions. This is a series where I will write one post for each day of our current lockdown in Melbourne (lockdown #6 that is expected to last seven days).
The app key contains the command that the AWS CDK CLI will execute to start the application.
For example, if we run npm run cdk synth to generate the application, it will use ts-node to enable us to run the bin/aws-cdk-with-typescript-foundations.ts without the need to transpile the file to JavaScript first.
The AWS CDK project comes with a list of useful commands to help you get started.
Command
Description
npm run build
Compile TypeScript to JavaScript
npm run watch
Watch for changes and compile
npm run test
Perform the Jest unit tests
cdk deploy
Deploy this stack to your default AWS account/region
cdk diff
Compare deployed stack with current state
cdk synth
Emits the synthesized CloudFormation template
cdk destroy
Tear down your deployed stack
When we work through all of our applications throughout this series, we will make significant use of these commands.
Creating our first stack
In our first example, we are simply going to create a simple stack that creates an S3 bucket.
To do so, we will need to install the @aws-cdk/aws-s3 package.
$ npm install --save @aws-cdk/aws-s3
It is important that all package versions for the @aws-cdk packages are at parity. If you run into any issues moving forward, ensure they are all at the latest.
To create our example bucket, we will update the lib/aws-cdk-with-typescript-foundations-stack.ts file to the following:
import * as cdk from "@aws-cdk/core";
import * as s3 from "@aws-cdk/aws-s3";
export class AwsCdkWithTypescriptFoundationsStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// The code that defines your stack goes here
new s3.Bucket(this, "MyFirstBucket", {
bucketName: "hello-aws-cdk-my-first-bucket",
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
}
}
This example is contrived, but it will allow us to create our first S3 bucket hello-aws-cdk-my-first-bucket.
For each CDK module, the documentation is your best friend. The AWS CDK S3 documentation can help us to explain our new s3.bucket invocation.
The overview documentation shows that new Bucket(this, 'MyFirstBucket'); is the basic example of creating an unencrypted s3 bucket. The pattern for all constructs is that you pass the scope as the first argument (this in our case), the name reference for this construct and, depending on the construct, the props object.
In our code, we have added the optional prop bucketName to the new s3.Bucket(this, "MyFirstBucket", { ... }); invocation in order to control the naming of our bucket.
We are also explicitly adding a removalPolicy to be the enum value cdk.RemovalPolicy.DESTROY which will cause the bucket to be deleted when the stack is destroyed.
At this stage, we are ready to deploy our CDK stack. First, however, we will update our first test to reflect our new code.
Updating our first test
Within test/aws-cdk-with-typescript-foundations.test.ts, we have a test file that will test the empty stack created when we initialized the project.
We can run tests by running npm run test or the shorthand npm t. Doing so will lead to a failed test (thanks to our change):
$ npm t
> aws-cdk-with-typescript-foundations@0.1.0 test
> jest
Resources
[+] AWS::S3::Bucket MyFirstBucketB8884501
FAIL test/aws-cdk-with-typescript-foundations.test.ts
✕ Empty Stack (139 ms)
● Empty Stack
Template comparison produced unacceptable match
16 | );
17 | // THEN
> 18 | expectCDK(stack).to(
| ^
19 | matchTemplate(
20 | {
21 | Resources: {},
at StackMatchesTemplateAssertion.assertOrThrow (node_modules/@aws-cdk/assert/lib/assertions/match-template.ts:38:13)
at StackInspector._to (node_modules/@aws-cdk/assert/lib/inspector.ts:25:15)
at StackInspector.to (node_modules/@aws-cdk/assert/lib/inspector.ts:15:14)
at Object.<anonymous> (test/aws-cdk-with-typescript-foundations.test.ts:18:20)
--------------------------------------------------------------------------------------
{
"Resources": {
"MyFirstBucketB8884501": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketName": "hello-aws-cdk-my-first-bucket"
},
"UpdateReplacePolicy": "Delete",
"DeletionPolicy": "Delete"
}
}
}
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 3.358 s
Ran all test suites.
The output tells us that the test failed and gives an output of the Resources object that was created. Given that we have confirmed that this is the expected output, we can copy that value and modify our test file:
import {
expect as expectCDK,
matchTemplate,
MatchStyle,
} from "@aws-cdk/assert";
import * as cdk from "@aws-cdk/core";
import * as AwsCdkWithTypescriptFoundations from "../lib/aws-cdk-with-typescript-foundations-stack";
test("Empty Stack", () => {
const app = new cdk.App();
// WHEN
const stack =
new AwsCdkWithTypescriptFoundations.AwsCdkWithTypescriptFoundationsStack(
app,
"MyTestStack"
);
// THEN
expectCDK(stack).to(
matchTemplate(
{
Resources: {
MyFirstBucketB8884501: {
Type: "AWS::S3::Bucket",
Properties: {
BucketName: "hello-aws-cdk-my-first-bucket",
},
UpdateReplacePolicy: "Delete",
DeletionPolicy: "Delete",
},
},
},
MatchStyle.EXACT
)
);
});
Rerunning the test now gives us the following output:
$ npm t
> aws-cdk-with-typescript-foundations@0.1.0 test
> jest
PASS test/aws-cdk-with-typescript-foundations.test.ts
✓ Empty Stack (106 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.504 s, estimated 4 s
Ran all test suites.
Success! Our stack now has tests for the expected output resources.
Deploying the stack
To deploy the stack, we need to synthesize and deploy the stack. In order to do this, it is assumed that your environment is setup with the correct AWS credentials.
To synthesize and deploy, let's run the following:
The will be a new cdk.out folder created at the root of the project with the synthesized output. I won't go into that folder, but feel free to explore the output files.
We can now deploy our project with npm run cdk deploy.
Depending on your stack, you may get a section requiring approval for a change to identity and access management rules based on your constructs and permissions required for them.
Validating our stack that deployed
We can use the AWS CLI to confirm that our bucket was created.
Assuming, you have the CLI installed, run the following to list your buckets and grep for the specified name:
$ aws s3 ls | grep hello-aws-cdk-my-first-bucket
2021-08-06 10:02:39 hello-aws-cdk-my-first-bucket
Success! We now have a bucket that we can use to store our data.
Destroying the stack
To ensure we are not paying for unused resources, it is important that we tear down our stack after usage.
We can do so with the cdk destory command.
Run the following and select y to confirm the tear down:
$ npm run cdk destroy
> aws-cdk-with-typescript-foundations@0.1.0 cdk
> cdk "destroy"
Are you sure you want to delete: AwsCdkWithTypescriptFoundationsStack (y/n)? y
AwsCdkWithTypescriptFoundationsStack: destroying...
✅ AwsCdkWithTypescriptFoundationsStack: destroyed
We can confirm the bucket was destroyed with the AWS CLI:
$ aws s3 ls | grep hello-aws-cdk-my-first-bucket
# empty response
Awesome! We have gone through an entire lifecycle of a stack deployment using the AWS CDK with TypeScript.
Understanding the constructs
We have breezed through the basic concepts of the AWS CDK. However, it would be remiss not to link to some resources to better understand some of the concepts that we have skimmed over.
I highly recommend having a read, but for now I will speak to the three types of constructs:
Construct
Description
Low level
Constructs which are automatically generated from and map 1:1 to all CloudFormation resources and properties - they have a CfnXXX prefix.
High level
High Level constructs that initialize CFN resources with sane defaults and provide a high level interface to said resource.
CDK pattern constructs
Constructs which stitch together multiple resources together, for example the LambdaRestApi construct which creates an api gateway that's backed by Lambda.
In our example above, we used a high level construct to initialize our S3 bucket. In future posts, we will work with custom constructs and building out CDK pattern constructs.
Summary
Today's post demonstrated how to get started with an new project on the AWS CDK with TypeScript.
We went over the project structure, the basic commands, construct definitions as well as going through an entire lifecycle of a stack deployment using the AWS CDK with TypeScript to deploy our first S3 bucket.
This is just the start of the series and over the coming days we will work through local development as well as more complex examples on deploying CDNs and lambda functions.