Automate AWS Infrastructure Using Terraform

Amit KumarAmit Kumar
10 min read

Infrastructure Deployment Steps with Terraform

This blog contains the neccessary instructions and Terraform code to deploy an infrastructure stack on a cloud provider using Terraform.

These are the following steps will guide you through the process of creating a VPC, Internet gateway, custom route table, subnet, security group, network interface, elastic IP, and an Ubuntu server with Apache2 installed and enabled.

Prerequisites

Before starting the deployment process, make sure you have the following prerequisites:

  • Terraform is installed on your local machine. You can download it from the official Terraform website. Download Terraform

  • An account with the chosen cloud provider (AWS here).

  • Appropriate access and permissions to create the required resources.

  • Basic knowledge of Terraform and the cloud provider's infrastructure and networking concepts.

I highly recommend you go through the attached docs as they can be really helpful.

Note: Terraform is declarative that is you describe the desired state of your infrastructure in a Terraform configuration file, specifying the resources, their properties, and the relationships between them. You define the desired end result rather than writing step-by-step instructions or imperative commands to achieve that result.

🪜Deployment Steps

Follow the steps below to deploy the infrastructure stack using Terraform:

1. Set Up Terraform and Configure AWS Provider

  1. Make sure to install Terraform on your device then install the Terraform extension from VS Code.

    1. Create a folder and give a name of your choice.

    2. Create main.tf file. Terraform file is created using .tf extension.

    3. Configure AWS provider with Terraform.

To get your Access key and Secret key follow this article - AWS Account and Access Keys

// Configure the AWS Provider

provider "aws" {
region      = ""  # Add Region
access_key  = ""  # Add access_key
secret_key  = ""  # Add secret_key
}

It is not recommended to hard code the secrets there are other methods to securely store your credentials. But for now, keep it simple!

Docs - AWS Provider

2. Create VPC

  • Configure the desired VPC settings, such as CIDR block, name, and other parameters (if you want ).

      resource "aws_vpc" "myvpc" {
        cidr_block = "10.0.0.0/16"
    
        tags = {
          Name = "myvpc"
        }
      }
    
    • While creating a VPC we need to give the resource type and the resource name.

    • cidr_block is a required parameter for creating the VPC. This is the CIDR block of VPC.

    • tags are optional.

Docs - Terraform AWS VPC

3. Create an Internet Gateway

  1. Within the same main.tf file, configure the internet gateway settings.

  2. Associate the internet gateway with the previously created VPC.

// 2. Create Internet Gateway
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.myvpc.id
}

Docs - Terraform AWS Internet Gateway

4. Create a Custom Route Table

  1. In the main.tf file, configure the custom route table settings.

  2. Associate the routing with the VPC.

// Create Custom Route Table

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

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

// for ipv6
route {
ipv6_cidr_block = "::/0"
gateway_id = aws_internet_gateway.igw.id
}
}
  • Here we set our default route to “0.0.0.0/0”

  • aws_internet_gateway refers to the Terraform resource type that represents an internet gateway in AWS.

  • .igw is a reference to a specific instance of the aws_internet_gateway resource. In this case, igw is the name given to the aws_internet_gateway resource instance that we previously gave in step 2.

  • .id refers to the id attribute of the aws_internet_gateway resource instance. The id attribute uniquely identifies the internet gateway resource within AWS.

  • In ipv6_cidr_block ,::/0 is the IPv6 equivalent of 0.0.0.0/0 in IPv4 notation. It denotes that the route matches all IPv6 addresses, effectively making it a default route for IPv6 traffic.

Docs - Terraform AWS Route Tables

🤷‍♂️Do you know what is AWS Route Table❓

A route table contains a set of rules, called routes, that are used to determine where network traffic from your subnet or gateway is directed.

5. Create a Subnet and associate it with the route table.

  1. Create Subnet.

     // 4. Create a Subnet
    
     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 = "sub2"
       }
     }
    

    Docs - Terraform AWS Subnet

