HA Environment on AWS Cloud Using Terraform

Introduction

Terraform is a tool for building and managing infrastructure in an organized way. It can be used to manage a large variety of services offered by all the cloud solution providers.

In this tutorial you will deploy a secure and highly available environment with Terraform on AWS cloud  using single-tier architecture with public subnet. The servers and Elastic Load Balancer will span multiple availability zones to achieve high availability.

Prerequisites

  1. One Ubuntu 18.04 server with a sudo non-root user.
  2. An AWS account access (programmatic access) with sufficient privileges.

Procedure

Step 1 – Installing terraform

 Initially, download the appropriate package for your OS and architecture from the official terraform Downloads page and save it to the ~/Downloads directory.

$ curl https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_amd64.zip -o ~/Downloads/terraform.zip

Now, extract Terraform

$ unzip ~/Downloads/terraform.zip

In a terminal, run:

$ mv ~/Downloads/terraform /usr/local/bin/

To verify that you have installed Terraform correctly, let’s try and run it. In a terminal, run Terraform:

$ terraform

Now the Terraform is installed.

Step 2 – Configure AWS Credentials

The AWS provider offers a flexible means of providing credentials for authentication. Refer the document for more details. Here we are using the basic method. Create a file named main.tf and add the aws credentials in it.

provider "aws" {
 region     = "us-west-2"
 access_key = "my-access-key"
 secret_key = "my-secret-key"
}

Replace “my-access-key” and “my-secret-key” with your own access key and secret key.

Step 3 – Get to know and create the AWS resources

After configuring the AWS credentials, let’s create the terraform configuration files to launch each AWS resource. The main components are

  • 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
  • Internet Gateway: VPC component that allows communication between instances in your VPC and the Internet.
  • Subnet: subnet is a logical subdivision of an IP network.
  • 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.
  • Security Group: A security group acts as a virtual firewall for your EC2 instances to control incoming and outgoing traffic.
  • ELB: Elastic Load Balancing automatically distributes incoming application traffic across multiple targets.
  • Launch Configuration: A launch configuration is a template that an EC2 Auto Scaling group uses to launch EC2 instances
  • Autoscaling Group: AWS Auto Scaling lets you build scaling plans that automate how groups of different resources respond to changes in demand.

The terraform configuration file to launch a basic aws environment is provided below. Initially, Create a folder in your home directory then copy the configuration given below to a file named  main.tf.

