Deploy AWS VPC using AWS CloudFormation

Shivam kushwah
6 min readJul 12, 2023

--

Welcome to our comprehensive guide on deploying AWS Virtual Private Cloud (VPC) using AWS CloudFormation. In this blog, we will dive deep into the world of VPCs and explore how CloudFormation can streamline the deployment process, making it easier and more efficient for you. Whether you’re a beginner or an experienced AWS user, this guide will provide you with valuable insights and practical tips to master VPC deployment with CloudFormation.

CloudFormation makes it easy to automate the deployment of resources in the AWS Cloud. Instead of manually launching services through the console, CloudFormation allows you to define your infrastructure as code using JSON or YAML templates. This approach offers several benefits, including faster deployment, repeatability, and reduced risk of misconfiguration.

In this blog, we will guide you through the process of deploying a VPC and associated resources using CloudFormation. We will use YAML as the language of choice for our templates, but you can also use JSON if you prefer. The complete code for this tutorial can be found on our GitHub repository.

Let’s begin by creating the following resources:

  1. VPC: A Virtual Private Cloud with a CIDR block range of 10.0.0.0/16.
  2. Internet Gateway: A gateway that connects the VPC to the internet.
  3. Public Subnets: Two subnets that are accessible from the internet.
  4. Private Subnets: Two subnets that are not accessible from the internet.
  5. Route tables: Route tables associated with the subnets.
  6. Security Group: A security group for a web server.
  7. EC2 Instance: An EC2 instance that will serve as our web server.

To create the VPC, add the following YAML code to your CloudFormation template:

Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: VPC Blog Post

In this section, we define the VPC resource using the AWS::EC2::VPC type. We specify the CIDR block range as 10.0.0.0/16 and enable DNS support and hostnames. Additionally, we assign the name "VPC Blog Post" to the VPC using tags.

Next, let’s create the Internet Gateway and attach it to the VPC:

  InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: VPC Blog Post

InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC

Here, we define the Internet Gateway resource and associate the name “VPC Blog Post” with it using tags. The InternetGatewayAttachment resource attaches the Internet Gateway to the VPC. The !Ref syntax refers to the resources created earlier, allowing us to specify dependencies.

In the next sections of the blog, you will continue building the remaining resources, including subnets, route tables, security groups, and the EC2 instance. you will also provide detailed explanations for each step along the way.

Now that we have created the VPC, internet gateway, and route tables, it’s time to add the subnets to our infrastructure. We’ll create two public subnets (PublicSubnet1 and PublicSubnet2) and two private subnets (PrivateSubnet1 and PrivateSubnet2). Here's the code to add the subnets:

PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: VPC Blog Post Public Subnet 1

PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: 10.0.2.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: VPC Blog Post Public Subnet 2

PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs '']
CidrBlock: 10.0.11.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: VPC Blog Post Private Subnet 1

PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs '']
CidrBlock: 10.0.12.0/24
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: VPC Blog Post Private Subnet 2

In the code above, we define each subnet using the AWS::EC2::Subnet resource type. We set the VpcId property to reference the VPC we created (!Ref VPC). The AvailabilityZone property is set to select the respective availability zone (!Select [0, !GetAZs ''] or !Select [1, !GetAZs '']) based on the index value. The CidrBlock property defines the range of IP addresses for each subnet. The MapPublicIpOnLaunch property is set to true for the public subnets and false for the private subnets. Finally, we assign names to the subnets using tags.

With the addition of the subnets, we have now created the foundational components of our VPC infrastructure. In the upcoming sections of the blog, we will cover the creation of a security group, launching an EC2 instance, and displaying the output of the web server’s public IP address.

Now that we have the VPC, internet gateway, and subnets in place, we can proceed to create a route table for the public subnets and associate them accordingly. Let’s go through the code you provided:

PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: VPC Blog Public Routes

DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway

PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1

PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2

Now that we have the VPC, internet gateway, and subnets in place, we can proceed to create a route table for the public subnets and associate them accordingly. Let’s go through the code you provided:

yamlCopy code
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: VPC Blog Public Routes
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2

In the code above, we create the PublicRouteTable resource using the AWS::EC2::RouteTable type. We specify the VPC to associate the route table with (VpcId: !Ref VPC). Additionally, we assign a name to the route table using tags.

Next, we define the DefaultPublicRoute which represents the route to the internet gateway. This route allows all traffic (0.0.0.0/0) to be routed through the internet gateway. The DependsOn attribute ensures that the internet gateway attachment is created before adding the route.

Following that, we create the PublicSubnet1RouteTableAssociation and PublicSubnet2RouteTableAssociation resources. These associations link the public subnets (PublicSubnet1 and PublicSubnet2) with the PublicRouteTable. This ensures that the subnets use the specified route table for routing.

Now let’s proceed with creating the security group for our web server and launching an EC2 instance with the desired configuration. Here’s the code to accomplish this:

WebServerSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupDescription: Web Server Traffic
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
VpcId: !Ref VPC

EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0e1d30f2c40c4c701
InstanceType: t2.micro
SubnetId: !Ref PublicSubnet1
SecurityGroupIds:
- !Ref WebServerSecurityGroup
Tags:
- Key: Name
Value: VPC Blog Post EC2
UserData:
Fn::Base64: |
#!/bin/bash
yum install httpd -y
service httpd start
echo "<html><body><h1>Hello from DCT!<h1></body></html>" > /var/www/html/index.html

In the code above, we create the WebServerSecurityGroup resource using the AWS::EC2::SecurityGroup type. We provide a description for the security group and define an inbound rule that allows TCP traffic over port 80 (HTTP) from any source (0.0.0.0/0). The security group is associated with the VPC (VpcId: !Ref VPC).

Next, we define the EC2Instance resource using the AWS::EC2::Instance type. We specify the Amazon Linux 2 AMI (ami-0e1d30f2c40c4c701) and choose a t2.micro instance type. The instance will be launched in PublicSubnet1. The security group for the instance is set to WebServerSecurityGroup. We also assign a name to the instance using tags.

The UserData section allows us to run custom scripts when the instance is launched. In this case, we use yum to install the Apache web server, start the service, and create a basic HTML page with the "Hello from DCT!" message.

To complete the configuration, you can add the necessary outputs to retrieve the public IP address of the web server. For example:

Outputs:
WebServerPublicIP:
Value: !GetAtt EC2Instance.PublicIp
Description: Public IP address of the web server

With this output, you can easily grab the IP address of the web server and access the web page.

You now have a fully functional AWS VPC infrastructure with a web server running!

--

--