Associate the Subnet with the Route table.

// 5. Associate subnet with the Route table

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
}

6. Create a Security Group to Allow Port 22, 80, 443

  1. Allow inbound traffic on ports 22 (SSH), 80(HTTP), and 443(HTTPS).
// 6. Create Security Group to allow port 22, 80, 443

resource "aws_security_group" "websg" {
  name   = "web"
  vpc_id = aws_vpc.myvpc.id

  ingress {
    description = "HTTP form VPC"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
ingress {
description = "HTPS"
from_port = 443
to_port   = 443
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"]
ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "websg"
  }
}
  • This declares an aws_security_group resource named "websg" and specifies a name and description for the security group. It also associates the security group with a VPC identified by aws_vpc.myvpc.id.

  • This ingress block allows inbound traffic on port 443 (HTTPS) with the "tcp" protocol. The cidr_blocks parameter allows traffic from any source IP address (0.0.0.0/0), meaning it permits access from any location.

  • Similarly next ingress block allows inbound traffic on port 80 (HTTP) with the “tcp” protocol and cidr_blocks parameter allows traffic from (0.0.0.0/0).

  • The egress block allows all outbound traffic by specifying from_port and to_port as 0, protocol as "-1" (indicating all protocols), and permitting all destination IP addresses (0.0.0.0/0 for IPv4 and ::/0 for IPv6).

Docs - Terraform AWS Security Group

🤷‍♂️Do you know what is the difference between ingress and load balancer

Ingress refers to the process of incoming traffic or data entering a network or a specific network component, such as a server or a service. It generally refers to the path or entry point through which external traffic reaches the internal network.

A load Balancer is a service that acts as a single entry point for incoming traffic and intelligently distributes it across the available resources, such as web servers or application instances, in a way that balances the workload.

7. Create a Network Interface in the subnet

  • Associate the network interface with the previously created subnet that was created in step 4.

      // 7. Create a Network Interface in the Subnet
    
      resource "aws_network_interface" "web-server" {
        subnet_id       = aws_subnet.sub1.id
        private_ips     = ["10.0.1.50"]
        security_groups = [aws_security_group.websg.id]
    
      }
    
    • subnet_id specifies the ID of the subnet in which the network interface will be created. It references the id attribute of an existing aws_subnet resource named subnet-1.

    • private_ips is an attribute that allows you to assign one or more private IP addresses to the network interface.

Docs - Terraform AWS Network Interface

8.Assign an Elastic IP to the Network Interface

  1. Associate the Elastic IP with the network interface that was created in step 7.
// 8. Assign an elastic IP to the network interface.
resource "aws_eip" "one" {
  vpc = true
  network_interface = aws_network_interface.web-server.id
  associate_with_private_ip = "10.0.1.50"
  depends_on = [ aws_internet_gateway.igw ]
}
  • depends_on allows you to create an explicit dependency between two resources.

Docs - Terraform AWS EIP

9. Create a Load Balancer (Application Load Balancer)

  1. In the main.tf file, configure the load balancer settings.
resource "aws_lb" "myalb" {
  name               = "myalb"
  internal           = false
  load_balancer_type = "application"

  security_groups = [aws_security_group.websg.id]
  subnets         = [aws_subnet.sub1.id, aws_subnet.sub2.id]

  tags = {
    Name = "web"
  }
}

Associate Load Balancer with Target group

resource "aws_lb_target_group" "tg" {
  name     = "myTG"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.myvpc.id

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

Docs - AWS Load Balancer Target Group

Assigning Load Balancer Target Group Attachment

resource "aws_lb_target_group_attachment" "attach1" {
  target_group_arn = aws_lb_target_group.tg.arn
  target_id        = aws_instance.webserver1.id
  port             = 80
}

resource "aws_lb_target_group_attachment" "attach2" {
  target_group_arn = aws_lb_target_group.tg.arn
  target_id        = aws_instance.webserver2.id
  port             = 80
}

Docs - AWS Load Balancer Target Group Attachment

Now Assigning Load Balancer Listener

resource "aws_lb_listener" "listener" {
  load_balancer_arn = aws_lb.myalb.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    target_group_arn = aws_lb_target_group.tg.arn
    type             = "forward"
  }
}

Docs - AWS Load Balancer Listener

aws_lb_listeneris a process that checks for incoming connection requests to a load balancer and forwards them to the appropriate target group based on the protocol and port configured.

aws_lb_listenerdefines how the load balancer will handle incoming traffic and how it will route the requests to backend resources.

Generating DNS Name for the Load Balancer

  • In the main.tf file, you can configure the dns setting
output "loadbalancerdns" {
  value = aws_lb.myalb.dns_name
}

Displays the DNS name of the load balancer after creation. This DNS name can be used to route traffic to your application.

10. Creating S3 Bucket

  • In the main.tf file, you can configure S3 Bucket Costom Setting
resource "aws_s3_bucket" "example" {
  bucket = "amit-terraform-2025-project"
}

Docs - AWS S3 Bucket

11. Create Ubuntu Server and Install/Enable Apache2

  1. Create the Ubuntu server and install Apache2.

  2. In the main.tf file, configure the instance settings.

resource "aws_instance" "webserver1" {
  ami                    = "ami-04b4f1a9cf54c11d0"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.websg.id]
  subnet_id              = aws_subnet.sub1.id
  user_data              = base64encode(file("userdata.sh"))
}

