How I deployed a two tier Architecture using Terraform

How I deployed a two tier Architecture using Terraform

Use Case:

  • Set up 2 EC2 instances on AWS

  • Deploy an Nginx web server on these instances

  • Set up an ALB to route request to my private EC2 instances

  • Make sure each server displays its hostname

Note:

  • The tutor should not be able to access my web servers through IP address( must only be via a load balancer

  • Define a logical network and launch the instances on the logic network

  • Instances should not be assigned private IP

  • You may or may not set up autoscaling

  • You must submit a domain name from a domain provider or the ALB'S domain name

Here is my Solution

Using Terraform

Prerequisites

  • Git Bash/Windows powershell

  • A Linux image

  • Terraform CLI

  • AWS CLI

Definition of terms

  • AWS(Amazon Web Services): This is a cloud provider, they provide cloud solutions for your applications. They are majorly a public cloud provider. Click here to learn more about AWS.

  • LOGICAL NETWORK: This is also known as VPC(A virtual private cloud) is a virtual network dedicated to your AWS account. It is logically isolated from other virtual networks in the AWS Cloud. I will be walking you through how to set up a VPC for this project.

  • EC2 INSTANCES: EC2 stands for Elastic Compute Cloud, It is a virtual server for running applications in AWS infrastructure.

  • LOAD BALANCER: A load balancer serves as the single point of contact for clients. The load balancer distributes incoming application traffic across multiple targets, such as EC2 instances, in multiple Availability Zones. This increases the availability of your application.

  • PRIVATE INSTANCE: An EC2 Instance launched without assigning a public IP.

  • BOSTIAN HOST: A bastion host is a server whose purpose is to provide access to a private network from an external network, such as the Internet. It has a public IP assigned to it.

  • TERRAFORM: Terraform is an open-source service that allows you to build infrastructure as code to provision resources from any infrastructure provider. Terraform creates and manages resources on cloud platforms and other services through its application programming interfaces (APIs). Providers enable Terraform to work with virtually any platform or service with an accessible API. The core Terraform workflow consists of three stages:

Write: You define resources, which may be across multiple cloud providers and services. For example, you might create a configuration to deploy an application on virtual machines in a Virtual Private Cloud (VPC) network with security groups and a load balancer.

Plan: Terraform creates an execution plan describing the infrastructure it will create, update, or destroy based on the existing infrastructure and your configuration.

Apply: On approval, Terraform performs the proposed operations in the correct order, respecting any resource dependencies. For example, if you update the properties of a VPC and change the number of virtual machines in that VPC, Terraform will recreate the VPC before scaling the virtual machines.

Variables: Input variables let you customize aspects of Terraform modules without altering the module’s source code. This functionality allows you to share modules across different Terraform configurations, making your module composable and reusable.

Resources: A resource block describes one or more infrastructure objects, such as virtual networks, compute instances, or higher-level components such as DNS records. See documentation.

Step 1: Install Terraform CLI

  • Follow the steps on the link to install Terraform.

Step 2: Install AWS CLI

  • Follow the steps on the link

After installing the CLI, ensure you configure your IAM secret and access keys using the command below;

$ aws configure

Step 3: Create a VPC

STEP 1: DEFINE LOGICAL NETWORK (follow the steps below)

  • Login into your AWS account using an IAM user. (It is best practice to set up an IAM user for your account, Follow the steps in the link to set up one

  • Search for VPC, click on Create VPC

  • Select VPC and more: this will do the setup automatically, all you need to do is edit the name of the VPC and CIDR if you want to and click create. See photo for hint.

creatingvpc

  • We are going to be using the VPC credentials for our next step.

Step 4: Create variables.tf

Kindly see content of file below. The variables.tf file is written in HCL(Hashicorp configuration language) which is the language Terraform uses. I used the variable module to create variables for my VPC id, private and public subnets etc.

variable "vpc_id" {
  type        = string
  description = "my vpc id"
}

variable "good-subnet-private1-us-east-1a" {
  type        = string
  description = "Private Subnet for us-east-1a"
}

variable "good-subnet-private2-us-east-1b" {
  type        = string
  description = "Private Subnet for us-east-1b"
}

variable "pub_sub_id" {
  type        = list(string)
  description = "Public Subnets for us-east-1"

}
variable "instance_name" {
  type        = string
  description = "My first instance name"
}

variable "instance_name2" {
  type        = string
  description = "My second instance name"
}

variable "ami" {
  type        = string
  description = "amazon linux 2 ami"
}

variable "instance_type" {
  type        = string
  description = "instance type"
}
variable "priv_sub_id" {
  type        = list(string)
  description = "Private Subnets for us-east-1"
}

Step 5: Create terraform.tfvars

This file should contain value of your variables.

Step 6: Create main.tf

Kindly see content of file below. The main.tf file was written in HCL(Hashicorp configuration language). It contains configuration to do the following:

  • A security group for my vpc/load bal

  • A target group for the application load balancer

  • A load balancer

  • A launch template with a script to install nginx and print hostname

  • An Autoscaling group to provision two private instances using the launch template

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

# Configure the AWS Provider
provider "aws" {
  region = "us-east-1"
}


resource "aws_security_group" "my_alb_sg" {
  name        = "my_alb_sg"
  description = "Allow inbound traffic to instance"
  vpc_id      = var.vpc_id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

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

}

#Create an ALB target group

resource "aws_lb_target_group" "my-alb-tg" {
  name     = "my-alb-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = var.vpc_id
}

#Create Load Balancer

resource "aws_lb" "my-alb" {
  name               = "my-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.my_alb_sg.id]
  subnets            = var.pub_sub_id
}

resource "aws_lb_listener" "lb_lst" {
  load_balancer_arn = aws_lb.my-alb.arn
  port              = "80"
  protocol          = "HTTP"

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

data "template_file" "myuserdata" {
  template = <<-EOF
    #!/bin/bash
    sudo amazon-linux-extras install nginx1
    sudo service nginx start
    echo "<h1>I am pleased to welcome you to my page, I am $(hostname -f) by name</h1>" > /usr/share/nginx/html/index.html
    EOF
}

resource "aws_launch_template" "gladys_asg" {
  name_prefix   = "gladys_asg"
  image_id      = var.ami
  instance_type = var.instance_type
  user_data     = "${base64encode(data.template_file.myuserdata.rendered)}"
  tags = {
    Name = var.instance_name2
  }
}

resource "aws_autoscaling_group" "gladys_asg" {
  vpc_zone_identifier = var.priv_sub_id
  desired_capacity    = 2
  max_size            = 2
  min_size            = 1
  target_group_arns   = ["${aws_lb_target_group.my-alb-tg.arn}"]

  launch_template {
    id      = aws_launch_template.gladys_asg.id
    version = "$Latest"
  }
}

Step 7: Create outputs.tf

Kindly see file below. This code in this file will display my load balancer's arn and vpc_id in the terraform.state file and output the value as outputs at the end of terraform apply. See below

done

The other files seen .statetf, .hcl.lock are automatically created after the following commands are run:

Step 8: Initialize the configuration file

  • Use the command below;
$ terraform init

This command prepares the current working directory for terraform to run the configuration. See result below

Step 9: Validate your code

  • Use the command below;
$ terraform validate

The code is a lifesaver as it validates your configuration, pointing out any errors or unspecified attributes in your resource blocks. I got that so many times after running the code severally. Now our configuration is super valid.

Step 10: See the plan

  • Use the command below;
$ terraform plan

This shows a plan for every resource that is to be executed/deployed. It allows you to review the plan before executing the configuration.

Step 11: Apply the configuration

  • Use the command below;
$ terraform apply

This takes action on the terraform configuration to create the resources.

You can go over to AWS account to see your newly created resources.

Final output

I configured my domain name with route 53 and added the domain name as a rule to my load balancer. This is why you can see my domain name on the screenshot.

usingdomip

usingdomip2

Thanks for reading.