How to Deploy to AWS with CDK

Caleb Reigada
9 min readMar 6, 2023

--

Learn to build robust apps using infrastructure-as-code in the cloud.

In this tutorial, we will cover the basics of using AWS CDK so that you can deploy your own applications using Infrastructure-as-Code on AWS.

AWS CDK or Cloud Development Kit is AWS’s framework for defining cloud infrastructure in code and provisioning it through AWS Cloud Formation. The AWS CDK supports TypeScript, JavaScript, Python, Java, C#/.Net, and Go. Using AWS CDK to deploy cloud infrastructure has the following benefits over building AWS applications manually:

  • Predictably and repeatedly perform infrastructure deployments with rollbacks on error.
  • Easily share infrastructure design defined in CDK
  • Add unit tests and source control to make infrastructure more robust
  • Take advantage of programming language features such as loops and conditionals to dynamically deploy infrastructure as needed

Prerequisites

Before getting started, here are a few steps you must complete.

Create an AWS Account

If you do not have an AWS account already or would like a separate account for CDK deployments, you need to create a free account here.

Install AWS CLI

The AWS CLI is used to configure authorization between your local system and your AWS account. AWS CLI installation instructions here.

Confirm installation with aws --version

Install Node and TypeScript

Regardless of what language you use, Node will be required for CDK to run. Node installation instructions here.

Confirm installation with npm --version

For this tutorial we will be using TypeScript. You can install TypeScript after installing Node by running the command below.

npm install -g typescript

Install CDK Package

Now you need to install the CDK package and toolkit. This can be done by running the commands below.

npm install aws-cdk-lib

npm install -g aws-cdk

Confirm installation with cdk --version

Note: On Windows, you may need to add the npm path to you environment variables and change the execution policy to run the cdk command. If you are having trouble at this step see this and this tutorial.

Project Overview

In this tutorial we will be making a simple AWS application where we create a Lambda that writes a dummy file to an S3 bucket. First, we will set up a new IAM User and authorize them to deploy infrastructure on your AWS account using Cloud Formation. Then we will define with AWS CDK an S3 bucket, a Python Lambda function, and an IAM Role for the Lambda to write to the S3 bucket. Once these constructs are defined we will deploy them via the CDK. Finally, we will end by cleaning up the resources created with CDK.

CDK Project Overview

Set Up IAM User

It is possible to deploy to your AWS account using the root user, but this is not best practice. It is recommended to create a new user for CDK deployments and grant them minimal permissions. In this tutorial we will create a new user with Admin permissions for simplicity.

Create an IAM User

In your AWS account create a new IAM user with permissions to deploy infrastructure with CDK.

  1. Go to IAM
  2. Go to Users and Create User
  3. In Step 1 enter a username for your user
IAM User Create Username

Create Policy for CDK

Create a new policy for your user to deploy infrastructure using CDK.

4. In Step 2 click on attach policies directly

5. Click on create policy

6. In the JSON tab copy the following:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": [
"arn:aws:iam::*:role/cdk-*"
]
},
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}

7. Proceed by selecting Next:Tags > Review

8. Name the policy

Create Policy Name

9. Select Create Policy > Attach Policy to User > Next

10. Create the user

Create Access Key

Create an access key for your IAM User so that you can configure authorization with the AWS CLI.

11. Go to the newly created user on IAM Users

12. Go to the Security Credentials tab

IAM User Security Credentials Tab

13. Scroll down and select Create Access Key

14. For usage select CLI and check the box saying that you understand the recommendation

15. Create the access key and download it as a csv file

Configure AWS authorization

Authorize the credentials of the IAM User created with the AWS CLI.

16. In a terminal run this command: aws configure

You will be prompted to input your IAM User credentials, region name and output format.

AWS Access Key ID [None]: <your access key>
AWS Secret Access Key [None]: <your secret key>
Default region name [None]: us-west-1
Default output format [None]: json

Bootstrap your Account

Before making any deployments you must bootstrap your account.

17. Bootstrap your account by running the following command with your information: cdk bootstrap aws://<your account id>/<your region>

CDK Bootstrap Output

Initialize your app

Now we can get started with creating the CDK application. Firstly, we will need to initialize a new app which will create files necessary for CDK deployments.

In a new directory where you would like your CDK application code to be stored, run this command: cdk init app --language typescript

The files created from the above command should be similar to the image below.

CDK file structure

bin/cdk_demo.ts is what is called to create the CDK application. In this file, any stacks that you created are imported and initialized.

cdk_demo.ts file

lib/cdk_demo-stack.ts is a sample stack. Here you define constructs such as S3 buckets or Lambdas.

cdk_demo-stack.ts file

cdk.json has meta data needed for app creation including pointing to the file that creates the CDK app. Here the file that creates the app is default set to bin/cdk_demo.ts.

cdk.json file

Add Infrastructure

With the CDK application initialized, we can add our infrastructure to the lib/cdk_demo-stack.ts file. We will create an S3 bucket, a Lambda Function, and Lambda role for accessing the bucket.

Full Stack Code

Below is all the CDK code for our application in lib/cdk_demo-stack.ts for reference. I will break down each line in the following sections.

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import {
Function,
Runtime,
Code
} from 'aws-cdk-lib/aws-lambda';
import {
Role,
ServicePrincipal,
PolicyStatement
} from 'aws-cdk-lib/aws-iam';
import * as path from 'path';

export class CdkDemoStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// S3 Bucket
const demoBucket = new Bucket(this, 'demo-bucket-id', {
bucketName: 'unique-bucket-name',
// allow cdk to destroy bucket
removalPolicy: cdk.RemovalPolicy.DESTROY
// Add additional bucket configurations here
});

// Lambda Function Code
const lambdaCode = Code.fromAsset(path.join(__dirname, 'lambda_code'));