provider "aws" {
  region     = "us-east-1"
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
}
resource "aws_vpc" "test-vpc-01" {
    cidr_block           = "10.0.0.0/16"
    enable_dns_hostnames = true
    enable_dns_support   = true
    instance_tenancy     = "default"
    tags = {
        Environment = "test"
        Name = "test-vpc-01"
    }
}
resource "aws_internet_gateway" "test-igw-01" {
    vpc_id = "${aws_vpc.test-vpc-01.id}"
    tags = {
        Environment = "test"
        Name = "test-igw-01"
    }
}
resource "aws_subnet" "test-pub-subnet-01" {
    vpc_id                  = "${aws_vpc.test-vpc-01.id}"
    cidr_block              = "10.0.10.0/24"
    availability_zone       = "us-east-1d
    map_public_ip_on_launch = false
    tags = {
        Name = "test-subnet-01"
    }
}
resource "aws_route_table" "test-pub-rt-01" {
    vpc_id     = "${aws_vpc.test-vpc-01.id}"
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.test-igw-01.id}"
    }
    tags = {
        Name = "test-pub-rt-01"
    }
}
resource "aws_route_table_association" "test-rt-association-01" {
  subnet_id      = "${aws_subnet.test-pub-subnet-01.id}"
  route_table_id = "${aws_route_table.test-pub-rt-01.id}"
}
resource "aws_security_group" "test-autoscaling-sg-01" {
    name        = "test-autoscaling-sg-01"
    description = "AutoScaling-Security-Group-1"
    vpc_id      = "${aws_vpc.test-vpc-01.id}
    ingress {
        from_port       = 0
        to_port         = -1
        protocol        = "icmp"
        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_launch_configuration" "test-lc-01" {
    name                        = "test-lc-01"
    image_id                    = "ami-0bcc094591f354be2"
    instance_type               = "t2.micro"
    key_name                    = "test-key"
    security_groups             = ["${aws_security_group.test-autoscaling-sg-01.id}"]
    enable_monitoring           = false
    ebs_optimized               = false
    root_block_device {
        volume_type           = "gp2"
        volume_size           = 8
        delete_on_termination = true
    }
}
resource "aws_elb" "test-lb-01" {
    name                        = "test-lb-01"
    subnets                     = ["${aws_subnet.test-pub-subnet-01.id}"]
    security_groups             = ["${aws_security_group.test-autoscaling-sg-01.id}"]
    instances                   = []
    cross_zone_load_balancing   = true
    idle_timeout                = 60
    connection_draining         = true
    connection_draining_timeout = 300
    internal                    = false
    listener {
        instance_port      = 80
        instance_protocol  = "http"
        lb_port            = 80
        lb_protocol        = "http"
        ssl_certificate_id = ""
    }
    health_check {
        healthy_threshold   = 10
        unhealthy_threshold = 2
        interval            = 30
        target              = "HTTP:80/index.html"
        timeout             = 5
    }
    tags = {
        Environment = "test"
        Name = "test-elb-01"
    }
}
resource "aws_autoscaling_group" "test-asg-01" {
    desired_capacity          = 1
    health_check_grace_period = 300
    health_check_type         = "EC2"
    launch_configuration      = "${aws_launch_configuration.test-lc-01.name}"
    max_size                  = 2
    min_size                  = 1
    name                      = "test-asg-01"
    vpc_zone_identifier       = ["${aws_subnet.test-pub-subnet-01.id}"]
    tag = [
      {
        key   = "Environment"
        value = "test"
        propagate_at_launch = true
      },
      {
        key   = "Name"
        value = "test-server-01"
        propagate_at_launch = true
      }
    ]
}
provider "aws" { region     = "us-east-1" access_key = "${var.access_key}" secret_key = "${var.secret_key}" } resource "aws_vpc" "test-vpc-01" {    cidr_block           = "10.0.0.0/16"    enable_dns_hostnames = true    enable_dns_support   = true    instance_tenancy     = "default"    tags = {        Environment = "test"        Name = "test-vpc-01"    } } resource "aws_internet_gateway" "test-igw-01" {    vpc_id = "${aws_vpc.test-vpc-01.id}"    tags = {        Environment = "test"        Name = "test-igw-01"    } } resource "aws_subnet" "test-pub-subnet-01" {    vpc_id                  = "${aws_vpc.test-vpc-01.id}"    cidr_block              = "10.0.10.0/24"    availability_zone       = "us-east-1d    map_public_ip_on_launch = false    tags = {        Name = "test-subnet-01"    } } resource "aws_route_table" "test-pub-rt-01" {     vpc_id     = "${aws_vpc.test-vpc-01.id}"    route {        cidr_block = "0.0.0.0/0"        gateway_id = "${aws_internet_gateway.test-igw-01.id}"    }    tags = {        Name = "test-pub-rt-01"    } } resource "aws_route_table_association" "test-rt-association-01" { subnet_id      = "${aws_subnet.test-pub-subnet-01.id}" route_table_id = "${aws_route_table.test-pub-rt-01.id}" } resource "aws_security_group" "test-autoscaling-sg-01" {    name        = "test-autoscaling-sg-01"    description = "AutoScaling-Security-Group-1"    vpc_id      = "${aws_vpc.test-vpc-01.id} ingress {        from_port       = 0        to_port         = -1        protocol        = "icmp"        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_launch_configuration" "test-lc-01" {    name                        = "test-lc-01"    image_id                    = "ami-0bcc094591f354be2"    instance_type               = "t2.micro"    key_name                    = "test-key"    security_groups             = ["${aws_security_group.test-autoscaling-sg-01.id}"]    enable_monitoring           = false    ebs_optimized               = false    root_block_device {        volume_type           = "gp2"        volume_size           = 8        delete_on_termination = true    } } resource "aws_elb" "test-lb-01" {    name                        = "test-lb-01"    subnets                     = ["${aws_subnet.test-pub-subnet-01.id}"]    security_groups             = ["${aws_security_group.test-autoscaling-sg-01.id}"]    instances                   = []    cross_zone_load_balancing   = true    idle_timeout                = 60    connection_draining         = true    connection_draining_timeout = 300    internal                    = false    listener {        instance_port      = 80        instance_protocol  = "http"        lb_port            = 80        lb_protocol        = "http"        ssl_certificate_id = ""    }    health_check {        healthy_threshold   = 10        unhealthy_threshold = 2        interval            = 30        target              = "HTTP:80/index.html"        timeout             = 5    }    tags = {        Environment = "test"        Name = "test-elb-01"    } } resource "aws_autoscaling_group" "test-asg-01" {    desired_capacity          = 1    health_check_grace_period = 300    health_check_type         = "EC2"    launch_configuration      = "${aws_launch_configuration.test-lc-01.name}"    max_size                  = 2    min_size                  = 1    name                      = "test-asg-01"    vpc_zone_identifier       = ["${aws_subnet.test-pub-subnet-01.id}"]    tag = [      {        key   = "Environment"        value = "test"        propagate_at_launch = true      },      {        key   = "Name"        value = "test-server-01"        propagate_at_launch = true      }    ] }

Step 4 – Launch Infrastructure

When you create a new configuration, you need to initialize the directory with terraform init. Run:

$ terraform init

To validate your configuration, run the following. If your configuration is valid, Terraform will return a success message.

$ terraform validate

Now in the same directory, run the following. This will launch your complete infrastructure.

$ terraform apply

After terraform execution check AWS console for created resources.

Step 5 – Destroy Infrastructure

To destroy the entire infrastructure, use terraform command itself.

$ terraform destroy

Step 6 – Terraform Modules

A module is a container for multiple resources that are used together. Modules can be used to create lightweight abstractions, so that you can describe your infrastructure in terms of its architecture, rather than directly in terms of physical objects. With reusable terraform modules, terraform infrastructure management is easier. To know more about terraform modules, refer: https://www.terraform.io/docs/modules/index.html

Conclusion

In this tutorial you used Terraform to build a load-balanced web infrastructure on DigitalOcean. The example setup is simple, but demonstrates how easy it is to automate the deployment of servers. Terraform has many more features, and can work with other providers. Check out the official Terraform Documentation to learn more about how you can use Terraform to improve your own infrastructure.

References

  1. https://www.terraform.io/intro/examples/aws.html
  2. https://www.terraform.io/docs/enterprise/before-installing/reference-architecture/aws.html

About The Author

HA Environment on AWS Cloud

Ancy Paul

Cloud DevOps Engineer | CloudControl

Cloud DevOps Engineer with 3+ years of experience in cloud infrastructure management, supporting, automating, and optimizing deployments to hybrid cloud platforms using DevOps processes, CI/CD, containers and Kubernetes in both Production and Development environments.