VPC with public and private subnet (NAT) on AWS using Terraform

kuldeep rajpurohit
9 min readJul 28, 2020
Architecture Diagram

What is VPC ?

Amazon Virtual Private Cloud (Amazon VPC) lets you provision a logically isolated section of the AWS Cloud where you can launch AWS resources in a virtual network that you define. … You can use both IPv4 and IPv6 in your VPC for secure and easy access to resources and applications.

What is Terraform ?

Terraform is a multi-cloud (IaC) Infrastructure as Code software by HashiCorp written in Go Language using (HCL) HashiCorp Config Language. An open source command line tool that can be used to provide an infrastructure on many different platforms and services such as IBM, AWS, GCP, Azure, OpenStack, VMware and more.

Prerequisites:

  • Basic knowledge of aws is required
  • Basic knowledge of terraform is required
  • Basic knowledge of Docker is required
  • AWS CLI (Install AWS CLI)
  • Terraform (Install Terraform)

In this article i will show you how to create a virtual private cloud (VPC) with a public subnet and a private subnet using Terraform. The most common usecase for this setup would be,if you want to run a public-facing web application, while maintaining back-end servers that aren’t publicly accessible. A common example is a multi-tier website, with the web servers in a public subnet and the database servers in a private subnet. You can set up security and routing so that the web servers can communicate with the database servers.

The instances in the public subnet can send outbound traffic directly to the Internet, whereas the instances in the private subnet can’t. Instead, the instances in the private subnet can access the Internet by using a network address translation (NAT) gateway that resides in the public subnet. The database servers can connect to the Internet for software updates using the NAT gateway, but the Internet cannot establish connections to the database servers.

Here we will use wordpress as public facing web application and mysql as an backend database server.

Let get Started….

aws configure

First we have to create a new profile using aws configure command we have to pass our access key and secret key to make profile,this profile will be used in terrform provider for authentication.

$ aws configure --profile yourProfilename

Provider and profile

Here we are using terraform aws provider ,so terraform can contact aws api for infrastructure provisioning,we are also passing profile name which we created in previous step using aws configure cli command and we are setting default region as ap-south-1

Variables

Now we are creating a variable for key name ,we can keep default blank if we want to pass key name from shell after terraform apply,we are also creating one more variable with name base_path this is nothing but the path to our project root.

Private Key and Key pair

Here we are creating a private key with RSA algorithm with bit length 4096 ,with this private key and public key we will create a key_pair in aws using aws_key_pair resource , this key will be used to ssh into instances,and at the end we will save this private key in our local machine at specified path using local_file resource.

VPC

Now we are creating a VPC with cidr block as 192.168.0.0/16 and instance tenancy as default,we are also enabling dns host name,so resources launched in this vpc will have a dns host name.

Subnets

Now we will create 2 subnets inside vpc that we created before,the 1st subnet will be a public-subnet with cidr block 192.168.0.0/24 in region ap-south-1 and AZ ap-south-1a,here we are also enabling map_public_ip_on_launch so when we launch instance in this subnet instances will have a public ip.

2nd subnet will be a private subnet with cidr block 192.168.0.0/24 in region ap-south-1 and AZ ap-south-1b,here we are not enabling map_public_ip_on_launch because this is a private subnet ,since internet connection to instance launched inside this subnet can’t be established their is no meaning to provide a public ip to instances.

Internet Gateway

An internet gateway is a horizontally scaled, redundant, and highly available VPC component that allows communication between your VPC and the internet.

An internet gateway serves two purposes: to provide a target in your VPC route tables for internet-routable traffic, and to perform network address translation (NAT) for instances that have been assigned public IPv4 addresses.

We created Internet Gatway but we also have to create a route table with target as internet gatway and associate it to public subnet so instances inside public subnet have internet connectivity.

IG Route Table and Public-subnet Association

Here we are creating a route table with cidr “0.0.0.0/0” means on any ip,with Internet gateway as traget.

And then associating route table to public subnet.

EIP & NAT Gateway

NAT Gateway is a highly available AWS managed service that makes it easy to connect to the Internet from instances within a private subnet in an Amazon Virtual Private Cloud (Amazon VPC).

NAT Gateway Only Provide SNAT so private subnet instance can connect to Internet,and Vice versa connectivity can’t be established.

NAT Gateway also required a EIP (Elastic IP) ,So here we are creating a EIP and then we are creating a NAT gateway.

