Create AWS VPC, public subnet, using terraform

In my previous post I showed you how to create EC2 instance using terraform. In this post I will show you how to create VPC, create public subnet in two availability zones and then add load balancer and an internet gateway to allow traffic. Also add EC2 instances and run Apache server on them and allow traffic to the servers through load balancer. And I will show you how to create this infrastructure as code by using Terraform.

VPC (Virtual Private Cloud) is an isolated group of infrastructure resources in a public cloud environment. It is a virtual network same as on premises, but on public cloud and the benefit of VPC is scalable infrastructure. In this example I will create a VPC and add two public subnets in two availability zones. Then I will add EC2 instances in each availability zone and add application load balancer. On top I will add internet gateway to allow traffic from internet. Below diagram will explain the VPC and resources we are going to create. I named my VPC Zoro, you can name any.

ZORO VPC

I am going to create this new VPC in US East region of AWS. In my previous post I separate the variables in a ‘variables.tf’ file. So I will do the same thing here. I define region, access keys and availability zones. I am creating zones in ‘US-EAST-1A’ and ‘US-EAST-1B’. The complete code of variables file shown below.

variable "region" {
    default = "us-east-1"
}
variable "aws_access_key" {
    default = "XXXXXXXXXXXXXXXXXX"
}

variable "aws_secret_key" {
    default = "XXXXXXXXXXXXXXXXXXX"
}

variable "aws_zone_one" {
    default = "us-east-1a"
}

variable "aws_zone_two" {
    default = "us-east-1b"
}

In the zorovpc.tf file after setting the region and secret key, I created the VPC. CIDR (Classless Inter Domain Routing) block IP address rang I set to ‘10.0.0.0/16’. Instance tenancy I set to default. With default option the VPC will be created on shared resources. If you set the ‘instance_tenancy’ value to ‘dedicated’, AWS will reserve dedicated resources for the VPC. For enterprise projects its a good idea. But for small projects or for demo I would suggest to set instance_tenancy to default so your resource will remain in free tier or it cost you minimal. In the next step I created two subnets in two availability zones. And keep them under the VPC by using the VPC Id ‘${aws_vpc.zorovpc.id}’.

##### VPC ######
resource "aws_vpc" "zorovpc" {
    cidr_block = "10.0.0.0/16"
    instance_tenancy = "default"
    enable_dns_support = true
    enable_dns_hostnames = true
    tags = {
        Name = "ZoroVPC"
    }
}

######## SubNet  #############
resource "aws_subnet" "zorosubone" {
    vpc_id = "${aws_vpc.zorovpc.id}"
    cidr_block = "10.0.1.0/24"
    availability_zone = "us-east-1a"
    map_public_ip_on_launch = true
    tags = {
        Name = "Zoro-SubNet-One"
    }
}

resource "aws_subnet" "zorosubtwo" {
    vpc_id = "${aws_vpc.zorovpc.id}"
    cidr_block = "10.0.2.0/24"
    availability_zone = "us-east-1b"
    map_public_ip_on_launch = true
    tags = {
        Name = "Zoro-SubNet-Two"
    }
}

Internet gateway allow internet traffic to communicate with our resources. But we will allow internet traffic to communicate with our instances only through load balancer. As you can see in the above diagram. In the below code you, I created a internet gateway. Then create a route table, and create its association with two subnets.

##### Internet Gateway ######

resource "aws_internet_gateway" "igw" {
    vpc_id = "${aws_vpc.zorovpc.id}"
    
    tags = {
        Name = "Zoro-IGW"
    }
}

############## Route Table ##############
resource "aws_route_table" "main-public-rt" {
    vpc_id = "${aws_vpc.zorovpc.id}"
    route{
        cidr_block="0.0.0.0/0"
        gateway_id="${aws_internet_gateway.igw.id}"
    }

    tags = {
        Name="zoro-public-rt"
    }
}

resource "aws_route_table_association" "route-tbl-link1" {
  subnet_id = "${aws_subnet.zorosubone.id}"
  route_table_id = "${aws_route_table.main-public-rt.id}"
}

