TechAnek

Building a Secure Cloud Architecture with AWS CDK and TypeScript​

What is AWS CDK?

To put it briefly, the AWS Cloud Development Kit (AWS CDK) is an open-source software development framework for defining cloud infrastructure in code (IaaC) and provisioning it through AWS CloudFormation. 

The key difference is that you can write Infrastructure as code in your preferred language, such as TypeScript, Python, JavaScript, Java, or any supported language, rather than scripting CloudFormation in JSON or YAML.

The AWS CDK consists of two primary components:

  • AWS CDK Construct Library – A collection of pre-written modular and reusable pieces of code, called constructs, that you can use, modify, and integrate to develop your infrastructure quickly. The goal of the AWS CDK Construct Library is to reduce the complexity required to define and integrate AWS services together when building applications on AWS.
  •  AWS CDK Toolkit – A command line tool for interacting with CDK apps. Use the AWS CDK Toolkit to create, manage, and deploy your AWS CDK projects.
    The AWS CDK supports TypeScript, JavaScript, Python, Java, C#/.Net, and Go. You can use any of these supported programming languages to define reusable cloud components known as constructs. You compose these together into stacks and apps. Then, you deploy your CDK applications to AWS CloudFormation to provision or update your resources.

Define your cloud infrastructure using general-purpose programming languages:

  • TypeScript, JavaScript, Python, Java, C#, Go

Other, JVM and .NET CLR languages may also be used in theory, but we do not offer official support at this time.

Note:

This article does not currently include instructions or code examples for Go aside from working with the AWS CDK in Go.

Select your preferred language and use programming elements like parameters, conditionals, loops, composition, and inheritance to define the desired outcome of your infrastructure.

Use the same programming language to define your infrastructure and your application logic. Receive the benefits of developing infrastructure in your preferred IDE (Integrated Development Environment), such as syntax highlighting and intelligent code completion.

Deploy infrastructure through AWS CloudFormation

AWS CDK integrates with AWS CloudFormation to deploy and provision your infrastructure on AWS. AWS CloudFormation is a managed AWS service that offers extensive support of resource and property configurations for provisioning services on AWS. With AWS CloudFormation, you can perform infrastructure deployments predictably and repeatedly, with rollback on error. If you are already familiar with AWS CloudFormation, you don’t have to learn a new IaC management service when getting started with the AWS CDK.

Get started developing your application quickly with constructs

Develop faster by using and sharing reusable components called constructs. Use low-level constructs to define individual AWS CloudFormation resources and their properties. Use high-level constructs to quickly define larger components of your application, with sensible, secure defaults for your AWS resources, defining more infrastructure with less code. Create your own constructs that are customized for your unique use cases and share them across your organization or even with the public.

Creating the first app using AWS CDK

Overview:

In this project, we’ll build a secure cloud architecture using AWS CDK and TypeScript. The architecture includes:

  1. A VPC with three public subnets and three private subnets.
  2. Two EC2 instances (one in a public subnet and other one in a private subnet) with dedicated security groups.
  3. Separate route tables for each subnets.
  4. A NAT Gateway for internet access for EC2 remain in a private subnet.
  5. An Internet Gateway for the public subnets.
  6. An Auto Scaling Group (ASG) with a scaling policy based on CPU utilization.

Prerequisites:

  • AWS Account : Ensure you have an active AWS account.
  • AWS CLI : Install and configure the AWS CLI with your credentials.
  • Node.js : Install Node.js.
  • AWS CDK : Install the AWS CDK.

Step 1: Create your CDK project

In this step, you create a new CDK project. A CDK project should be in it’s own directory, with it’s own local module dependencies.

To create a CDK project:

1. From a starting directory of your choice, create and navigate to a directory named my-cdk-project:

				
					$ mkdir my-cdk-project && cd my-cdk-project
				
			

Important:

Ensure the name of your project directory is my-cdk-project, exactly as shown here. The CDK CLI uses this directory name to name resources within your CDK code. If the different directory name is used, you may face an issues while following the instructions.

2. From the my-cdk-project directory, initialize a new CDK project using the AWS CDK CLI “cdk init” command. Specify the app template and your preferred programming language with the “–language” option: TypeScript.

				
					$ cdk init app --language typescript
				
			

