DEV Community

Cover image for Get rid of old habits- Use CloudFormation for infrastructure set up -01
Gayan Fonseka for AWS Community Builders

Posted on • Edited on

2 2

Get rid of old habits- Use CloudFormation for infrastructure set up -01

Yes, I too had the habit of creating the required infrastructure manually. Yes, we gave up the idea of changing databases manually a long time ago for good reasons and switched to migrations. With so many resources, policies, roles getting added and having to replicate them in three to four environments, why still stuck with the same old habits when it is as easy as writing a DB migration script. In case you haven't tried yet, I want to share a common scenario, where I'll guide you through setting up the infrastructure to host a service-based application running in multiple availability zones in secure subnets with auto-scaling enabled to provide high availability and scalability. In case you are wondering why I am writing this, I saw some really bad practices recently, and I think they should change and I am doing my bit to help anyone in need.

Sample Setup

I'll base my experience to write about an ideal setup. If you want to host a business application that you've written, most probably it will consist of two different sections namely applications for internal staff to run the business and client-facing applications / APIs which enable customer interaction.
Let's assume that the application for the internal staff is developed using a framework such as asp.net zero, serenity.io, or Laravel. Every bounded context can be hosted separately in the InternalApp Subnet with a public application load balancer handling the traffic.
The APIs that will be consumed by the web applications and the mobile applications better be secured using Cognito user pools and identity pools. When security for the APIs are provided using Cognito at API Gateway level it is not possible to use a public application load balancer, instead, it will have to be a private application load balancer. When the load balancer is private it will have to be connected to the API Gateway using a VPC link, so that the APIs you developed can be exposed in a secure manner. The deployment architecture diagram is as follows.

Deployment Diagram

Our goal is to use CloudFormation templates to automate the deployment of the above infrastructure. I am not hoping to explain what CloudFormation templates are and their basics, hence providing the link to Amazon documentation.Link

The first step would be to create the VPC and it can be done with the following code. You may decide how many private IPs you need, here I am going for the maximum with a CIDR block of 10.0.0.0/16.

Resources:
#VPC creation
AbcVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}

The next step would be the creation of subnets in the two availability zones. The expected outcome can be shown diagrammatically as follows.
Deployment Diagram 01

#Subnet Creation
#AZ 01 subnets
InternalAppSub01:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [0, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: InternalAppSubnet01
ExternalAppSub01:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.3.0/24
AvailabilityZone: !Select [0, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: ExternalAppSubnet01
DataSubnet01:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.5.0/24
AvailabilityZone: !Select [0, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: DataSubnet01
PublicSub01:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.11.0/24
MapPublicIpOnLaunch: True
AvailabilityZone: !Select [0, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: PublicSub01
#AZ 02 subnets
InternalAppSub02:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [1, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: InternalAppSubnet02
ExternalAppSub02:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.4.0/24
AvailabilityZone: !Select [1, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: ExternalAppSubnet02
DataSubnet02:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.6.0/24
AvailabilityZone: !Select [1, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: DataSubnet02
PublicSub02:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.12.0/24
MapPublicIpOnLaunch: True
AvailabilityZone: !Select [1, Fn::GetAZs: !Ref 'AWS::Region']
VpcId: !Ref AbcVpc
Tags:
- Key: Name
Value: PublicSub02

As the next step, we can create other resources such as internet gateway, nat gateway and etc. We could also do the route table configuration. Once the route table configuration is done we may create the associations between subnets and route tables. The code for the above tasks is as follows,

#Internet Gateway configuration and attachment to VPC
AbcIgw:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-IGW
VpcIgwAttchment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref AbcVpc
InternetGatewayId: !Ref AbcIgw
#Nat GateWay configuration with EIP
AbcNatGw:
Type: AWS::EC2::NatGateway
DependsOn: EIP
Properties:
AllocationId: !GetAtt [EIP, AllocationId]
SubnetId: !Ref PublicSub01
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-NATGW
EIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
#Route Table Configurations
PubRouteTable:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: PubRoute
VpcId: !Ref AbcVpc
PriRouteTable:
Type: AWS::EC2::RouteTable
Properties:
Tags:
- Key: Name
Value: PriRoute
VpcId: !Ref AbcVpc
#Subnet to Route Table associations
PubSub01RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PubRouteTable
SubnetId: !Ref PublicSub01
PubSub02RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PubRouteTable
SubnetId: !Ref PublicSub02
InternalApp01RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref InternalAppSub01
InternalApp02RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref InternalAppSub02
ExternalAppSub01RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref ExternalAppSub01
ExternalAppSub02RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref ExternalAppSub02
DataSub01RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref DataSubnet01
DataSub02RouteAss:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref DataSubnet02
#Routes Configuration
IGRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref AbcIgw
RouteTableId: !Ref PubRouteTable
NatGWRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref AbcNatGw
RouteTableId: !Ref PriRouteTable

In the future stacks, we'll be needing the resources we created above and for that purpose, we'll have to output these resources with names that could be referenced later.

#Output resources
Outputs:
#VPC
StackVpc:
Description: Deatils about vpc
Value: !Ref AbcVpc
Export:
Name: AbVPC
Internalapp01sub:
Description: Output of the private internal subnet
Value: !Ref InternalAppSub01
Export:
Name: InternalAppSub01
Externalapp01sub:
Description: Output of the private External subnet
Value: !Ref ExternalAppSub01
Export:
Name: ExternalAppSub01
DBSub01:
Description: Output of the private Prod DB subnet
Value: !Ref DataSubnet01
Export:
Name: DataSubnet01
Internalapp02sub:
Description: Output of the private internal subnet
Value: !Ref InternalAppSub02
Export:
Name: InternalAppSub02
Externalapp02sub:
Description: Output of the private External subnet
Value: !Ref ExternalAppSub02
Export:
Name: ExternalAppSub02
DBSub02:
Description: Output of the private Prod DB subnet
Value: !Ref DataSubnet02
Export:
Name: DataSubnet02
#Public Subnets in Availability Zone A.
Public01:
Description: Output of the Public subnet AZ 01
Value: !Ref PublicSub01
Export:
Name: PublicSubnet01
Public02:
Description: Output of the Public subnet AZ 02
Value: !Ref PublicSub02
Export:
Name: PublicSubnet02

We are done creating the resources but there is some more to be done such as security groups configuration, adding IAM roles, adding compute resources and etc. Due to the length of the article, I'll keep them for another day but you can expect them to arrive soon. Thanks for reading.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Create a simple OTP system with AWS Serverless cover image

Create a simple OTP system with AWS Serverless

Implement a One Time Password (OTP) system with AWS Serverless services including Lambda, API Gateway, DynamoDB, Simple Email Service (SES), and Amplify Web Hosting using VueJS for the frontend.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay