The Anatomy of a CDK App

Exploring an AWS Cloud Development Kit (CDK) App by Example

Craig Sloggett
Slalom Build
5 min readNov 12, 2020

--

While there’s an abundance of resources online to help developers start writing an AWS Cloud Development Kit (CDK) app, I wanted to distill some of this information down further into a ready-to-use guide to deployment.

In this post, I aim to provide a clear set of instructions that can be used to deploy a Relational Database Service (RDS) instance. I chose to use the RDS service as an example due to the level of complexity — it covers the majority of concepts but is still relatively simple.

Amazon lets you define your cloud infrastructure as CDK code using your choice of five supported programming languages. I have chosen TypeScript, but the principles apply to all of the languages.

For further context, the AWS Documentation page is truly a great resource.

A Beginner’s Guide to CDK

The AWS CDK Developer Guide provides a great overview of the key concepts but here’s a quick summary:

  • Apps are the final artifact of code that define one or more Stacks.
  • Stacks are equivalent to CloudFormation stacks and are made up of Constructs.
  • Constructs define one or more concrete AWS resources, such as the Amazon Relational Database Service (Amazon RDS).

Getting Started

The following prerequisites must be made available before starting development on your machine:

With these tools installed, create a project directory:

And initialize the app:

HelloWorld Distilled

With the new app initialized, a default directory layout is created. You will notice the app has been named based on the current working directory. I’ve chosen to name this app HelloCDK.

For the purposes of this article, only one file is of interest — lib/hello_cdk-stack.ts:

The rest of the directory is filled with files cdk uses to deploy the app. Feel free to poke around.

By default, we are given the following Stack constructor:

Default CDK Stack

This is where we will add an AWS resource to this app.

CDK Mechanics

A simple S3 example can be found in the Hello World Tutorial provided by Amazon. Following along with that tutorial, I have determined the process is essentially as follows:

  1. Review the desired AWS resource in the AWS Construct Library.
  2. Install the appropriate package.
    $ npm install @aws-cdk/aws-s3
  3. Import the library for use in the Stack.
    import * as s3 from '@aws-cdk/aws-s3';
  4. Define the resource in the Stack constructor.
    new s3.Bucket(this, 'MyFirstBucket', {});

Here’s a snippet of the S3 Stack constructor from the tutorial:

S3 Stack Constructor

In my experience, reviewing the AWS resource construct documentation is where most of my time is spent. It has been incredibly valuable to know which parameters are available, which are required and comparing these to what is available through the familiar AWS console.

Applying Ourselves

If you browse through the RDS CDK module documentation, you can see that there are different resource constructs associated with deploying the service. Each construct will provide the ability to deploy different RDS services.

We will be focusing on the DatabaseInstance class to deploy a (non-clustered) MySQL database instance.

Following the same process outlined above, we can begin to define an RDS instance:

  1. Install the appropriate packages.
    $ npm install @aws-cdk/aws-rds
    $ npm install @aws-cdk/aws-ec2
  2. Import the libraries for use in the Stack.
    import * as rds from '@aws-cdk/aws-rds';
    import * as ec2 from '@aws-cdk/aws-ec2';
  3. Define the resource in the Stack constructor.
    new rds.DatabaseInstance(this, 'database-instance', { ... });

Now is a good time to read through the construct properties. There are only three required properties — the rest are optional.

Here’s an example of the bare minimum required to deploy an unversioned SQL Server SE instance to a known VPC using only the required properties:

Only engine , masterUsername and vpc are passed into the construct. Understanding each of these properties can take you down a rabbit hole of new information, so let me explain what’s going on.

Cloud Forming the CDK Way

One important insight I’ve learned when working with the CDK is that it is essentially a wrapper around the existing CloudFormation (CF) process. You can see evidence of this in the fact that there are CF (CDK) modules that map directly to CF resources.

From my understanding, using the higher level construct modules over the CF modules is preferred since you get added benefits “for free” with little code since things like security are abstracted away. This article from Amazon has a good explanation on why this is preferable.

Knowing this, we can see that there are a few ways to provide the details needed to deploy our AWS resources.

Engine — the engine parameter requires an IInstanceEngine type, which is an:

Interface representing a database instance (as opposed to cluster) engine.

Requiring an interface as a parameter is a common pattern in Stack constructors. This allows for some flexibility such that we can use resources that have been defined outside of our CDK app. For example, deploying to an existing VPC.

Master Username — the master username can be passed in as a string, simple!

VPC — similar to the database engine, the VPC parameter requires an IVpc type, which is an:

Interface representing an AWS Virtual Private Cloud.

In our example above, we are using the fromLookup method of the Vpc class which allows us to:

Import an existing VPC from by querying the AWS environment this stack is deployed to.

My preference has been to define my resource interfaces together as constants and simply pass in the constants to the constructor method. I find this is a cleaner approach than defining an interface inline when assigning a value to a construct parameter.

Putting It All Together

The following gist shows a working SQL Server DB instance construct:

To build this module, I have expanded a bit on the basic CDK mechanics outlined above:

  1. Review the desired AWS resource in the AWS Construct Library.
  2. Install the appropriate package.
  3. Import the library for use in the Stack.
  4. Build the necessary resource interfaces.
  5. Define the resource in the Stack constructor, passing in a resource interface where appropriate.

Closing Remarks

I hope that this beginner’s guide to using CDK will prove useful to someone just having read the introductory documentation and is looking for a bit more useful example. The process I’ve outlined has been useful for me to build CDK modules quickly and easily.

I found it overwhelming at first to navigate all of the documentation, but after building my first app, it has become much easier! I hope this summary is able to provide a less overwhelming experience to others trying to pick up CDK for the first time.

--

--

Craig Sloggett
Slalom Build

Cloud, DevOps and Security Architect at Slalom Build.