Explaining Amazon-Virtual Private Cloud (VPC) with its Terraform Code

Kvs Vishnu Kumar
7 min readSep 19, 2020

Creating WordPress in public subnet,MySQL in private subnet and finally creating Bastion Host with Terraform

What is Virtual Private Cloud (VPC) ?

Amazon Virtual Private Cloud is a cloud computing service that provides its users a virtual private cloud, by provisioning a logically isolated section of Amazon Web Services Cloud.

It enables you to launch AWS resources into a virtual network that you’ve defined. It resembles a traditional network that you’d operate in your own data center, but with extra benefits like scaling your infrastructure.

Concepts of VPC

  1. Virtual Private Cloud
  2. Subnets
  3. Route Table
  4. Internet Gateway
  5. Security Groups

Basics of Networking

A network is simply a bunch of computers that are connected to each other and can communicate. Here, computer networking means sharing or exchanging data among the computers.

To connect to other devices, each device should have a Network Interface Card(NIC). By default, each network interface card (NIC) has its own unique IP address. However, you can assign multiple IP addresses to a single NIC.

There are two types of ip address. They are public ip’s and private ip’s. Private ip addresses are free to use where as public ip’s aren’t. In a Local Area Network (LAN), we use private ip’s for all devices. And then, for their connection, we use a Switch.

Private ip address range

We can determine if two devices are in same network with its network name. Network name is a portion of a ip address which is defines by Net Mask. For example 192.168.1.5 with net mask 255.255.0.0 has a network name 192.168.0.0. So, all devices with a same network name can connect to each other.

A public IP address is an ip address that can be accessed over the Internet. A public ip address is a globally unique address assigned to a computing device.

NOTE: A device with private ip address cant connect to a device with public ip address and vice versa. To make this possible, we use Network Address Translation (NAT) concept.

Switch, Router and Bridge

These are three main networking devices.

Switch is used for connecting all devices of a same network. For example, we use Switch in a LAN.

Router is a device which is used to connect a device from private world to a device in public world. Router uses NAT concept. NAT means Network Address Translation.

Router has two interfaces.One with local ip and other with global ip. When a local packet approaches the router, it replaces the local ip to its global ip for internet access. And also it changes global ip(public ip) to connect with the private ip bu replacing it with its local ip.

Bridge is a device which acts as both router and switch. Also it can act as a specific device as well such as L2 Bridge(Switch) and L3 Bridge(Router)

Internet Gateway and NAT Gateway are Routers

VPC and Subnets

In simple terms, VPC is a network consisting of a range of ip addresses. For example, lets assume 10.0.0.0/16 is a VPC. With the help of net mask, we can say that its network name is 10.0.0.0 and its ip ranges from 10.0.0.1 to 10.0.255.255. We get a total of 65536 ips.

Subnet is a part of VPC. Its like a subset of VPC. By above example, 10.0.1.0/24 and 10.0.2.0/24 are two subnets. Each subnet has 255 ip addresses. (Like-10.0.1.0 to 10.0.1.255)

Any instance in a network should be launched in a subnet. It gets its unique ip from its subnet.

Terraform Code

  1. Create a VPC.
  2. Create two subnets: Public and Private
  3. Create an Internet Gateway and attach it to VPC
  4. Create routing table for Internet gateway so that instance can connect to outside world, update and associate it with public subnet.
  5. Launch ec2 instance with WordPress setup. Also create a security group to open port 80
  6. Launch another ec2 instance in private subnet with MySQL in it. Create another security group to allow port 3306
  7. Attach keys to instances for ssh logging.

Creating a Virtual Private Cloud(VPC):-

Amazon VPC is Network as a Service provided by AWS. It enables you to launch AWS resources into a virtual network that you’ve defined.In simply terms , it is a network layer for EC2.

resource "aws_vpc" "myvpc" {
cidr_block = "10.0.0.0/16"
}

Creating Subnets

We create two subnets, a public subnet and a private subnet. In terraform,the main difference between public and private subnet is that, if a subnet has map_public_ip_on_launch = true, then its public. If its false then its a private subnet.

resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-south-1a"
map_public_ip_on_launch = true
tags = {
Name = "public subnet"
}
}
resource "aws_subnet" "private_subnet" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.2.0/24"
availability_zone="ap-south-1a"
tags = {
Name = "private subnet"
}
}

Creating an Internet Gateway

There are two types of gateways: Internet gateway and NAT gateway. An Internet gateway provides internet to the VPC. We attach Internet gateway to the VPC.

resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.myvpc.id
}

Creating route table and route table association

Route Table : It controls the routing for all subnets that are not explicitly associated with any other route table.

Route table association : The association between a route table and a subnet, internet gateway, or virtual private gateway.