The cdk init command creates a structure of files and folders within the my-cdk-project directory to help organize the source code for your CDK app. Take a moment to explore the CDK project.

Step 2: Build your CDK app​

In most programming environments, you build or compile code after making changes. This isn’t necessary with the AWS CDK since the CDK CLI will automatically perform this step. However, you can still build manually when you want to catch syntax and type errors. The following is an example:

				
					$ npm run build
				
			

Step 3: (Optional) Bootstrap your environment​

For this tutorial, the app will be deployed into your default AWS environment. Assuming the current environment is configured and bootstrapped during the getting started process. If not and need to deploy this application into another environment, user must specify the environment in CDK code and bootstrap the environment. For instructions, view the following:

How to specify environments with the AWS CDK.

How to bootstrap your environment.

				
					$ cdk bootstrap
				
			

Step 4: List the CDK stacks in your app​

At this point, now you should have a CDK app containing a single CDK stack. To verify, use the CDK CLI list command to display all the stacks. The output should display a stack named HelloCdkProjectStack.

				
					$ cdk list
				
			

If you don’t see this output, verify that you are in the correct working directory of your project and try again. If the stack still doesn’t appear on screen, repeat the  Step 1 and try again.

Step 5: Define Your Infrastructure

Edit the files in the lib directory to define your AWS resources. By default, lib/my-cdk-project-stack.ts" is created for you. Add resources to this file using CDK constructs.

AWS Infrastructure
				
					import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Vpc, SubnetType, Instance, InstanceType, InstanceClass, InstanceSize, AmazonLinuxImage, SecurityGroup, Peer, Port} 
from 'aws-cdk-lib/aws-ec2';

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

    // VPC with 3 public and 3 private subnets
    const vpc = new Vpc(this, 'MyVpc', {
      natGateways: 1,
      availabilityZones: [ 'us-east-1a','us-east-1b', 'us-east-1c'],
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'PublicSubnet',
          subnetType: SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: 'PrivateSubnet',
          subnetType: SubnetType.PRIVATE_WITH_EGRESS,
        },
      ],
    });

    // Security Group for Public Instance
    const publicSecurityGroup = new SecurityGroup(this, 'PublicSG', {
      vpc,
      allowAllOutbound: true,
    });
    publicSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(22), 'Allow SSH access');
    
    // Security Group for Private Instance
    const privateSecurityGroup = new SecurityGroup(this, 'PrivateSG', {
      vpc,
      allowAllOutbound: true,
    });
    privateSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(22), 'Allow SSH access');
    privateSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(80), 'Allow HTTP traffic');
    privateSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443), 'Allow HTTP traffic');

    // Public Instance
    new Instance(this, 'PublicInstance', {
      vpc,
      instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.MICRO),
      machineImage: new AmazonLinuxImage(),
      securityGroup: publicSecurityGroup,
      vpcSubnets: { subnetType: SubnetType.PUBLIC },
    });

    // Private Instance
    new Instance(this, 'PrivateInstance', {
      vpc,
      instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.MICRO),
      machineImage: new AmazonLinuxImage(),
      securityGroup: privateSecurityGroup,
      vpcSubnets: { subnetType: SubnetType.PRIVATE_WITH_EGRESS },
    });
  }
}

const app = new cdk.App();
new MyCdkProjectStack(app, 'MyCdkProjectStack');
app.synth();
				
			

Step 6: Deploy your CDK stack

In this step, we’ll use the command to deploy the CDK stack. This command retrieves the generated CloudFormation template and deploys it through AWS CloudFormation service, which provisions your resources as part of a CloudFormation stack.

From the root of your project, run the following. Confirm changes if prompted:

				
					$ cdk deploy
				
			

Step 7: Synthesize a CloudFormation template

In this step, prepare for deployment by synthesizing a CloudFormation template with the CDK command. The command will performs basic validation of your CDK code, runs CDK app, and generates a CloudFormation template from CDK stack.

If your app contains more than one stack, you must specify which stacks to synthesize. Since the app contains a single stack, the CDK CLI will automatically detects the stack to synthesize.

