In this fast-moving world of evolving technology, where every day brings forth a new innovation or technology to the market, we find ourselves continually relying on the backbone of our digital experiences: web and mobile applications.
Many developers and programmers are familiar with developing and designing a performant and scalable web application in this era where there are plenty of languages, frameworks, and tutorials available online. But the problem arises when we have to make our application go live. This is when we require the help of a cloud/devops engineer.
But what if there was a way for developers to deploy the application using IAAC, i.e., infrastructure as code? This is when AWS CDK comes into play.
Introduction to AWS CDK
The AWS Cloud Development Kit (AWS CDK) is a framework that lets you define your cloud infrastructure as code using familiar programming languages.
The AWS CDK was first announced as a developer preview at the AWS Summit in New York on July 17, 2018. It reached general availability (GA) on July 11, 2019, with initial support for TypeScript and Python programming languages. Since then, it has expanded to support Java and C# as well.
Now, if you have a good understanding of AWS Services and it’s properties, you can easily write a CDK script to deploy your application or just create the infrastructure required to host your application.
I know what you are thinking 😭 and to not waste much of your time let’s start the actual implementation.
Agenda
In this tutorial, we will look into how to deploy our static frontend application onto an AWS S3 bucket, accompanied by a CloudFront distribution with Origin Access Identity. This setup aims to prevent unauthorized access to the AWS bucket, leveraging the capabilities of the AWS CDK.
If you’re already familiar with CDK and just need the deployment script, you can find the repository for this tutorial’s CDK stack here. Feel free to check it out for a quick start to your deployment process.
Prerequisites
To follow along with me in this tutorial, you must have an active AWS account. If you have administrator privileges, then it is fine; otherwise, assign the below permissions to your IAM user.
- AWSCloudFormationFullAccess (for creating CloudFormation stacks)
- AmazonS3FullAccess (for creating S3 buckets and managing assets)
- IAMFullAccess (for creating roles and associating policies)
- AmazonCloudFrontFullAccess (for creating CloudFront distributions and OAIs)
- IAMPassRole (Create Inline Policy)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "*"
}
]
}
- Create an access key and secret key and authorize your system to interact with your AWS account via CLI.
- Make sure you have Node and npm installed, as I will be using TypeScript for this tutorial.
Step 1: Installation
If you have Node.js installed, install the AWS CDK Toolkit (the cdk
command):
npm install -g aws-cdk
Test the installation with the below command:
cdk --version
You also need TypeScript itself (version 3.8 or later). If you don’t already have it, you can install it using npm
.
npm install -g typescript
Step 2: Create a Project
We will be creating a directory for our project and then we will be initializing our CDK app inside it.
mkdir frontendcdk
cd frontendcdk
cdk init app --language typescript
Open your preffered code editor and you will be able to see the below set of files.
Step 3: Understanding file structure
1. lib/frontendcdk-stack.ts
This file acts as the heart of your AWS CDK application. Here, we define our CDK stacks, the primary building blocks of our infrastructure.
We define desired resources in this file, such as Amazon S3 buckets, AWS Lambda functions, Amazon DynamoDB tables, and more.
Most of our design and specification work will occur here.
2. bin/frontendcdk.ts
This file is your CDK application’s entry point. It loads and deploys the stack from the lib/frontendcdk-stack.ts file.
Additionally, it might have code for configuring the deployment environment, setting regions, and specifying stack parameters.
3. package.json
This file serves as a manifest for your npm module and contains details about your project. It carries metadata like your application’s name, version, and dependencies.
Here, you can also define scripts for tasks such as building, testing, and deploying your application. npm generates and maintains the package-lock.json file to ensure deterministic dependency resolution.
4. cdk.json
The AWS CDK Toolkit uses this configuration file to determine your application’s run and deployment methods.
It defines the deployment command for the CDK application, typically “npx ts-node bin/frontendcdk.ts”. You can tailor this configuration to your specific deployment needs.
5. tsconfig.json
This TypeScript configuration file sets the rules for transpiling and compiling your TypeScript code. It outlines compiler options like target, module type, and source map settings.
Setting TypeScript options here ensures consistent and correct code compilation throughout your project.
6. .gitignore and .npmignore
These files instruct version control systems (like Git) and the npm package manager on which files and directories to exclude when tracking changes or publishing the package.
Excluding unnecessary files from your repository or package distribution is vital.
7. node_modules
npm creates and oversees this directory. It houses all your project’s dependencies, including the AWS CDK libraries and any third-party libraries.
npm takes care of the installation and management of dependencies based on your package.json file.
Step 4: Creating S3 Bucket
▪ Navigate to lib/frontendcdk-stack.ts file
▪ Import the required libraries
import * as cdk from 'aws-cdk-lib'; // For core constructs
import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; // For creating cloudfront distribution
import * as cloudfrontOrigins from "aws-cdk-lib/aws-cloudfront-origins"; // For setting up cloudfront origin
import * as s3 from "aws-cdk-lib/aws-s3"; // For creating S3 bucket
import { Construct } from 'constructs'; // For defining resources in a stack
▪ Remove the existing comments under constructor and let’s start creating S3 bucket.
// Create a S3 bucket
const bucket = new s3.Bucket(this, "myApp", {
bucketName: 'myappbucketcdkstack',
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
publicReadAccess: false,
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true
});
- bucketName: Specify unique name for your bucket
- blockPublicAccess: For security, we don’t want to make our bucket public and will be keeping private.
- publicReadAccess: We don’t want to provide public read access for our bucket.
- removalPolicy (Optional): When we destroy our cdk stack we want to also delete the S3 bucket during this tutorial. By default, when you perform CDK destroy, AWS does not deletes the created S3 bucket.
- autoDeleteObjects (Optional): This field is optional and is used along with removalPolicy. By default, if bucket contains objects then it won’t get deleted by your removalPolicy and will give you error. In order to delete your bucket along with all your objects autoDeleteObjects flag is used. Be cautious while using this ⚠.
Step 5: Creating Cloudfront Distribution
Let’s now start creating cloudfront distribution for our s3 bucket hosted web application.
const distribution = new cloudfront.Distribution(this, "MyAppDistribution", {
defaultBehavior: {
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
compress: true,
origin: new cloudfrontOrigins.S3Origin(bucket),
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
},
defaultRootObject: "index.html",
errorResponses: [
{
httpStatus: 400,
responseHttpStatus: 200,
responsePagePath: "/index.html",
ttl: cdk.Duration.seconds(10),
},
{
httpStatus: 403,
responseHttpStatus: 200,
responsePagePath: "/index.html",
ttl: cdk.Duration.seconds(10),
},
{
httpStatus: 404,
responseHttpStatus: 200,
responsePagePath: "/index.html",
ttl: cdk.Duration.seconds(10),
},
],
minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2019,
});
- defaultBehaviour: The default behavior determines how CloudFront handles requests and responses by default.
- allowedMethods: HTTP methods to allow for this behavior.
- compress: Whether you want CloudFront to automatically compress certain files for this cache behavior.
- origin: The origin that you want CloudFront to route requests to when they match this behavior.
- viewerProtocolPolicy: The protocol that viewers can use to access the files controlled by this behavior.
2. defaultRootObject: The object that you want CloudFront to request from your origin (for example, index.html) when a viewer requests the root URL for your distribution. If no default object is set, the request goes to the origin’s root (e.g., example.com/).
3. errorResponses: How CloudFront should handle requests that are not successful (e.g., PageNotFound).
- httpStatus: The HTTP status code for which you want to specify a custom error page and/or a caching duration.
- responseHttpStatus: The HTTP status code that you want CloudFront to return to the viewer along with the custom error page.
- responsePagePath: The path to the custom error page that you want CloudFront to return to a viewer when your origin returns the
httpStatus
, for example, /4xx-errors/403-forbidden.html - ttl: The minimum amount of time, in seconds, that you want CloudFront to cache the HTTP status code specified in ErrorCode.
4. minimumProtocolVersion: The minimum version of the SSL protocol that you want CloudFront to use for HTTPS connections. CloudFront serves your objects only to browsers or devices that support at least the SSL version that you specify.
Now, everything is configured but let’s also create a variable to print the cloudfront distribution url once it get’s created.
Define a variable for cloudfront distribution URL above your constructor declaration.
private cfnOutCloudFrontUrl: cdk.CfnOutput;
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
And now to print domain name of our created cloudfront distributionadd the below code after your cloudfront distribution
this.cfnOutCloudFrontUrl = new cdk.CfnOutput(this, "CfnOutCloudFrontUrl", {
value: `https://${distribution.distributionDomainName}`,
description: "The CloudFront URL",
});
Step 6: Deploying our CDK stack
1. Build the application
Generally, you should be in the project’s root directory when building and running your application.
Node.js cannot run TypeScript directly; instead, your application is converted to JavaScript using the TypeScript compiler, tsc
. The resulting JavaScript code is then executed.
The AWS CDK automatically does this whenever it needs to run your app. However, it can be useful to compile manually to check for errors and to run tests. To compile your TypeScript app manually, issue below command.
npm run build
2. Bootstrap the environment
Bootstrapping is the process of provisioning resources for the AWS CDK before you can deploy AWS CDK apps into an AWS environment. (An AWS environment is a combination of an AWS account and Region).
These resources include an Amazon S3 bucket for storing files and IAM roles that grant permissions needed to perform deployments.
The required resources are defined in an AWS CloudFormation stack, called the bootstrap stack, which is usually named CDKToolkit
. Like any AWS CloudFormation stack, it appears in the AWS CloudFormation console once it has been deployed.
cdk bootstrap
The output will appear something like thisThe output will appear something like this.
If you are facing issues during this, then check that there are no existing CDKtoolkit clodformation stacks existing in your account and no such related CDK S3 buckets.
3. Synthesize an AWS Cloudformation template
Synthesizes a AWS CloudFormation template from one or more of the stacks in your AWS CDK app.
The cdk synth
command executes your app, which causes the resources defined in it to be translated into an AWS CloudFormation template.
The displayed output of cdk synth
is a YAML-format template.
The template is also saved in the cdk.out
directory in JSON format.
cdk synth
The cdk synth
generates a perfectly valid AWS CloudFormation template. You could take it and deploy it using the AWS CloudFormation console or another tool. But the AWS CDK Toolkit can also do that.
4. Deploying the stack
To deploy the stack using AWS CloudFormation, issue below command
cdk deploy
cdk deploy
displays progress information as your stack is deployed. When it’s done, the command prompt reappears. You can go to the AWS CloudFormation console and see that it now lists your created CDK stack.
After successful deployment, output will look like this and it will print the cloudfront distribution url to deploy your application.
Hurray 🎉 Our S3 bucket and cloudfront distribution is configured.
Now, we just need to upload our web application code manually via AWS console.
I know you might be wondering why the title mentions ‘Effortless deployment’ when we are supposed to perform a manual deployment process. 😠
No worries, I have a solution for that too. 😅
Step 7: Deploy your frontend application code
We can deploy our frontend application directly using CDK instead of uploading files manually using the console.
Create a directory named web-build at the root of your project and paste the frontend build that you want to deploy inside it.
You can download the build from here.
Import the following libraries at the top of lib/frontendcdk-stack.ts file
import * as s3Deploy from "aws-cdk-lib/aws-s3-deployment"; // To deploy objects to an S3 bucket
import * as path from "path"; // For working with files and directory paths
Add the following code to deploy your application directly from the web-build directory.
new s3Deploy.BucketDeployment(this, 'myAppDeployment', {
sources: [
s3Deploy.Source.asset(path.join(__dirname, '../web-build')),
],
destinationBucket: bucket
});
Now let’s synthesize the CloudFormation template from our CDK stack and deploy it.
cdk synth
cdk deploy
After a successful deployment, you can now access our frontend app by visiting the CloudFront distribution URL.
Step 7: Destroy our CDK Stack
If you have followed along with me, make sure to destroy our CDK stack; otherwise, AWS will be charging you for your resources.
Run the below command to destroy our CDK stack.
cdk destroy
Conclusion
I hope you now understood on how to deploy our frontend web applications directly on S3 and Cloudfront easily with the help of AWS Cloud development kit.
If you found this post helpful, give it a 👏 and follow for more useful blogs. Thanks, and have a great day!