Creating Personal VPC Using NAT Gateway

Vikas Goswami
7 min readJul 19, 2020

--

Here I am going to discuss a very great use case to create infrastructure over AWS using Terraform.

For this set I am going to deploy a simple wordpress website with MySQL database.

So let’s talk about infrastructure I am going to setup.

  1. Write an Infrastructure as code using terraform, which automatically create a VPC.
  2. In that VPC we have to create 2 subnets:
    1. public subnet [ Accessible for Public World! ]
    2. private subnet [ Restricted for Public World! ]
  3. Create a public facing internet gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC.
  4. Create a routing table for Internet gateway so that instance can connect to outside world, update and associate it with public subnet.
  5. Create a NAT gateway for connect our VPC/Network to the internet world and attach this gateway to our VPC in the public network
  6. Update the routing table of the private subnet, so that to access the internet it uses the NAT gateway created in the public subnet
  7. Launch an ec2 instance which has WordPress setup already having the security group allowing port 80 so that our client can connect to our wordpress site. Also attach the key to instance for further login into it.
  8. Launch an ec2 instance which has MYSQL setup already with security group allowing port 3306 in private subnet so that our wordpress instance can connect with the same. Also attach the key with the same.

So my setup is like that we have 1 VPC and inside that VPC I have 2 subnets. One subnet is public where I deploy my front-end or user landing page inside VM & second subnet is private where I deploy my database inside VM or ec2 instance.

Pr-Requisite

  • Terraform install in your system.
  • You have account on AWS.
  • aws has been configured.
aws-configure --profile=con

So let’s start with the deployment.

Step1: Firstly I have mention the provider with my credentials.

provider "aws" {
region = "ap-south-1"
profile = "con"
}

Step2: I am creating a ssh key which I am to attach with my instances.

resource "aws_key_pair" "mykey" {
key_name = "mykey"
public_key = "public key or path to my public key on your local
system"
}

Step3: Now I am going to create a VPC name lwtask3.

resource "aws_vpc" "lwtask3" {
cidr_block = "192.168.0.0/16"
instance_tenancy = "default"
enable_dns_hostnames = "true"
tags = {
Name = "lwtask3"
}
}

Step4: Now I create 2 subnets (1 public & 1 private)

resource "aws_subnet" "sub-1a" {
depends_on = [
aws_vpc.lwtask3,
]
vpc_id = "${aws_vpc.lwtask3.id}"
availability_zone = "ap-south-1a"
cidr_block = "192.168.0.0/24"
map_public_ip_on_launch = "true"
tags = {
Name = "sub-1a"
}
}
resource "aws_subnet" "sub-1b" {
depends_on = [
aws_subnet.sub-1a,
]
vpc_id = "${aws_vpc.lwtask3.id}"
availability_zone = "ap-south-1b"
cidr_block = "192.168.1.0/24"
tags = {
Name = "sub-1b"
}
}

Step5: Now I create am Internet Gateway to provide Internet connection to public subnet.

resource "aws_internet_gateway" "myigw" {
depends_on = [
aws_subnet.sub-1b,
]vpc_id = "${aws_vpc.lwtask3.id}"
tags = {
Name = "lwtask3"
}
}

Step6: Now I am going to create a route table and associate with public subnet. This will provide ex-poser to world. Also attach to Internet Gateway.

resource "aws_route_table" "lwtask3" {
depends_on = [
aws_internet_gateway.myigw,
]
vpc_id = "${aws_vpc.lwtask3.id}
"route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.myigw.id}"
}
tags = {
Name = "lwtask3"
}
}
resource "aws_route_table_association" "a" {
depends_on = [
aws_route_table.lwtask3,
]
subnet_id = "${aws_subnet.sub-1a.id}"
route_table_id = aws_route_table.lwtask3.id
}

Step7: Now I will create 3 Security groups as discussed.

resource "aws_security_group" "MySG" {
depends_on = [
aws_route_table_association.a,
]
name = "MySG"
description = "Allow TLS inbound traffic"
vpc_id = "${aws_vpc.lwtask3.id}"
ingress {
description = "TLS from VPC"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "TLS from VPC"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "MySG"
}
}
resource "aws_security_group" "bastion" {
depends_on = [
aws_route_table_association.a,
]
name = "bastion"
description = "Allow TLS inbound traffic"
vpc_id = "${aws_vpc.lwtask3.id}"
ingress {
description = "TLS from VPC"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "TLS from VPC"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["192.168.1.0/24"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "BastionSG"
}
}

resource "aws_security_group" "MySGPri" {
depends_on = [
aws_route_table_association.a,
]
name = "MySGPri"
description = "Allow TLS inbound traffic"
vpc_id = "${aws_vpc.lwtask3.id}"
ingress {
description = "TLS from VPC"
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.MySG.id]
}
ingress {
description = "TLS from VPC"
from_port = 0
to_port = 0
protocol = "-1"
security_groups = [aws_security_group.bastion.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "MySGPriv"
}
}

Step8: Now I am going to launch 3 instances as discussed above. Also I attached my key to private subnet instance also, so that I can provide Internet connection for downloading things from internet but that doesn’t mean mysql instance is publicly accessible.

Step9: Now, I am going to configure the db credentials inside wordpress file wp-config.

resource "null_resource" "nullremote1" {
depends_on = [
aws_instance.mysql,
]connection {
type = "ssh"
user = "ubuntu"
private_key = file("/home/devops/.ssh/id_rsa")
host = aws_instance.wordpress.public_ip
}provisioner "remote-exec" {
inline = [
"sudo sed -i -e 's/localhost/${aws_instance.mysql.private_ip}/g' /var/www/wordpress/wp-config.php",
]
}
}

Step10: Now I create my NAT gateway. Also to NAT gateway I have to assign EIP, for that first I have to create on EIP.

Remember always launch NAT gateway in public subnet & attach it to your private subnet.

resource "aws_eip" "nat" {
vpc = true
}
resource "aws_nat_gateway" "lwtask3" {
depends_on = [
aws_instance.mysql,
]
allocation_id = "${aws_eip.nat.id}"
subnet_id = "${aws_subnet.sub-1a.id}"
tags = {
Name = "lwtask3 NAT"
}
}

I have created a NAT & attached to my private subnet but still my private instance don’t have access to internet. To provide Internet connection, I have to create the route table. I need to tell my subnet where he have to go, if he want to go outside what is the gateway for that. So I need create that rule in our route table

Step11: Now I will create a route table for private subnet & associate with private subnet.

resource "aws_route_table" "lwtask3nat" {
depends_on = [
aws_nat_gateway.lwtask3,
]
vpc_id = "${aws_vpc.lwtask3.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_nat_gateway.lwtask3.id}"
}
tags = {
Name = "lwtask3"
}
}
resource "aws_route_table_association" "nat" {
depends_on = [
aws_route_table.lwtask3nat,
]
subnet_id = "${aws_subnet.sub-1b.id}"
route_table_id = aws_route_table.lwtask3nat.id
}

All set!!

Finally our code is ready, Now I am going to run this code & our infra will be setup.

To run the terraform code, run the below commands:

terraform init
terraform apply --auto-approve

Now you can check on AWS that you infra has been setup.

Also I am able connect to my private instance via bastion host & also I am able ping “google.com” that means I have internet connectivity on private instance.

Finally I can see my wordpress site which I had deployed.

Finally we have achieved our goal successfully to deploy a wordpress site on AWS.

Thank You for reading the article. If you like the article please clap & share the article among you friends, colleagues & whoever needed.

For more article stay tuned & connect to me on LinkedIn.

Vikas Goswami

LinkedIn: con007

GitHub: con007

--

--