Skip to main content

Command Palette

Search for a command to run...

Create Infrastructure using Terraform ๐Ÿ”ฅ๐Ÿ”ฅ

Published
โ€ข10 min read
Create Infrastructure using Terraform ๐Ÿ”ฅ๐Ÿ”ฅ

Project: You'll create a VPC and deploy 2 applications in different availability zones, also there will be two public subnets attached to the internet gateway & an s3 bucket will be there along with will also create a load balancer to balance the load between the instances automatically.

As a first step launch an Ec2 instance(Ubuntu for me ) and install Terraform [https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli ] as per your OS setup.

Then Install Aws-Cli for the Linux platform

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

Don't forget to configure your terraform with aws with access and secret-access-key.

Let's start the project...

Step 1:

Create a directory and go inside it, then create two files main.tf and provider.tf

mkdir terraform-project/
cd terraform-project/
touch provider.tf main.tf

step 2:

Mention the provider and region in the provider.tf file

provider "aws" {
  region="us-east-1"
}

Step 3:

Now start writing the main.tf file ,, as per the project

First create the VPC as the whole setup will be inside of a created VPC[Virtual Private Cloud].

resource "aws_vpc" "new_vpc" {
  cidr_block       = "10.0.0.0/16"

  tags = {
    Name = "Aws-Vpc"
  }
}

Step 4:

Now have to create two Public subnets and they also it should be in two different availability zones,,,, There are 6 availability zones present in the N.Virginia region so you can take any two and attach the vpc to the subnets

## Create the public subnet

resource "aws_subnet" "public_subnet1" {
  vpc_id     = aws_vpc.new_vpc.id    ## attach the vpc to the subnets
  cidr_block = "10.0.1.0/24"
  availability_zone ="us-east-1b"   ##mention the availability zones,as per your choice
  map_public_ip_on_launch = true    ## there should be a public ip created 

  tags = {
    Name = "Public-Subnet1"
  }
}

resource "aws_subnet" "public_subnet2" {
  vpc_id     = aws_vpc.new_vpc.id
  cidr_block = "10.0.2.0/24"
  availability_zone ="us-east-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "Public-Subnet2"
  }
}

Step 5:

Public subnets should be internet facing so that attach an internet gateway to the subnets.

## Create the internet-gateway

resource "aws_internet_gateway" "aws_igw" {
  vpc_id = aws_vpc.new_vpc.id   ## reference to the vpc

  tags = {
    Name = "IGW"
  }
}

Step 6:

Create the Route table for the routes to the internet.

## Create the route tabel

resource "aws_route_table" "RT" {
  vpc_id = aws_vpc.new_vpc.id   ## refernce to the vpc

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.aws_igw.id   ## Reference to the Internet gateway
  }


  tags = {
    Name = "Aws-RT"
  }
}

Step 7:

Associate the subnets to the Route Table,,,,

##Public route Table Association

resource "aws_route_table_association" "RTA1" {
  subnet_id      = aws_subnet.public_subnet1.id    ## Public subnet 1
  route_table_id = aws_route_table.RT.id    ## Refernce to the route table id
}


resource "aws_route_table_association" "RTA2" {
  subnet_id      = aws_subnet.public_subnet2.id    ## Public subnet 2
  route_table_id = aws_route_table.RT.id     
}

Step 8:

Now create the Security Group and attach it to the Vpc.

Here I have allowed the all traffic but you can only allow only port 22 and port 80 ...

## Create the security group
resource "aws_security_group" "mysg" {
  name        = "Aws-Security-Group"
  description = "Allow TLS inbound traffic"
  vpc_id      = aws_vpc.new_vpc.id

  ingress {
    description      = "TLS from VPC"
    from_port        = 0   ## All traffic
    to_port          = 0    ## All traffic
    protocol         = "-1"   ## All protocol
    cidr_blocks      = ["0.0.0.0/0"]

  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  tags = {
    Name = "my-sg"
  }
}

Step 9:

Create the S3 bucket and its name should be globally unique.

## Create S3 bucker

resource "aws_s3_bucket" "s3-bucket" {
  bucket = "terraform-project-aws-public-subnet"
}

Step 10:

Create two Ec2 instances and the subnets should be attached to the instances.

Also, I want that some scripts should run after the launching of the instances so add the user-data scripts with the instances.

## Create Ec2 instnace

resource "aws_instance" "Instance1" {
 ami = "ami-053b0d53c279acc90"
 instance_type ="t2.micro"
 vpc_security_group_ids = [aws_security_group.mysg.id]   ## Attach the Security Group
 subnet_id = aws_subnet.public_subnet1.id   ## Attach the Public Subnet-1 to the instance
 user_data = base64encode(file("userdata.sh"))   ## Add the userdata1 to instance 1 and encode it 

}

resource "aws_instance" "Instance2" {
 ami = "ami-053b0d53c279acc90"   ## mention your ami-id 
 instance_type ="t2.micro"       ## Instance type
 vpc_security_group_ids = [aws_security_group.mysg.id]
 subnet_id = aws_subnet.public_subnet2.id
 user_data = base64encode(file("userdata1.sh"))  ## Add the userdata1 to instance 2 and encode it

}

User-data 1 script:

#!/bin/bash
apt update
apt install -y apache2

# Get the instance ID using the instance metadata
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)