// Policy for allowing access to the S3 bucket
const s3AccessPolicy = new PolicyStatement({
actions: ['s3:*'],
resources: [
`${demoBucket.bucketArn}*`,
`${demoBucket.bucketArn}/*`
]
});

// Lambda role for accessing S3
const lambdaRole = new Role(this, 'lambda-role-id', {
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
});

// Add policy to lambda role
lambdaRole.addToPolicy(s3AccessPolicy);

// Lambda
const demoLambda = new Function(this, 'lambda-id', {
runtime: Runtime.PYTHON_3_9,
handler: 'demo_lambda.lambda_handler',
code: lambdaCode,
role: lambdaRole,
environment: {
// Passes bucket name as environment variable
BUCKET_NAME: demoBucket.bucketName
}
});

}
}

S3 Bucket

The below code defines a new S3 bucket. The name provided in the bucketName field must be unique. the removalPolicy is set to allow CDK to destroy this bucket during cleanup. The default will prevent CDK from destroying the bucket to ensure data is not accidentally deleted.

//IMPORTS
//---------------------------
import * as cdk from 'aws-cdk-lib';
import { Bucket } from 'aws-cdk-lib/aws-s3';

// STACK CONSTRUCTOR METHOD
//---------------------------
// S3 Bucket
const demoBucket = new Bucket(this, 'demo-bucket-id', {
bucketName: 'unique-bucket-name',
// allow cdk to destroy bucket
removalPolicy: cdk.RemovalPolicy.DESTROY
// Add additional bucket configurations here
});

Lambda Code

Create a new folder within lib named lambda_code. Within this new folder, create a Python file named demo_lambda.py. Copy the below code into the file. This code creates a dummy file and writes it to an S3 bucket.

import boto3
import os
from datetime import datetime
import logging


def lambda_handler(event, context):
write_dummy_file()

def write_dummy_file():
s3 = boto3.resource('s3')
bucket_name = os.environ['BUCKET_NAME']

content_string = "blah blah"
encoded_string = content_string.encode('utf-8')

file_name = f"dummy_file_{datetime.now().strftime('%Y%m%d')}.txt"
s3_file_path = f"test/{file_name}"

log_success = f"{file_name} was uploaded to {bucket_name}."
log_fail = f"Error occured while attempting to write to {bucket_name}"

try:
s3.Bucket(bucket_name).\
put_object(Key=s3_file_path, Body=encoded_string)
logging.info(msg=log_success)
except Exception as e:
logging.error(msg=f"{log_fail}:\n{e}")

Lambda Code CDK

Back in the Stack, you can point to the Python file you just created using the snippet below.

//IMPORTS
//---------------------------
import { Code } from 'aws-cdk-lib/aws-lambda';
import * as path from 'path';


// STACK CONSTRUCTOR METHOD
//---------------------------
// Lambda Function Code
const lambdaCode = Code.fromAsset(path.join(__dirname, 'lambda_code'));

S3 Access Policy

We must create a new policy for the Lambda to be able to write to S3. The policy required is defined below.

//IMPORTS
//---------------------------
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';

// STACK CONSTRUCTOR METHOD
//---------------------------
// Policy for allowing access to the S3 bucket
const s3AccessPolicy = new PolicyStatement({
actions: ['s3:*'],
resources: [
`${demoBucket.bucketArn}*`,
`${demoBucket.bucketArn}/*`
]
});

Lambda Role

Define a new role the the Lambda can assume and attach the policy we just created to it.

//IMPORTS
//---------------------------
import {
Role,
ServicePrincipal,
PolicyStatement
} from 'aws-cdk-lib/aws-iam';


// STACK CONSTRUCTOR METHOD
//---------------------------
// Lambda role for accessing S3
const lambdaRole = new Role(this, 'lambda-role-id', {
assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
});

// Add policy to lambda role
lambdaRole.addToPolicy(s3AccessPolicy);

Lambda

Finally, we can define the Lambda by using the Code, Role and Bucket defined above. Make sure the handler is correctly mapped to your Python file and handler function.

//IMPORTS
//---------------------------
import {
Function,
Runtime,
Code
} from 'aws-cdk-lib/aws-lambda';


// STACK CONSTRUCTOR METHOD
//---------------------------
// Lambda
const demoLambda = new Function(this, 'lambda-id', {
runtime: Runtime.PYTHON_3_9,
handler: 'demo_lambda.lambda_handler',
code: lambdaCode,
role: lambdaRole,
environment: {
// Passes bucket name as environment variable
BUCKET_NAME: demoBucket.bucketName
}
});

Deploy to AWS

We can now deploy the infrastructure defined with CDK to our AWS account. To do this, only 2 steps are required.

  1. Run the command cdk synth to convert your CDK code to Cloud Formation template
  2. Run the command cdk deploy to deploy your infrastructure to your AWS account

After inputting yes when prompted in the command line you will see output similar to that of below.

CDK deploy output

After the deployment completes, if you go to your account, you will see your Lambda and S3 deployed. You can execute your lambda and see a new file created in your S3 bucket

Clean Up

Once you are done using the resources deployed with CDK, we can clean up everything simply by running the following command: cdk destroy

Summary and Final Thoughts

In this tutorial you learned how to install, configure, and use AWS CDK to deploy infrastructure as code to AWS. You created a new CDK stack that defined an S3 bucket and a Lambda that writes sample files to that bucket. You should now have the knowledge necessary to start converting your existing AWS apps to CDK or even starting new CDK apps from scratch. As you explore and develop your CDK applications, I recommend you first test by creating resources manually and confirming what works and what does not.

References

https://docs.aws.amazon.com/cdk/v2/guide/home.html

--

--

Caleb Reigada

Data Engineering, Machine Learning, and anything in between