resource "aws_instance" "webserver2" {
  ami                    = "ami-04b4f1a9cf54c11d0"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.websg.id]
  subnet_id              = aws_subnet.sub2.id
  user_data              = base64encode(file("userdata1.sh"))
}
  • Add your preferred ami (Amazon Machine Image) and add it's instance type, aws_security_group, subnet_id, and userdata.

  • If you have trouble generating your key pair refer to these docs on AWS key-pair.

Docs - AWS Key-Pair

  • In userdata.sh and userdata1.sh add the bash code to install apache2 on the Ubuntu server.

11. Time to run the server🏃

Open up the terminal in VS Code or you can also use cmd to make sure that you are in your correct directory.

Here are some basic CLI commands of Terraform: -

Docs - Terraform Basic CLI Commands

  1. Run terraform init

You would see somethings similar to this -

Running the previous command terraform will automatically create the required files.

2. Now run terraform plan

  • It is used to preview the changes that terraform will make to your infrastructure before applying them - it’s dry run.

As you can see there are no running instances in AWS EC2 instances.

As you can see there is not created any load balancer in AWS.

  1. Let us now run terraform apply

  1. Type yes

You can also useterraform apply -auto-approve to auto-approve all the actions.

🎉WOOOOOOHOOOOO!!! Your Ubuntu EC2 instance got successfully launched!!!!

Hurray!!!

You can also verify this by using a Public IPv4 address, it must show a result similar to this.

You can see your very first web server written that is you successfully launched your instance and created a web server.

🏁The End

Congratulations! You have successfully deployed an infrastructure stack using Terraform. The stack includes a VPC, internet gateway, custom route table, subnet, security group, network interface, elastic IP, and an Ubuntu server with Apache2 installed and enabled. You can now access the Ubuntu server over the internet using the elastic IP and start hosting your websites or applications.

Please refer to the Terraform documentation and guides provided by your cloud provider for more detailed information on each step and additional customization options.

So, far you have seen the power of Iac using Terraform. I hope this blog might have helped you get to know about Terraform and a small demonstration of what it can do.

Don’t forget to try it by yourself!

Architecture Diagram

Git Repo -

  1. Star and fork the repository.

  2. Clone the repository.

  3. Read the steps from Steps.md

  4. Automate your AWS Infrastructure.

https://github.com/amitkumar-Github8/Automating-AWS-Infrastructure-Using-Terraform/tree/main

1
Subscribe to my newsletter

Read articles from Amit Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Amit Kumar
Amit Kumar