# Install the AWS CLI
apt install -y awscli

# Download the images from S3 bucket
#aws s3 cp s3://myterraformprojectbucket2023/project.webp /var/www/html/project.png --acl public-read

# Create a simple HTML file with the portfolio content and display the images
cat <<EOF > /var/www/html/index.html
<!DOCTYPE html>
<html>
<head>
  <title>My Portfolio</title>
  <style>
    /* Add animation and styling for the text */
    @keyframes colorChange {
      0% { color: red; }
      50% { color: green; }
      100% { color: blue; }
    }
    h1 {
      animation: colorChange 2s infinite;
    }
  </style>
</head>
<body>
  <h1>Terraform Project Server 1</h1>
  <h2>Instance ID: <span style="color:green">$INSTANCE_ID</span></h2>
  <p>Welcome to Abhishek Veeramalla's Channel</p>

</body>
</html>
EOF

# Start Apache and enable it on boot
systemctl start apache2
systemctl enable apache2

This is the User-Data 2 Script:

Both Scripts are the same but only the Heading is Different.

#!/bin/bash
apt update
apt install -y apache2

# Get the instance ID using the instance metadata
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)

# Install the AWS CLI
apt install -y awscli

# Download the images from S3 bucket
#aws s3 cp s3://myterraformprojectbucket2023/project.webp /var/www/html/project.png --acl public-read

# Create a simple HTML file with the portfolio content and display the images
cat <<EOF > /var/www/html/index.html
<!DOCTYPE html>
<html>
<head>
  <title>My Portfolio</title>
  <style>
    /* Add animation and styling for the text */
    @keyframes colorChange {
      0% { color: red; }
      50% { color: green; }
      100% { color: blue; }
    }
    h1 {
      animation: colorChange 2s infinite;
    }
  </style>
</head>
<body>
  <h1>Terraform Project Server 1</h1>
  <h2>Instance ID: <span style="color:green">$INSTANCE_ID</span></h2>
  <p>Welcome to CloudChamp's Channel</p>

</body>
</html>
EOF

# Start Apache and enable it on boot
systemctl start apache2
systemctl enable apache2

Step 11:

As per the diagram, everything is created except the load balancer part.

Let's create the application load balancer [It operates at Layer 7 of the OSI model]

  • An Application Load Balancer operates at layer 7 (application layer) and allows defining routing rules based on content across multiple services or containers running on one or more EC2 instances.

  • Scales the load balancer as traffic to the application changes over time.

  • Supports health checks, used to monitor the health of registered targets so that the load balancer can send requests only to the healthy targets.

For the Application LB you can create a separate security group but I have given the previously created security group reference and also mention subnets as it will load the balance between the two instances which are connected to the public subnets.

### Create the application load balancer

resource "aws_lb" "test_aplb" {
  name               = "test-alb-terraform"   
  internal           = false    ## It will be internal or external
  load_balancer_type = "application"    ### Type is application as there are more lb present
  security_groups    = [aws_security_group.mysg.id]
  subnets            = [aws_subnet.public_subnet1.id, aws_subnet.public_subnet2.id]


  tags = {
    Name = "Alb-Project"
  }
}

Step 12:

Create the Target-Group of ALB,,,

  • routes requests to one or more registered targets, such as EC2 instances, using the specified protocol and port number.

  • a target can be registered with multiple target groups.

  • health checks can be configured on a per-target group basis

  • When a load balancer receives a request, it evaluates the listener rules in priority order to determine which rule to apply and then selects a target from the target group for the rule action.

  • Listener rules can be configured to route requests to different target groups based on the content of the application traffic.

## Alb target group creation

resource "aws_lb_target_group" "test-target-group" {
  name     = "test-target-group"
  port     = 80    ## Port number is 80...
  protocol = "HTTP"
  vpc_id   = aws_vpc.new_vpc.id    ## Refernce to the vpc

  health_check {
    path ="/"    
    port = "traffic-port"
}
}

Step 13:

The next step is to attach the load balancer to the created target group and also attach the Instances to it.

[Note: To attach the target group and instances to the target group you can use the for-each loop for the instances attachment but here I have created it twice to refer to.]

## Load balancer Target group attachment::
 resource "aws_lb_target_group_attachment" "Instance-Attach1" {
  target_group_arn = aws_lb_target_group.test-target-group.arn   ## target group arn arn 
  target_id        = aws_instance.Instance1.id   ## Instance 1
  port             = 80
}

resource "aws_lb_target_group_attachment" "Instance-Attach2" {
  target_group_arn = aws_lb_target_group.test-target-group.arn
  target_id        = aws_instance.Instance2.id   ## Instance 2
  port             = 80    ## listening to the port 80
}

\*** target_group_arn*

The ARN of the target group with which to register targets.

Step 14:

Attach a Listenr to the Load balancer.