resource "aws_route_table" "rt" {
vpc_id = aws_vpc.myvpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = {
Name = "Route Table"
}
}
resource "aws_route_table_association" "rt_association" {
subnet_id = aws_subnet.public_subnet.id
route_table_id = aws_route_table.rt.id
}

NAT Instance vs Bastion Host

NAT Instance: NAT instance is an EC2 instance which is used as a gateway for private instances to allow them access to internet to download any packages. We launch a new NAT instance in public subnet and ssh in to this nat instance, upload pem key to the private instances and ssh in to those private instances.

Bastion Host:Bastion Host also acts as a gate to the infrastructure to protect our private resources.This bastion is launched using public subnets and allows users who has right credentials to access private resources.

The main difference between NAT instance and Bastion host is that NAT instance allows internet access to private instances whereas Bastion host is only used to ssh into private instances.Bastion doesn’t allow internet access.

Here, we are going to create a Bastion instance.

Creating Elastic-Ip address

Elastic ip addresses are nothing but static ip addresses which means these ip’s are fixed.Normally when you stop and start an instance, the public ips might change. So if we need a fixed ip, then we should assign a static ip to the instance.

For NAT instance, as we allow only this instance for connecting to private instances, the ip of NAT instance should be static. Do , we assign a static ip address.

resource "aws_eip" "bastion" {
instance = "${aws_instance.bastion.id}"
vpc = true
}

Note: Here we create NAT instance and then attached an elastic ip to it

Creating Security groups

Here, we create three security groups for three instances.

Our first security group if for wordpress instance. Here we allow 22,80 ports in ingress. We allow all traffic in outbound.

We create second security group for mysql instance. We allow port 3369 only to ip addresses in the vpc. And we allow ssh connection only to one ip address that is the NAT instance ip address. Through this,we achieve security to the mysql instance.

And then we create third security group for bastion host. Here we allow only ssh in ingress and all traffic in egress for internet connectivity.

Wordpress Security Group

resource "aws_security_group" "wordpress_sg" {
vpc_id = aws_vpc.myvpc.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = [aws_subnet.private_subnet.cidr_block]
}
}

MySQL Security Group

resource "aws_security_group" "mysql_sg" {
vpc_id = aws_vpc.myvpc.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = [aws_vpc.myvpc.cidr_block]
}
ingress {
protocol = "tcp"
from_port = 22
to_port = 22
cidr_blocks = ["${aws_instance.bastion.private_ip}/32"]
}
ingress {
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = [aws_vpc.myvpc.cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
depends_on = [aws_instance.bastion]
}

Bastion security group

resource "aws_security_group" "bastion_sg" {
name = "bastion-security-group"
vpc_id = aws_vpc.myvpc.id
ingress {
protocol = "tcp"
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"]
}
egress {
protocol = -1
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
}
}

Creating Instances

We should create three instances. They are for WordPress,MySQL and then bastion instance.For Wordpress and MySQL, I used bitnami. All bitnami AMIs are present in community AMI’s section.

Wordpress Instance

resource "aws_instance" "wordpress" {
ami = "ami-000cbce3e1b899ebd" //bitnami wordpress
instance_type = "t2.micro"
key_name = "wpkey"
subnet_id = aws_subnet.public_subnet.id
vpc_security_group_ids = [aws_security_group.wordpress_sg.id]
associate_public_ip_address = true
depends_on = [aws_instance.bastion]
tags = {
Name = "V_wordpress"
}
}

MySQL Instance

resource "aws_instance" "mysql" {
ami = "ami-0019ac6129392a0f2" //bitnami mysql
instance_type = "t2.micro"
subnet_id = aws_subnet.private_subnet.id
key_name = "mysqlkey"
vpc_security_group_ids = [aws_security_group.mysql_sg.id, aws_security_group.bastion_sg.id]
tags = {
Name = "V_mysql"
}
depends_on = [aws_instance.bastion]
}

Bastion Instance

resource "aws_instance" "bastion" {
ami = "ami-00b3aa8a93dd09c13"
//aws - vpc - bastion or you can choose Linux 2 also
instance_type = "t2.micro"
key_name = "bastionkey"
subnet_id = aws_subnet.public_subnet.id
vpc_security_group_ids = [aws_security_group.bastion_sg.id]
tags = {
Name = "V_bastion"
}
}
resource "aws_eip" "bastion" {
instance = aws_instance.bastion.id
vpc = true
}

We have used bitnami for our servers. In bitnami, we have to conect wordpress and mysql. First, we have to copy mysql private key to bastion host. Then we login to it and then ssh to mysql instance. Then we can create tables in mysql, and connect it to wordpress

NOTE: Bitnami has its own documentation to connect wordpress to mysql. You can get it online

Finally, we completed our script. Click here for github link.

Thanks Vimal Daga sir for your guidance.

--

--