Note we are creating this NAT gateway in public subnet because,NAT gateway required a Internet connectivity,so Instances launched in private subnet have internet connectivity,And NAT gateway will use Internet Gateway for Internet connectivity,Remember we Associated a Route to Internet Gateway in public subnet.

NAT Route Table and Private-subnet Association

We have to create a route table with target as NAT gateway and Associate it to private subnet So instances in private subnet can connet to internet.

Here we are creating a route with cidr “0.0.0.0/0" means any ip and target as NAT gateway then we associated this table to private subnet.

So request from instance inside private subnet goes to NAT gateway in public subnet and from NAT gateway it goes to Internet Gateway.

Bastion host Instance & Security Group

A bastion host is a server whose purpose is to provide access to a private network from an external network, such as the Internet. Because of its exposure to potential attack, a bastion host must minimize the chances of penetration. For example, you can use a bastion host to mitigate the risk of allowing SSH connections from an external network to the Linux instances launched in a private subnet of your Amazon Virtual Private Cloud (VPC).

We can’t do ssh into instances launched inside private subnet for internet,but subnets with in same vpc has connectivity.

So we will launch a bastion host in public subnet and from this instance we to ssh into our instances in private subnet.

In bastion host security group we are allowing ssh from anywhere in inbound rule,you can assign a custom ip for more security.

For Bastion host instance we are using amzon linux 2 AMI and instance type of t2.micro and we are attaching the key we created earlier.

Then we are copying the same key from local machine to bastion host using terraform file privisioner,so using that key we can SSH from bastion host to instances in private subnet.

I am using same key for this demonstration but you should create diffrent key for every instance for more security.

Wordpress Instance & Security Group

Now we will launch a public facing web application (wordpress) in public subnet so it can be accessed from internet.

In security group we are allowing port 80 for http traffic from any where and port 22 for ssh from bastion host,but rather than using the ip address of bastion host in inboud rule for port 22 we are using bastion host security group,because public ip of bastion host might change,either we have to using EIP so it can be static or we can use security group in inbound rule.

For wordpress Instance we are using Amazon linux 2 ami and from ec2 userdata we are passing a script for configuration for wordpress.

Script in user data by default run with root user so dont required to use sudo,in this script we are installing docker and then pull wordpress image for docker hub and then launching a wordpress container with this image ,we are also passing environment variables while launching wordpress so it automatically get configured for mysql database,also port forwarding on port 80 from docker host to port 80 on docker container has been done.

We are using the same key for this instance also as we used for bastion host.

MySQL Instance & Security Group

Now we will launch a EC2 instance for mysql in private subnet with Amazon linux 2 ami and here also we will pass a script in user data.In wordpress instance we embeded script in terraform filr because their we have to pass terraform attribute for mysql instance private ip in script.

but here we dont required any terraform attribute,so we created a .sh file and passing this file in ec2 user data with file function.

In this script we are installing and enabling docker and then pulling mysql:5.6 docker image from docker hub,then we are launching a container with this image,here also we are passing required envirnoment variable and doing port forwarding on port 3306 from docker host to port 3306 on docker container.

In Security Group we will allow port 3306 for wordpress instance using the security group id of wordpress in inbound rule and allowed port 22 for bastion host using security group id of bastion host.

Only Wordpress instance can connect on port 3306 and only bastion host instance can do SSH into MySQL instance.

Usng security group in inbound rule helps in 2 ways,we dont have to take care of dynamic public ip of instances and if we want to scale our instances we dont have to create diffrent inbound rule for every instances.

Terraform Apply

Now we can create all our infrastructure we just one command…

$ terraform apply

Wordpress

Once all infrastructure is provisioned ,copy public ip of wordpress instance and paste it in browser…

we dont required to confihure wordpress for mysql remember we already pass all environment variable will launching container..

we just required to select language and create account..

And we have successfully created a VPC with Public & Private Subnet using Terraform…and deployed wordpress and mysql using docker…..

That’s it for this Article. I hope you learned something new.

You can find all code related to this article here:-

If You want that Private Subnet Instance do not have internet connectivity for security reasons you can skip creating NAT gateway and NAT route table but their you have to use a preconfigured mysql Ami image because now their is no internet connectivity in private subnet.

You can find code without NAT gateway here:-

If you liked this article, please drop a clap so it can reach to more people.You can follow me on Twitter at @callbyrefrence or find me on LinkedIn or take a look at my work on github

--

--