Build a Highly Available Infrastructure on AWS with Terraform!
Kundan Antyakula??
Devops Engineer - Data & Infrastructure Specialist | AWS Certified (2x) | GitHub Certified (1x) | Kubernetes & Containerization | CI/CD & Infrastructure Automation | Driving Secure Data & Scalable DevOps Solutions
This guide takes you through creating a robust infrastructure on AWS using Terraform, the king of Infrastructure as Code (IaC).
Get Started Quickly:
#provider.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.53.0"
}
}
}
provider "aws" {
# Configuration options
region = "us-east-1"
}
Instead of hardcoding values we can also use variable.tf
variable "cidr" {
default = "10.0.0.0/16"
}
resource "aws_vpc" "myvpc" {
cidr_block = var.cidr
}
Step-by-Step Breakdown:
#Create 2 subnets
resource "aws_subnet" "sub1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.0.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
tags = {
Name = "sub"
var = "1"
}
}
resource "aws_subnet" "sub2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1b"
map_public_ip_on_launch = true
tags = {
Name = "sub"
var = "2"
}
}
Reference of Internet Gateway
Create AWS Internet Gateway
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "igw"
}
}
Reference of route table
Route table defines how the traffic has to flow in the subnet.
Here, we mentioned VPC, CIDR block and the destination as internet gateway id.
resource "aws_route_table" "rt" {
vpc_id = aws_vpc.myvpc.id
route{
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
tags = {
Name = "rt"
}
Note:If you run into some issue’s terraform validate command help’s to debug your code.
Now, Attach this to the public subnets using public subnet association.
Reference of Subnet association
resource "aws_route_table_association" "rta1" {
subnet_id = aws_subnet.sub1.id
route_table_id = aws_route_table.rt.id
}
resource "aws_route_table_association" "rta2" {
subnet_id = aws_subnet.sub2.id
route_table_id = aws_route_table.rt.id
}
Plan is a dry run on AWS platform
terraform plan
# aws_vpc.myvpc will be created
+ resource "aws_vpc" "myvpc" {
+ arn = (known after apply)
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_dns_hostnames = (known after apply)
+ enable_dns_support = true
+ enable_network_address_usage_metrics = (known after apply)
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ tags_all = (known after apply)
}
Plan: 7 to add, 0 to change, 0 to destroy.
terraform apply
aws_vpc.myvpc: Creating...
aws_vpc.myvpc: Creation complete after 5s [id=vpc-00728b1cb80f751e0]
aws_internet_gateway.gw: Creating...
aws_subnet.sub2: Creating...
aws_subnet.sub1: Creating...
aws_internet_gateway.gw: Creation complete after 2s [id=igw-029597e8a849a2c91]
aws_route_table.rt: Creating...
aws_route_table.rt: Creation complete after 3s [id=rtb-06c362ff1ed469a89]
aws_subnet.sub2: Still creating... [10s elapsed]
aws_subnet.sub1: Still creating... [10s elapsed]
aws_subnet.sub1: Creation complete after 13s [id=subnet-06f9ed054cceee49e]
aws_route_table_association.rta1: Creating...
aws_subnet.sub2: Creation complete after 13s [id=subnet-0c172b3aa988ec52a]
aws_route_table_association.rta2: Creating...
aws_route_table_association.rta1: Creation complete after 1s [id=rtbassoc-008bb307030fe993a]
aws_route_table_association.rta2: Creation complete after 1s [id=rtbassoc-02feece10e3b1bed2]
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
领英推荐
Security group creation including inbound rule and outbound rule
resource "aws_security_group" "sg" {
name = "websg"
vpc_id = aws_vpc.myvpc.id
ingress {
description = "HTTP from VPC"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH 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"]
}
}
Creating 2 instances on two different public subnets previously created
resource "aws_instance" "server1" {
ami = "ami-04b70fa74e45c3917"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.sg.id]
subnet_id = aws_subnet.sub1.id
user_data = base64encode(file("userdata.sh"))
}
resource "aws_instance" "server2" {
ami = "ami-04b70fa74e45c3917"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.sg.id]
subnet_id = aws_subnet.sub2.id
user_data = base64encode(file("userdata1.sh"))
}
terraform apply -auto-approve
-auto-approve skips the prompt directly start creating it.
Load balancer associated with both subnets for accessing EC2 Instances inside those subnets we initially created.
resource "aws_lb" "apploadbalancer" {
name = "apploadbalancer"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.sg.id]
subnets = [aws_subnet.sub1.id, aws_subnet.sub2.id]
}
Furthermore, Create target group to inform load balancer that this is the target. Target send requests to the instances.
resource "aws_lb_target_group" "tg" {
name = "TG"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.myvpc.id
health_check {
path = "/"
port = "traffic-port"
}
}
Target group is created but empty we need to define what should be inside that Target group like instances in our case.
resource "aws_lb_target_group_attachment" "attach1" {
target_group_arn = aws_lb_target_group.tg.arn
target_id = aws_instance.server1.id
port=80
}
resource "aws_lb_target_group_attachment" "attach2" {
target_group_arn = aws_lb_target_group.tg.arn
target_id = aws_instance.server2.id
port=80
}
count.index & for each mappings can be used in production to reduce code.
But still the load balancer is not attached to the target group. For attaching we need to define a listener rule.
resource "aws_lb_listener" "listener" {
load_balancer_arn = aws_lb.apploadbalancer.arn
port = 80
protocol = "HTTP"
default_action {
target_group_arn = aws_lb_target_group.tg.arn
type = "forward"
}
}
Defined load balancer, port, protocal, default action to forward or data redirection in the listener rule.
output "loadbalancerdns" {
value = aws_lb.apploadbalancer.dns_name
}
This will show us the output of dns name after execution is done.
terraform fmt
#It checks the formatting of the terraform code.
Load balancer balances traffic by switching between two instances
For easy Understanding I have split the code the main.tf whole code is below integrated with all the above aspects.
resource "aws_vpc" "myvpc" {
cidr_block = var.cidr
}
resource "aws_subnet" "sub1" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.0.0/24"
availability_zone = "us-east-1a"
map_public_ip_on_launch = true
tags = {
Name = "sub"
var = "1"
}
}
resource "aws_subnet" "sub2" {
vpc_id = aws_vpc.myvpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1b"
map_public_ip_on_launch = true
tags = {
Name = "sub"
var = "2"
}
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.myvpc.id
tags = {
Name = "igw"
}
}
resource "aws_route_table" "rt" {
vpc_id = aws_vpc.myvpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
tags = {
Name = "rt"
}
}
resource "aws_route_table_association" "rta1" {
subnet_id = aws_subnet.sub1.id
route_table_id = aws_route_table.rt.id
}
resource "aws_route_table_association" "rta2" {
subnet_id = aws_subnet.sub2.id
route_table_id = aws_route_table.rt.id
}
resource "aws_security_group" "sg" {
name = "websg"
vpc_id = aws_vpc.myvpc.id
ingress {
description = "HTTP from VPC"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "SSH 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"]
}
}
resource "aws_s3_bucket" "terrabucket" {
bucket = "kundanantyakulaterrabucket"
}
resource "aws_instance" "server1" {
ami = "ami-04b70fa74e45c3917"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.sg.id]
subnet_id = aws_subnet.sub1.id
user_data = base64encode(file("userdata.sh"))
}
resource "aws_instance" "server2" {
ami = "ami-04b70fa74e45c3917"
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.sg.id]
subnet_id = aws_subnet.sub2.id
user_data = base64encode(file("userdata1.sh"))
}
resource "aws_lb" "apploadbalancer" {
name = "apploadbalancer"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.sg.id]
subnets = [aws_subnet.sub1.id, aws_subnet.sub2.id]
}
resource "aws_lb_target_group" "tg" {
name = "TG"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.myvpc.id
health_check {
path = "/"
port = "traffic-port"
}
}
resource "aws_lb_target_group_attachment" "attach1" {
target_group_arn = aws_lb_target_group.tg.arn
target_id = aws_instance.server1.id
port = 80
}
resource "aws_lb_target_group_attachment" "attach2" {
target_group_arn = aws_lb_target_group.tg.arn
target_id = aws_instance.server2.id
port = 80
}
resource "aws_lb_listener" "listener" {
load_balancer_arn = aws_lb.apploadbalancer.arn
port = 80
protocol = "HTTP"
default_action {
target_group_arn = aws_lb_target_group.tg.arn
type = "forward"
}
}
output "loadbalancerdns" {
value = aws_lb.apploadbalancer.dns_name
}
Benefits of Using Terraform:
By following these steps and exploring further, you'll be well on your way to building robust and scalable infrastructure on AWS using the power of Terraform!