Before you start using your Application Load Balancer, you must add one or more listeners. A listener is a process that checks for connection requests, using the protocol and port that you configure. The rules that you define for a listener determine how the load balancer routes requests to its registered targets.

## Alb Listener

resource "aws_lb_listener" "Alb_Listener" {
  load_balancer_arn = aws_lb.test_aplb.arn    ## load balancer arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.test-target-group.arn   ## target group arn
  }
}

Step 15:

Create another file as output.tf and print the load balancer dns-name to get the dns url

output "load-balancer-dns" {

   value = aws_lb.test_aplb.dns_name
}

So this is the whole main.tf file.....

resource "aws_vpc" "new_vpc" {
  cidr_block       = "10.0.0.0/16"

  tags = {
    Name = "Aws-Vpc"
  }
}


## Create the public subnet

resource "aws_subnet" "public_subnet1" {
  vpc_id     = aws_vpc.new_vpc.id
  cidr_block = "10.0.1.0/24"
  availability_zone ="us-east-1b"
  map_public_ip_on_launch = true

  tags = {
    Name = "Public-Subnet1"
  }
}

resource "aws_subnet" "public_subnet2" {
  vpc_id     = aws_vpc.new_vpc.id
  cidr_block = "10.0.2.0/24"
  availability_zone ="us-east-1a"
  map_public_ip_on_launch = true

  tags = {
    Name = "Public-Subnet2"
  }
}
## Create the internet-gateway

resource "aws_internet_gateway" "aws_igw" {
  vpc_id = aws_vpc.new_vpc.id

  tags = {
    Name = "IGW"
  }
}


## Create the route tabel

resource "aws_route_table" "RT" {
  vpc_id = aws_vpc.new_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.aws_igw.id
  }


  tags = {
    Name = "Aws-RT"
  }
}
##Public route Table Association

resource "aws_route_table_association" "RTA1" {
  subnet_id      = aws_subnet.public_subnet1.id
  route_table_id = aws_route_table.RT.id
}


resource "aws_route_table_association" "RTA2" {
  subnet_id      = aws_subnet.public_subnet2.id
  route_table_id = aws_route_table.RT.id
}

## Create the security group
resource "aws_security_group" "mysg" {
  name        = "Aws-Security-Group"
  description = "Allow TLS inbound traffic"
  vpc_id      = aws_vpc.new_vpc.id

  ingress {
    description      = "TLS from VPC"
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]

  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  tags = {
    Name = "my-sg"
  }
}

## Create S3 bucker

resource "aws_s3_bucket" "s3-bucket" {
  bucket = "terraform-project-aws-public-subnet"
}


## Create Ec2 instnace

resource "aws_instance" "Instance1" {
 ami = "ami-053b0d53c279acc90"
 instance_type ="t2.micro"
 vpc_security_group_ids = [aws_security_group.mysg.id]
 subnet_id = aws_subnet.public_subnet1.id
 user_data = base64encode(file("userdata.sh"))

}

resource "aws_instance" "Instance2" {
 ami = "ami-053b0d53c279acc90"
 instance_type ="t2.micro"
 vpc_security_group_ids = [aws_security_group.mysg.id]
 subnet_id = aws_subnet.public_subnet2.id
 user_data = base64encode(file("userdata1.sh"))

}

### Create the application load balancer

resource "aws_lb" "test_aplb" {
  name               = "test-alb-terraform"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.mysg.id]
  subnets            = [aws_subnet.public_subnet1.id, aws_subnet.public_subnet2.id]


  tags = {
    Name = "Alb-Project"
  }
}

## Alb target group creation

resource "aws_lb_target_group" "test-target-group" {
  name     = "test-target-group"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.new_vpc.id

  health_check {
    path ="/"
    port = "traffic-port"
}
}

## Load balancer Target group attachment::
 resource "aws_lb_target_group_attachment" "Instance-Attach1" {
  target_group_arn = aws_lb_target_group.test-target-group.arn
  target_id        = aws_instance.Instance1.id
  port             = 80
}

resource "aws_lb_target_group_attachment" "Instance-Attach2" {
  target_group_arn = aws_lb_target_group.test-target-group.arn
  target_id        = aws_instance.Instance2.id
  port             = 80
}

## Alb Listener

resource "aws_lb_listener" "Alb_Listener" {
  load_balancer_arn = aws_lb.test_aplb.arn
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.test-target-group.arn
  }
}

Step 16:

Run the terraform init command.

Step 17:

Check the blueprint of the resources as a pre-plan execution.

Run terraform plan command.

16 resources are going to be created and the DNS name will be generated after applying the command.

Now finally execute the terraform apply command.

terraform apply --auto-approve

Check the dns-name as the printed output

So this is the final output of this project,,

Copy the printed DNS name and paste it into the browser.

To get the load between two instances refresh the page repeatedly...

Please destroy the infra after the project execution whether through terraform or manually as the cost is included here ...

terraform destroy -auto-approve

Refer to the amazing terraform documentation it's very helpful to create any infrastructure.

This is the link

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/

Thank You For Reading ...๐Ÿ˜‡