Nick Dalalelis

Cloud Software Engineer

Programming Infrastructure with the Amazon CDK

2020-03-15 Nick DalalelisDevOps

In this blog post we will explore how the AWS CDK can be used to deploy a VPC, security groups and EC2 instances. In Part 1, we created a VPC with a public and private subnet using the AWS Console. In Part 2, we created Security Groups and Network ACLs. Now we will define the infrastructure in code using the AWS CDK.

Below is the infrastructure that we will deploy to AWS. We will not be installing the software for the web server and database server in order to keep this example simple.

VPC-EC2-RDS

The code shown in this blog can be found on my github repository.

Introduction

It’s important to realize that the code that we write using the AWS CDK will be converted into a CloudFormation template. The difference is that we use modern programming languages such as TypeScript, JavaScript, Python, Java, and C#/.Net to define cloud resources.

In order to build and deploy the project, you will need to first install the following components.

  • Node.js
  • Typescript
  • AWS CLI
  • AWS CDK

Directions on how to install the above prerequisites can be found here.

Exploring The Project

aws-cdk-project

After installing the prerequisites, run the following commands to build and deploy the project.

# Install dependencies
npm install

# Build the project
npm run build

# Deploy using CLI
cdk deploy

After successful deployment, we can see the CloudFormation stack in the AWS Console.

cloudformation

Let’s now walk through the code to see how resources are defined using the AWS CDK.

Main Entry Point

The entry point to our project is bin/aws-cdk-vpc-example.ts. This is a very basic file that just instantiates the AwsCdkVpcExampleStack class from ../lib/aws-cdk-vpc-example-stack.

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import * from '../lib/aws-cdk-vpc-example-stack';

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

The Main Stack

The main file that defines our AWS infrastructure is lib\aws-cdk-vpc-example-stack.ts. The stack includes the following:

  • VPC (public & private subnets, NAT Gateway, Internet Gateway)
  • Security Groups (web server & database server security groups)
  • EC2 instances (web server & database instances)

VPC

Below is the code snippet in lib\aws-cdk-vpc-example-stack.ts that defines the VPC. We define a VPC with a 192.168.0.0/16 CIDR block along with a public and private subnet.

const vpc = new ec2.Vpc(this, 'Demo CDK VPC', {
      // 192.168.0.0/16 CIDR block for our VPC
      cidr: '192.168.0.0/16',

      // For this example, we'll only use 1 availability zone
      maxAzs: 1,

      // Set up a Public and a Private subnet in the single availability zone
      subnetConfiguration: [
        {
          name: 'Public Subnet',
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 24
        },
        {
          name: 'Private Subnet',
          subnetType: ec2.SubnetType.PRIVATE,
          cidrMask: 24
        }
      ]
    });

From the AWS console, you can see the the VPC and subnets provisioned by the code above.

vpc

subnet

You may be asking the question about NAT and Internet gateways. According to the documentation, both the NAT and Internet Gateways are created by default.

internet-gateway

nat-gateway

Web Server Security Group

Security groups act as a virtual firewall that control the flow of traffic between instances in your VPC. You can read more about securing the VPC in my previous blog Amazon Virtual Private Cloud (Amazon VPC) Fundamentals (Part 2). Below is the code that creates a Security Group within our VPC. This Security Group allows connections to port 80 from any IPv4 address through an ingress rule.

const webSecurityGroup = new ec2.SecurityGroup(this,
      "Web Server Instances",
      {
        vpc: vpc
      }
);
webSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(),
      ec2.Port.tcp(80),
      'allow web access from the world'
);

web-server-sg

Web Server EC2 Instance

Now that we have our Security Group defined, it’s now time to attach it to our web server EC2 instance.

const webServer = new ec2.Instance(this, "Web Server", {
      vpc: vpc,
      instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO),
      machineImage: ec2.MachineImage.latestAmazonLinux(),
      vpcSubnets: {
        subnets: vpc.publicSubnets
      },
      securityGroup: webSecurityGroup
});

web-server-ec2

Database Security Group

The Database Security Group allows connections to port 5432. Notice that we are allowing incoming connections from the Web Server Security Group.

const rdsSecurityGroup = new ec2.SecurityGroup(this,
      "RDS Instances",
      {
        vpc: vpc
      }
);
rdsSecurityGroup.addIngressRule(webSecurityGroup,
      ec2.Port.tcp(5432),
      'allow postgreSQL access from the web server security group'
);

database-sg

Database EC2 Instance

Just like we did for the web server EC2 instance, we are going to attach the RDS Security Group to the database server EC2 instance.

const databaseServer = new ec2.Instance(this, "Database Server", {
      vpc: vpc,
      instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO),
      machineImage: ec2.MachineImage.latestAmazonLinux(),
      vpcSubnets: {
        subnets: vpc.privateSubnets
      },
      securityGroup: rdsSecurityGroup
});

database-ec2

Summary

The AWS CDK is a great way to deploy AWS resources using modern programming languages. There are other methods to define Infrastructure as Code (IaC) like CloudFormation and Terraform using JSON and YAML. The choice comes down to preference.