Skip to main content

Command Palette

Search for a command to run...

Automate AWS Infrastructure Using Terraform

This is an hands on project for AWS and Terraform

Updated
10 min read
Automate AWS Infrastructure Using Terraform

👑Infrastructure Deployment Steps with Terraform

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

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.

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

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

  4. Configure AWS provider with Terraform.

  5. To get your AWS 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 acces_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

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

resource "aws_vpc" "prod-vpc" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "production"
  }
}
  • 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" "gw" {
  vpc_id = aws_vpc.prod-vpc.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 table with the VPC.

// 3. Create Custom Route Table 

resource "aws_route_table" "prod-route-table" {
  vpc_id = aws_vpc.prod-vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id
  }

  route {
    ipv6_cidr_block = "::/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "Prod"
  }
}
  • 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.

  • .gw is a reference to a specific instance of the aws_internet_gateway resource. In this case, gw 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" "subnet-1" {
  vpc_id            = aws_vpc.prod-vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-1a"

  tags = {
    Name = "prod-subent"
  }

}

Docs - Terraform AWS Subnet

  1. Associate the subnet with the Route table.
# 5. Associate subnet with the Route table

resource "aws_route_table_association" "a" {
  subnet_id      = aws_subnet.subnet-1.id
  route_table_id = aws_route_table.prod-route-table.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,00,443

resource "aws_security_group" "allow_web" {
  name        = "allow_web_traffic"
  description = "Allow web inbound traffic"
  vpc_id      = aws_vpc.prod-vpc.id

  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTP"
    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"]
  }

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

  tags = {
    Name = "allow_web"
  }
}
  • This declares an aws_security_group resource named "allow_web" and specifies a name and description for the security group. It also associates the security group with a VPC identified by aws_vpc.prod-vpc.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

  1. 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-nic" {
  subnet_id       = aws_subnet.subnet-1.id
  private_ips     = ["10.0.1.50"]
  security_groups = [aws_security_group.allow_web.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-nic.id
  associate_with_private_ip = "10.0.1.50"
  depends_on = [ aws_internet_gateway.gw ]
}
  • depends_on allows you to create an explicit dependency between two resources.

Docs - Terraform AWS EIP

9. Create Ubuntu Server and Install/Enable Apache2

  1. Create the Ubuntu server and install Apache2.
// 9. Create Ubuntu server and install/enable apache2 

resource "aws_instance" "web-server-instance" {
  ami = "ami-053b0d53c279acc90"
  instance_type = "t2.micro"
  availability_zone = "us-east-1a"
  key_name = "main-key"

  network_interface {
    device_index = 0
    network_interface_id = aws_network_interface.web-server-nic.id
  }
  user_data = <<-EOF
              #!/bin/bash
              sudo apt update -y
              sudo apt install apache2 -y
              sudo systemctl start apache2
              sudo bash -c 'echo your very first web server > /var/www/html/index.html'
              EOF

  tags = {
    Name = "Web-Server"
  }
}
  • Add your preferred ami (Amazon Machine Image) and add it's instance type, availability zone and key_name .

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

Docs - AWS Key-Pair

  • In user_data add the bash code to install apache2 on the Ubuntu server.

10. Time to run 🏃🏻‍♂️

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 something similar to this -

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

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

  1. Let us now run terraform apply

  1. Type yes

You can also use terraform 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!

➕Full Code

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

# Configure the AWS Provider

provider "aws" {
  region     = ""  //Add region
  access_key = ""   //Add acces_key
  secret_key = ""   //Add secret_key
}

# 1. Create vpc 

resource "aws_vpc" "prod-vpc" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "production"
  }
}

# 2. Create Internet Gateway 

resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.prod-vpc.id
}

# 3. Create Custom Route Table 

resource "aws_route_table" "prod-route-table" {
  vpc_id = aws_vpc.prod-vpc.id

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

  route {
    ipv6_cidr_block = "::/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "Prod"
  }
}

# 4. Create a Subnet 

resource "aws_subnet" "subnet-1" {
  vpc_id            = aws_vpc.prod-vpc.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "" //Add Availability Zone

  tags = {
    Name = "prod-subent"
  }

}

# 5. Associate subnet with the Route table

resource "aws_route_table_association" "a" {
  subnet_id      = aws_subnet.subnet-1.id
  route_table_id = aws_route_table.prod-route-table.id
}

# 6. Create Security Group to allow port 22,00,443

resource "aws_security_group" "allow_web" {
  name        = "allow_web_traffic"
  description = "Allow web inbound traffic"
  vpc_id      = aws_vpc.prod-vpc.id

  ingress {
    description = "HTTPS"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    description = "HTTP"
    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"]
  }

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

  tags = {
    Name = "allow_web"
  }
}

# 7. Create a network interface with an ip in the subnet that was created in step 4

resource "aws_network_interface" "web-server-nic" {
  subnet_id       = aws_subnet.subnet-1.id
  private_ips     = ["10.0.1.50"]
  security_groups = [aws_security_group.allow_web.id]

}

# 8. Assign an elastic IP to the network interface created in step 7 

resource "aws_eip" "one" {
  vpc = true
  network_interface = aws_network_interface.web-server-nic.id
  associate_with_private_ip = "10.0.1.50"
  depends_on = [ aws_internet_gateway.gw ]
}

# 9. Create Ubuntu server and install/enable apache2 

resource "aws_instance" "web-server-instance" {
  ami = "" #Add ami 
  instance_type = "t2.micro"
  availability_zone = ""
  key_name = "" //enter your key pair that you created

  network_interface {
    device_index = 0
    network_interface_id = aws_network_interface.web-server-nic.id
  }
  user_data = <<-EOF
              #!/bin/bash
              sudo apt update -y
              sudo apt install apache2 -y
              sudo systemctl start apache2
              sudo bash -c 'echo your very first web server > /var/www/html/index.html'
              EOF

  tags = {
    Name = "Web-Server"
  }
}

📄 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/UjjwalMahar/AWS-Terraform-Project