Create AWS VPC Infrastructure with Cloudformation
Prerequisites
You must have an AWS account and aws-cli installed on your machine. Here you can find instructions to satisfy the prerequisites for this tutorial.
At this point, after the initialization, you should have the following folder structure:
The file template.yaml should contain the following lines:
AWSTemplateFormatVersion: 2010-09-09
Description: STW network template
VPC Resources
Let’s start adding all the AWS resources needed to provide a VPC!!
From now on every resource must be declared inside the resource block:
Resources:
First of all, maybe could look obvious, but the first resource to create is a vpc 😅
#============ VPC =============
STWVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: STW vpc
Now we’ve created the container for all the network resources we are going to provide.
Next, let’s create the internet gateway and internet gateway attachment for permitting the communication with public internet and link it to the vpc already created.
#============ INTERNET GATEWAY =============
STWInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: STW
STWVPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref STWInternetGateway
VpcId: !Ref STWVpc
Time for creating subnets!! 🤩
We will create different subnets spread over 2 different availability zones (if you don’t know what an availability zone is, check this).
#============ SUBNETS =============
STWSubnetPublic1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.101.0/24
MapPublicIpOnLaunch: true
VpcId: !Ref STWVpc
AvailabilityZone: "eu-west-1a"
Tags:
- Key: Name
Value: STW
STWSubnetPublic2:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.102.0/24
MapPublicIpOnLaunch: true
VpcId: !Ref STWVpc
AvailabilityZone: "eu-west-1b"
Tags:
- Key: Name
Value: STW
STWSubnetPrivate1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: false
VpcId: !Ref STWVpc
AvailabilityZone: "eu-west-1a"
Tags:
- Key: Name
Value: STW
STWSubnetPrivate2:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.2.0/24
MapPublicIpOnLaunch: false
VpcId: !Ref STWVpc
AvailabilityZone: "eu-west-1b"
Tags:
- Key: Name
Value: STW
As you can see we have declared 4 resources of type AWS::EC2::Subnet in the region eu-west-1 (Ireland, the same region where has been declared also the vpc) across 2 different AZ (eu-west-1a and eu-west-1b).
Now we have created all the subnets where our computing resources will be placed but, something is missing 🧐. For example, how can we instruct our system to route the traffic based on the requested address? Here come routing tables!
Let’s begin with the public subnets!
We will create a route table and routes with the following rules:
— if a request is directed to the vpc cidr (10.0.0.0/16) forward it inside the vpc (is implicit in the resource definition);
— if a request is directed to any other address (0.0.0.0/0) forward it to the internet gateway in order to permit the request to reach the public internet.
STWRouteTablePublic:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref STWVpc
Tags:
- Key: Name
Value: STW
STWVPCRoutePublic1:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref STWInternetGateway
RouteTableId: !Ref STWRouteTablePublic
Now is the time to assign the created route table to the public subnets with the terraform resource called aws_route_table_association.
STWSubnetRouteTableAssociationPublic1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref STWRouteTablePublic
SubnetId: !Ref STWSubnetPublic1
STWSubnetRouteTableAssociationPublic2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref STWRouteTablePublic
SubnetId: !Ref STWSubnetPublic2
Ok, the public subnets are done! Unfortunately is not possible to do the same thing with the private subnets because the ec2 won’t have a public ip and so they are not able to make public internet request via the internet gateway 😓.
The solution is to provide a nat gateway with a public ip associated 🤯.
The aim of the nat gateway is to forward every request made by any resource inside the private network to the internet using his public ip.
Obviously, the nat gateway must be provisioned inside a public subnet; the result will be that a machine inside a private subnet can communicate with the internet but it will not be available outside of the vpc (this could be useful for databases).
#============ NAT GATEWAY =============
STWNatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt STWEIP.AllocationId
ConnectivityType: public
SubnetId: !GetAtt STWSubnetPublic1.SubnetId
Tags:
- Key: Name
Value: STW
STWEIP:
Type: AWS::EC2::EIP
Properties:
Tags:
- Key: Name
Value: STW
Similarly to the public subnet, we must instruct our resources inside the private subnets to route the requests based on the target:
— if a request is directed to the vpc cidr (10.0.0.0/16) forward it inside the vpc (is implicit in the resource definition);
— if a request is directed to any other address (0.0.0.0/0) forward it to the nat gateway in order to permit the request to reach the public internet via the internet gateway (the public route table will be used).
The route table must be associated with the private subnets.
STWRouteTablePrivate:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref STWVpc
Tags:
- Key: Name
Value: STW
STWVPCRoutePrivate1:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref STWNatGateway
RouteTableId: !Ref STWRouteTablePrivate
STWSubnetRouteTableAssociationPrivate1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref STWRouteTablePrivate
SubnetId: !Ref STWSubnetPrivate1
STWSubnetRouteTableAssociationPrivate2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref STWRouteTablePrivate
SubnetId: !Ref STWSubnetPrivate2
You can find the final version of this template on my GitHub profile at this link: cloudformation-infrastructure-aws-vpc.
Let’s deploy the template!!
In order to deploy the declared resources, you must run the following command for the first upload:
$ aws cloudformation create-stack --template-body file://template.yaml --stack-name stw-vpc
Remember to add the parameter --profile <profile name>
in case your profile name is different from default.
You can follow all the creation events in the cloudformation dashboard inside your AWS console.
if you need to update an already deployed template you must run the following command:
$ aws cloudformation update-stack --template-body file://template.yaml --stack-name stw-vpc
If you want to destroy all resources:
$ aws cloudformation delete-stack --stack-name stw-vpc
Bye 😘