resource "aws_route_table_association" "route-tbl-link2" {
  subnet_id = "${aws_subnet.zorosubtwo.id}"
  route_table_id = "${aws_route_table.main-public-rt.id}"
}

Next I created a load balancer security group which allow incoming traffic (ingress) on port 80 and allow all out going traffic (egress). And assign this security group to the load balancer in the next step. And assign two subnets to my new load balancer zoro-lb. Then I created target group and assign the target group to load balancer via load balancer listener.

##### ALB Security Group ######
resource "aws_security_group" "lb-sg" {
  name = "zoro-lb-sg"
  description = "Load balancer security group"
  vpc_id = "${aws_vpc.zorovpc.id}"
  ingress {
    from_port   = 80
    to_port     = 80
    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"]
  }
}
######## ALB ############
resource "aws_lb" "zorolb" {
    name = "zoro-lb"
    internal = false
    load_balancer_type = "application"
    security_groups = ["${aws_security_group.lb-sg.id}"]
    subnets = ["${aws_subnet.zorosubone.id}","${aws_subnet.zorosubtwo.id}"]
    
}
##### ALB Target Group
resource "aws_alb_target_group" "lb-tg" {
  name = "zoro-lb-tg"
  port = 80
  protocol = "HTTP"
  vpc_id = "${aws_vpc.zorovpc.id}"

}
###### LB Listner #####
resource "aws_alb_listener" "lb-listner" {
  load_balancer_arn = "${aws_lb.zorolb.arn}"
  port = "80"
  protocol = "HTTP"
  default_action {
      target_group_arn = "${aws_alb_target_group.lb-tg.arn}"
      type = "forward"
  }
}

EC2 instance T2 micro comes under free tier. I created two EC2 machines in two zones. The EC2 machines will read the user data from userdata.sh file. User data will update the instance and install apache server, create a index page which will return the machine IP address. A new security group is created which allow TCP traffic at port 80 and it will allow traffic only through load balancer. In the end newly created instances are added to load balancer target group.

######## Security Group ######
resource "aws_security_group" "ec2_sg" {
  name = "allow_http"
  vpc_id = "${aws_vpc.zorovpc.id}"
  ingress {
      from_port = 80
      to_port = 80
      protocol = "TCP"
      security_groups = ["${aws_security_group.lb-sg.id}"]
  }
  egress {
      from_port = 0
      to_port = 0
      protocol = "-1"
      cidr_blocks = ["0.0.0.0/0"]
  }
}

###### EC2 #####

resource "aws_instance" "myinstance-one" {
	ami = "ami-0b69ea66ff7391e80"
	instance_type = "t2.micro"
    availability_zone = "${var.aws_zone_one}"
    subnet_id = "${aws_subnet.zorosubone.id}"
    vpc_security_group_ids = ["${aws_security_group.ec2_sg.id}"]
    user_data = "${file("userdata.sh")}"
	tags = {
		Name = "EC2one"
	}
}



resource "aws_instance" "myinstance-two" {
	ami = "ami-0b69ea66ff7391e80"
	instance_type = "t2.micro"
    availability_zone = "${var.aws_zone_two}"
    subnet_id = "${aws_subnet.zorosubtwo.id}"
    vpc_security_group_ids = ["${aws_security_group.ec2_sg.id}"]
    user_data = "${file("userdata.sh")}"
	tags = {
		Name = "EC2two"
	}
}

###### Target group attachment #####
resource "aws_alb_target_group_attachment" "alb_instance1" {
  target_group_arn = "${aws_alb_target_group.lb-tg.arn}"
  target_id = "${aws_instance.myinstance-one.id}"
  port = 80
}

resource "aws_alb_target_group_attachment" "alb_instance2" {
  target_group_arn = "${aws_alb_target_group.lb-tg.arn}"
  target_id = "${aws_instance.myinstance-two.id}"
  port = 80
}

Now run the terraform file and you will see that it will create the VPC, subnets load balancer and EC2 instances. The security groups are also get created. You can find the complete code here.