If you don’t synthesize a template, the CDK CLI will automatically perform this step when you deploy. However, it’s recommend that you perform step before each deployment to check for synthesis errors.

Before synthesizing a template, user can optionally build your application to catch syntax and type errors. For instructions, see Step 2: Build your CDK app.

To synthesize a CloudFormation template, run the following from the root of the project:

				
					$ cdk synth
				
			

If successful, the CDK CLI will output a YAML–formatted CloudFormation template to stdout and save a JSON–formatted template in the cdk.out directory of your project.

By defining a single L2 construct, the AWS CDK creates an extensive CloudFormation template containing your Lambda resources, along with the permissions and glue logic required for your resources to interact within your application.

Step 8: Removing the application

In this process, destroy command is used to delete the application deployed on cloud. This command removes the CloudFormation stack associated with your CDK stack, which includes the resources created by CDK.

To delete the application, run the cdk destroy command and confirm your request to delete the application. The following is an example:

				
					$ cdk destroy
				
			

Additionally, you may create more extensive cloud resources, autoscaling groups, Dynamo DB, RDS, Serverless Lambda Functions, SQS, SNS and more with the help of CDK. Here, we'll learn more about autoscaling groups and how to use CDK to make them happen.

Setting Up an Auto-Scaling Group with Scaling Policies Using AWS CDK and TypeScript:

Auto-scaling group is a powerful feature in AWS that allows your applications to handle varying loads by dynamically adjusting the number of instances running. In this article, we will walk through setting up an Auto-Scaling Group (ASG) with scaling policies using the AWS CDK and TypeScript. This setup will monitor the CPU utilization of ec2 and scale in or out based on the defined thresholds.

Step Scaling

This type of scaling scales in and out in deterministic steps that you configure, in response to metric values.

After deployment, your Auto Scaling Group will automatically adjust the number of instances based on the CPU utilization. You can monitor the scaling activities in the AWS Management Console under the EC2 Auto Scaling section.

Define Your Infrastructure

Edit the “lib/my-cdk-project-stack.ts" file to define the VPC, Auto Scaling Group, and scaling policies.

Do perform all the above steps. (Step 1 to Step 4)

				
					import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { AutoScalingGroup, CfnLaunchConfiguration } from 'aws-cdk-lib/aws-autoscaling';
import { Metric } from 'aws-cdk-lib/aws-cloudwatch';
import { Role, ServicePrincipal, ManagedPolicy } from 'aws-cdk-lib/aws-iam';

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

    // Auto Scaling Group IAM Police
    const asgRole = new Role(this, 'AsgRole', {
      assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
      managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ReadOnlyAccess')],
    });

    // Create a VPC
    const vpc = new Vpc(this, 'MyVpc', {
      maxAzs: 3
    });

    // Auto Scaling Group
    const asg = new AutoScalingGroup(this, 'MyAutoScalingGroup', {
      vpc,
      instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.MICRO),
      machineImage: new AmazonLinuxImage(),
      minCapacity: 1,
      maxCapacity: 5,
      desiredCapacity: 2,
      role: asgRole,
      vpcSubnets: { subnetType: SubnetType.PUBLIC },
    });

    // Scaling Policy Based on CPU Utilization
    asg.scaleOnMetric('ScaleOnCPU', {
      metric: new Metric({
        namespace: 'AWS/EC2',
        metricName: 'CPUUtilization',     //metric which is used for scaling
      }),
      scalingSteps: [
        { upper: 20, change: -1 },
        { lower: 60, change: +1 },
        { lower: 80, change: +2 },
      ],
      adjustmentType: cdk.aws_autoscaling.AdjustmentType.CHANGE_IN_CAPACITY,
    });
  }
}

const app = new cdk.App();
new MyCdkProjectStack(app, 'MyCdkProjectStack');
app.synth();
				
			

Conclusion:

This instructions shows how to build a secure cloud architecture using AWS CDK and TypeScript. The setup provides resource isolation, secure access, and scalability based on demand. We also explored how to configure an Auto-Scaling Group with scaling policies using AWS CDK and TypeScript. By monitoring CPU utilization, your application can handle varying loads efficiently, ensuring optimal performance and cost management while following best practices for AWS infrastructure design.