Terraform | Create a VPC, subnets and more…

Create Infrastructure as Code!

This concept is so popular nowadays. Simply put, Terraform enables you to create infrastructure with code and codes can be stored in version control. In this article, instead of talking about what are the advantages of Terraform or how can I install it (you can check it form https://www.terraform.io/), I will make a real world example on AWS. Terraform must be installed!

I’m going to create a VPC, 1 public subnet, 1 private subnet, 1 Internet gateway, 1 security group and 1 EC2. It will be a NGINX in this EC2. In addition, I need to create Custom Route Tables and associate them with subnets. All this infrastructure will be in London region.

If you are not familiar with AWS services such as VPC, subnets, security groups, internet gateway, some networking concepts, it may good to read about them and then study on Terraform. Because, at the end of the day, you will create an AWS (or GCP, Azure) infrastructure. That is why, Cloud architecture knowledge is so important.

Let’s Start!

1. Create your working folder.

I have chosen terraform-vpc

2. Create “vars.tf”

All variables will be in this file. Now, there is only one item but there will be more…

variable "AWS_REGION" {    
default = "eu-west-2"

3. Create “provider.tf”

All infrastructure will be on the AWS. If you want to use another cloud provider such as GCP or Azure, you need to change this.

We will use variables in this demo. Later, you will see the vars.tf file and variables in it. Now, var.AWS_REGION = eu-west-2 is enough.

provider "aws" {
region = "${var.AWS_REGION}"

4. Now, we have 2 files.

Now, we have 2 files

We are ready to init!

Initialization is successful!

5. After the initialization there must be 2 file and 1 folder in your project folder.

After “terraform init”

6. Create VPC!

Create vpc.tf

cidr_block: allows you to use the IP address that start with “10.0.X.X”. There are 65,536 IP addresses are ready to use.

instance_tenancy: if it is true, your ec2 will be the only instance in an AWS physical hardware. Sounds good but expensive.

resource “aws_vpc” “prod-vpc” {
cidr_block = “”
enable_dns_support = “true” #gives you an internal domain name
enable_dns_hostnames = “true” #gives you an internal host name
enable_classiclink = “false”
instance_tenancy = “default”

tags {
Name = “prod-vpc”

7. Create Public Subnet

We are still in the vpc.tf

vpc_id: this subnet will be the vpc just created before. We give the created VPC id to the subnet.

cidr_block: We have 254 IP addresses in this subnet

map_public_ip_on_launch: This is so important. The only difference between private and public subnet is this line. If it is true, it will be a public subnet, otherwise private.

resource “aws_subnet” “prod-subnet-public-1” {
vpc_id = “${aws_vpc.prod-vpc.id}”
cidr_block = “”
map_public_ip_on_launch = “true” //it makes this a public subnet
availability_zone = “eu-west-2a” tags {
Name = “prod-subnet-public-1”

8. Create Internet Gateway

Create network.tf

It enables your vpc to connect to the internet

resource "aws_internet_gateway" "prod-igw" {
vpc_id = "${aws_vpc.prod-vpc.id}"
tags {
Name = "prod-igw"

9. Create Custom Route Table

We are still in the network.tf

Create a custom route table for public subnet. public subnet can reach to the internet by using this.

resource "aws_route_table" "prod-public-crt" {
vpc_id = "${aws_vpc.main-vpc.id}"

route {
//associated subnet can reach everywhere
cidr_block = "" //CRT uses this IGW to reach internet
gateway_id = "${aws_internet_gateway.prod-igw.id}"

tags {
Name = "prod-public-crt"

10. Associate CRT and Subnet

We are still in the network.tf

resource "aws_route_table_association" "prod-crta-public-subnet-1"{
subnet_id = "${aws_subnet.prod-subnet-public-1.id}"
route_table_id = "${aws_route_table.prod-public-crt.id}"

11. Create a Security Group

We are still in the network.tf

We will use this Security Group for our EC2.

I want to access my EC2 by using SSH. That is why, I opened the port 22 for all the internet. Please read the comments just above cidr_blocks.

Also, visitors can reach our NGINX (we will install it soon) because I opened the port 80.

resource "aws_security_group" "ssh-allowed" {
vpc_id = "${aws_vpc.prod-vpc.id}"

egress {
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = [""]
} ingress {
from_port = 22
to_port = 22
protocol = "tcp" // This means, all ip address are allowed to ssh !
// Do not do it in the production.
// Put your office or home address in it!
cidr_blocks = [""]
} //If you do not add this rule, you can not reach the NGIX
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = [""]
} tags {
Name = "ssh-allowed"

12. Add Ami variable into the “vars.tf”

I will create ec2 soon. Before that, I need to add AMI variable to the vars file. I used “map” type variable. It is not necessary for this demo but I want to show you what variables are capable of.

You can find proper image from this site.

variable "AMI" {
type = "map"

default {
eu-west-2 = "ami-03dea29b0216a1e03"
us-east-1 = "ami-0c2a1acae6667e438"

13. Create EC2

We have created lots of components and now we will create an EC2.

I used lookup to get ami from the variables file because its type is map

I describe subnet_id (this ec2 will be in this subnet) and security group (this ec2 will use this SG). As I said before, I want to connect my ec2, so i need a key-pair. Later, i will create it.

aws_key_pair resource sends the public key to the EC2. So, I can connect it.

2 provisioner and 1 connection parts are required for nginx installation. There are better ways to do this. For example; cloud init config method, I will prepare another article about it.

resource "aws_instance" "web1" {
ami = "${lookup(var.AMI, var.AWS_REGION)}"
instance_type = "t2.micro" # VPC
subnet_id = "${aws_subnet.prod-subnet-public-1.id}" # Security Group
vpc_security_group_ids = ["${aws_security_group.ssh-allowed.id}"] # the Public SSH key
key_name = "${aws_key_pair.london-region-key-pair.id}" # nginx installation
provisioner "file" {
source = "nginx.sh"
destination = "/tmp/nginx.sh"
} provisioner "remote-exec" {
inline = [
"chmod +x /tmp/nginx.sh",
"sudo /tmp/nginx.sh"
} connection {
user = "${var.EC2_USER}"
private_key = "${file("${var.PRIVATE_KEY_PATH}")}"
}// Sends your public key to the instance
resource "aws_key_pair" "london-region-key-pair" {
key_name = "london-region-key-pair"
public_key = "${file(var.PUBLIC_KEY_PATH)}"

14. Create a key-pair

Press enter for all question

15. We are ready!

If you clone the project from github, it must be successful

terraform plan -out terraform.outterraform apply terraform.out

16. The End!

EC2 is ready
NGINX is ready


That’s all. All you need is follow all these steps and clone this repository to start terraform.

After cloning the repo, just run these 3 commands

ssh-keygen -f london-region-key-pair

terraform init

terraform plan -out terraform.out

terraform apply terraform.out

Leave a Comment

MFH IT Solutions (Regd No -LIN : AP-03-46-003-03147775)

Consultation & project support organization.


MFH IT Solutions (Regd)
NAD Kotha Road, Opp Bashyam School, Butchurajupalem, Jaya Prakash Nagar Visakhapatnam, Andhra Pradesh – 530027