Build a Highly Available Infrastructure on AWS with Terraform!

Build a Highly Available Infrastructure on AWS with Terraform!

This guide takes you through creating a robust infrastructure on AWS using Terraform, the king of Infrastructure as Code (IaC).

Get Started Quickly:

  1. Initialize your working directory: terraform init
  2. Define essential configurations in separate files:provider.tf: Sets up the Terraform AWS provider.variable.tf: Stores reusable variables like CIDR block.main.tf: Defines the core infrastructure resources.

#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 a VPC (Virtual Private Cloud):
  • Create Public Subnets: here's reference of subject in the AWS Console

  • map_public_ip_on_launch - Specify true to indicate that instances launched into the subnet should be assigned a public IP address. Default is false

#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 an Internet Gateway (IGW):

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

  • Create a Route Table:
  • Associate Route Table with Public Subnets:

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 the Infrastructure:

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.        

  • Apply the Infrastructure:

VPC Is created
Subnets are created
Route Tables are created
Internet Gateways are created

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.

Two Instances are being instantiated
As we mentioned ingress rules and egress rules accordingly.It has been configured.

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.

Target Group Reference


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.        
Output obtained after terraform script execution
Load Balancer Created
Target Group is created
Listeners are also created for data redirection


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:

  • Automation: Effortlessly manage infrastructure with code.
  • Repeatability: Ensures consistent configurations across deployments.
  • Version Control: Track changes and rollback if needed.
  • Error Detection: Built-in checks identify configuration mistakes.

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!

要查看或添加评论,请登录

Kundan Antyakula??的更多文章

社区洞察

其他会员也浏览了