Skip to content

Problem Statement 5 || Solution

Nishant-opstree edited this page Jul 23, 2021 · 5 revisions

Solution || Day 5

Directory Structure:

Terraform documentation for AWS provider

Step 1 : Create a terraform directory

mkdir terraform
cd terraform

Step 2 : Create module and wrapper code for creating subnets.

2.1 : Create module directory for subnets

touch main.tf vars.tf provider.tf output.tf backend.tf
mkdir subnet
touch subnet/main.tf subnet/output.tf subnet/vars.tf
  • main.tf : This file will contain the wrapper code for creating the complete architecture with the help of modules.

  • backend.tf : This defines where the terraform state will be stored loclally or remotely in S3.

  • vars.tf : This file contains the parameters for arguments used in the wrapper code(main.tf)

  • output.tf : In this file we are mentioning the output which act as the return value for the terraform module.

  • provider.tf : It contains the providers section that is the most important part of terraform code as terraform relies on plugins called "providers" to interact with cloud providers, SaaS providers, and other APIs.

  • subnet/main.tf : This files contain the main code for creating resource.

  • subnet/vars.tf : This file contains variable required for creating resources.

  • subnet/output.tf : In this file we keep outputs that will be used by other modules for refrencing other modules.

2.2: Create main.tf

module "aws-module-vpc" {
  source           = "./modules/VPC"
  instance_tenancy = "default"
  dns_support      = true
  dns_hostnames    = false

}

module "aws-module-subnet" {
  source           = "./modules/Subnet"
  subnet1_cidr     = var.subnet_cidr-1
  subnet2_cidr     = var.subnet_cidr-2
  subnet1_cidr-AZ2 = var.subnet_cidr-AZ1
  subnet2_cidr-AZ2 = var.subnet_cidr-AZ2
  az_zone1         = var.az_zone1
  az_zone2         = var.az_zone2
  vpcid            = var.vpc_id
}

module "aws-module-NAT" {
  source    = "./modules/NAT"
  subnet2id = module.aws-module-subnet.subnet-2
  rtID      = module.aws-module-RouteTable.rtTableid
}

module "aws-module-RouteTable" {
  source        = "./modules/Route_Table"
  VPCid         = var.vpc_id
  subnet1id     = module.aws-module-subnet.subnet-1
  subnet1-AZ2id = module.aws-module-subnet.subnet-1-AZ2
  subnet2id     = module.aws-module-subnet.subnet-2
  subnet2-AZ2id = module.aws-module-subnet.subnet-2-AZ2
  igw_id        = var.igw_id
  nat_id        = module.aws-module-NAT.ngw-id
}

module "aws-module-SG" {
  source         = "./modules/Security-Group"
  whitelisted-ip = ["0.0.0.0/0"]
  vpc-Id         = var.vpc_id
}

module "aws-module-alb" {
  source        = "./modules/alb"
  ALB           = "aws-ALB"
  external      = false
  vpc-ID        = var.vpc_id    
  alb1-sgid     = module.aws-module-SG.alb1SG
  subnet2id     = module.aws-module-subnet.subnet-2
  subnet2-AZ2id = module.aws-module-subnet.subnet-2-AZ2
}

module "aws-module-BastionHost" {
  source              = "./modules/BastionHost"
  ami                 = var.ami
  instance_type       = var.instance_type
  associate_public_ip = true
  key_name            = var.key_name
  bastion_sg          = var.bastion_sg
  vpc-id              = var.vpc_id
  subnet2id           = module.aws-module-subnet.subnet-2
}

module "aws-module-frontend" {
  source              = "./modules/frontend-server"
  ami                 = var.image_id_frontend
  instance_type       = var.instance_type
  associate_public_ip = true
  key_name            = var.key_name
  frontend_sg         = "Frontend_Security_Group"
  vpc-id              = var.vpc_id
  subnetid1           = module.aws-module-subnet.subnet-1-AZ2
  subnetid2           = module.aws-module-subnet.subnet-2-AZ2
  target_group_arn    = module.aws-module-alb.target-group-external-arn
}

module "aws-module-salary" {
  source              = "./modules/salary-server"
  ami                 = var.image_id_salary
  instance_type       = var.instance_type
  associate_public_ip = true
  key_name            = var.key_name
  salary_sg           = "salary_Security_Group"
  vpc-id              = var.vpc_id
  subnetid1           = module.aws-module-subnet.subnet-1-AZ2
  subnetid2           = module.aws-module-subnet.subnet-2-AZ2
}

2.3: Create vars.tf

variable "vpc_id" {
  type        = string
  description = "VPC id"
  default     = "vpc-05bf0f5972db4885a"
}
variable "igw_id" {
  type        = string
  description = "IGW id"
  default     = "igw-0bf544e911106c10f"
}
variable "cidr" {
  type        = string
  description = "The CIDR block for the VPC"
  default     = "10.0.0.0/16"
}
variable "instance_tenancy" {
  type        = string
  description = "A tenancy option for instances launched into the VPC"
  default     = "default"
}
variable "tag" {
  type        = map(string)
  description = "A map of tags to assign to the resource"
  default = {
    "Name" = "VPC"
  }
}
variable "dns_support" {
  type        = bool
  description = "A boolean flag to enable/disable DNS support in the VPC"
  default     = true
}
variable "dns_hostnames" {
  type        = bool
  description = "A boolean flag to enable/disable DNS hostnames in the VPC"
  default     = false
}
variable "subnet_cidr-1" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.2.0/24"
}
variable "subnet_cidr-2" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.3.0/24"
}
variable "subnet_cidr-AZ1" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.4.0/24"
}
variable "subnet_cidr-AZ2" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.5.0/24"
}
variable "az_zone1" {
  type        = string
  description = "The availability zone for the subnet"
  default     = "us-east-2a"
}
variable "az_zone2" {
  type        = string
  description = "The availability zone for the subnet"
  default     = "us-east-2b"
}
variable "map_public_ip_on_launch" {
  type        = bool
  description = "It indicates that instances launched into the subnet should be assigned a public IP address. Setting it to 'true' will create public subnet"
  default     = false
}
variable "map_ip_on_launch" {
  type        = bool
  description = "It indicates that instances launched into the subnet should be assigned a public IP address. Setting it to 'true' will create public subnet"
  default     = true
}
variable "subnet_tag" {
  type        = map(string)
  description = "A map of tags to assign to the resource"
  default = {
    "Name" = "subnet"
  }
}
variable "alb" {
  type        = string
  description = "Name of the application load balancer"
  default     = "aws-alb"
}
variable "internal" {
  type        = bool
  description = "Specify whether ALB is internal or not"
  default     = true
}


variable "alb-sg" {
  type        = string
  description = "Name of the security group"
  default     = "alb-sg"
}
variable "whitelisted-ip" {
  type        = list(string)
  description = "IP address allowed to access ALB at port 80"
  default     = ["0.0.0.0/0"]
}
variable "launch-template" {
  type        = string
  description = "Name of launch template"
  default     = "ec2-template"
}
variable "iam_instance_profile_arn" {
  type        = string
  description = "Name of instance profile"
  default     = "test"
}
variable "image_id_frontend" {
  type        = string
  description = "Image Id"
  default     = "ami-0233c2d874b811deb"
}
variable "instance_type" {
  type        = string
  description = "type of instance"
  default     = "t2.micro"
}
variable "key_name" {
  type        = string
  description = "key name"
  default     = "test"
}
variable "vpc_security_group_ids" {
  type        = string
  description = "Image Id"
  default     = "test"
}
variable "user_data" {
  type        = string
  description = "Image Id"
  default     = "test"
}
variable "ALB" {
  type        = string
  description = "Name of external ALB"
  default     = "aws-ALB"
}
variable "external" {
  type        = bool
  description = "Specify whether ALB is internal or not"
  default     = false
}
variable "tags" {
  type        = map(string)
  description = "Tags given to the Route Table"
  default = {
    "Name" = "Route-Table-public"
  }
}
variable "tag-private" {
  type        = map(string)
  description = "Tags given to the Route Table"
  default = {
    "Name" = "Route-Table-private"
  }
}
variable "salary-launch-template" {
  type        = string
  description = "Name of launch template(salary)"
  default     = "ec2-template-salary"
}
variable "key_name_Salary" {
  type        = string
  description = "key name"
  default     = "test" #This key pair must exist in AWS
}
variable "instance_type_salary" {
  type        = string
  description = "type of instance"
  default     = "t2.micro"
}
variable "image_id_salary" {
  type        = string
  description = "Image Id"
  default     = "ami-0233c2d874b811deb"
}
variable "sg-alb-frontend" {
  type        = string
  description = "Name of the security group"
  default     = "sg_alb_frontend"
}
variable "bashion-key" {
  type        = string
  description = "key name"
  default     = "test"
}
variable "ami" {
  type        = string
  description = "Bashion AMI"
  default     = "ami-0233c2d874b811deb"
}
variable "bastion_sg" {
  type        = string
  description = "Name of Security Group for bastion host"
  default     = "Bastion_Security_Group"
}
variable "private_instance_sg" {
  type        = string
  description = "Name of Security Group for private instances"
  default     = "Private_Instance_Security_Group"
}
variable "associate_public_ip" {
  type        = bool
  description = "Specifies whether to associate public IP or not"
  default     = true
}

2.4: Create output.tf

2.5: Create provider.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "3.50.0"
    }
  }
}

provider "aws" {
  region  = "us-east-2"
  profile = "default"
}

2.6: Create backend.tf

terraform {
  backend "s3" {
    bucket       = "glcloud-terraform-state-bucket"
    key          = "modules/terraform.tfstate"
    region       = "us-east-1"
  }
}

2.6: Create subnet/main.tf

#Private subnet in AZ1
resource "aws_subnet" "subnet1" {
  vpc_id                  = var.vpcid
  cidr_block              = var.subnet1_cidr
  availability_zone       = var.az_zone1
  map_public_ip_on_launch = var.map_public_ip_on_launch
  tags                    = var.subnet_tag
}

#Public subnet in AZ1
resource "aws_subnet" "subnet2" {
  vpc_id                  = var.vpcid
  cidr_block              = var.subnet2_cidr
  availability_zone       = var.az_zone1
  map_public_ip_on_launch = var.map_ip_on_launch
  tags                    = var.subnet_tag
}

#Private subnet in AZ2
resource "aws_subnet" "subnet1-AZ2" {
  vpc_id                  = var.vpcid
  cidr_block              = var.subnet1_cidr-AZ2
  availability_zone       = var.az_zone2
  map_public_ip_on_launch = var.map_public_ip_on_launch
  tags                    = var.subnet_tag
}

#Public subnet in AZ2
resource "aws_subnet" "subnet2-AZ2" {
  vpc_id                  = var.vpcid
  cidr_block              = var.subnet2_cidr-AZ2
  availability_zone       = var.az_zone2
  map_public_ip_on_launch = var.map_ip_on_launch
  tags                    = var.subnet_tag
}

2.7: Create subnet/vars.tf

variable "subnet1_cidr" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.1.0/24"
}
variable "az_zone1" {
  type        = string
  description = "The availability zone for the subnet"
  default     = "us-east-2a"
}
variable "map_ip_on_launch" {
  type        = bool
  description = "It indicates that instances launched into the subnet should be assigned a public IP address. Setting it to 'true' will create public subnet"
  default     = true
}
variable "subnet_tag" {
  type        = map(string)
  description = "A map of tags to assign to the resource"
  default = {
    "Name" = "subnet"
  }
}
variable "subnet1_cidr-AZ2" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.3.0/24"
}
variable "az_zone2" {
  type        = string
  description = "The availability zone for the subnet"
  default     = "us-east-2b"
}
variable "map_public_ip_on_launch" {
  type        = bool
  description = "It indicates that instances launched into the subnet should be assigned a public IP address. Setting it to 'true' will create public subnet"
  default     = false
}
variable "subnet2_cidr-AZ2" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.4.0/24"
}
variable "vpcid" {
  default = null
}
variable "subnet2_cidr" {
  type        = string
  description = "The CIDR block for the subnet"
  default     = "10.0.2.0/24"
}

2.8: Create subnet/output.tf

output "subnet-1" {
  value = aws_subnet.subnet1.id
}
output "subnet-2" {
  value = aws_subnet.subnet2.id
}
output "subnet-1-AZ2" {
  value = aws_subnet.subnet1-AZ2.id
}
output "subnet-2-AZ2" {
  value = aws_subnet.subnet2-AZ2.id
}

Step 3 : Create route tables

[ Note: As we have already created provider.tf, output.tf, main.tf and vars.tf, we will just use them from now on as wrapper code]

3.1: Create directory for Route_Table

mkdir Route_Table
touch Route_Table/main.tf Route_Table/output.tf Route_Table/vars.tf

3.2: Create Route_Table/main.tf

#Public Route Table
resource "aws_route_table" "route_table" {
  vpc_id = var.VPCid
  tags   = var.tags
}

resource "aws_route_table_association" "rt-association" {
  route_table_id = aws_route_table.route_table.id
  subnet_id      = var.subnet2-AZ2id
}

resource "aws_route_table_association" "rt-Association" {
  route_table_id = aws_route_table.route_table.id
  subnet_id      = var.subnet2id
}

resource "aws_route" "routing-igw" {
  route_table_id         = aws_route_table.route_table.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = var.igw_id
}

#Private Route Table
resource "aws_route_table" "route_table-pvt" {
  vpc_id = var.VPCid
  tags   = var.tag-private
}

resource "aws_route_table_association" "rt-association-pvt" {
  route_table_id = aws_route_table.route_table-pvt.id
  subnet_id      = var.subnet1id
}

resource "aws_route_table_association" "rt-Association-pvt1" {
  route_table_id = aws_route_table.route_table-pvt.id
  subnet_id      = var.subnet1-AZ2id
}

resource "aws_route" "route" {
  route_table_id         = aws_route_table.route_table-pvt.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = var.nat_id
}

3.3: Create Route_Table/vars.tf

variable "VPCid" {
  default = null
}
variable "tags" {
  type = map(string)
  default = {
    "Route-Table" = "Private"
  }
}
variable "subnet1id" {
  default = null
}
variable "subnet1-AZ2id" {
  default = null
}
variable "tag-private" {
  type = map(string)
  default = {
    "RouteTable" = "Private"
  }
}
variable "subnet2id" {
  type    = string
  default = null
}
variable "subnet2-AZ2id" {
  type    = string
  default = null
}

variable "nat_id" {
  default = ""
}

variable "igw_id" {
  default = ""
}

3.4: Create Route_Table/output.tf

output "rtTableid" {
  value = aws_route_table.route_table-pvt.id
}
output "rtTableid-pub" {
  value = aws_route_table.route_table.id
}

Step 4 : Create NAT gateway

4.1: Create directory for NAT

mkdir NAT
touch NAT/main.tf NAT/output.tf NAT/vars.tf

4.2: Create NAT/main.tf

resource "aws_eip" "eip" {
  vpc = true
}

resource "aws_nat_gateway" "ngw" {
  subnet_id     = var.subnet2id
  allocation_id = aws_eip.eip.id
}

resource "aws_route" "route" {
  route_table_id         = var.rtID
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.ngw.id
}

4.3: Create NAT/vars.tf

variable "subnet2id" {
  default = null
}
variable "rtID" {
  default = null
}

4.4: Create NAT/output.tf

output "ngw-id" {
  value = aws_nat_gateway.ngw.id
}

Step 5 : Create security group

5.1: Create directory for Security-Group/

mkdir Security-Group
touch Security-Group/main.tf Security-Group/output.tf Security-Group/vars.tf

5.2: Create Security-Group/main.tf

resource "aws_security_group" "alb1-sg" {
  name   = "alb1-sg"
  vpc_id = var.vpc-Id

  ingress {
    description = "Opening port 80 for a single ip"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = var.whitelisted-ip
  }

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

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

resource "aws_security_group" "asg1-sg" {
  name   = "asg1-sg"
  vpc_id = var.vpc-Id

  ingress {
    description     = "Opening port 80 for a single ip"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb1-sg.id]
  }

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

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


resource "aws_security_group" "alb2-sg" {
  name   = "alb2-sg"
  vpc_id = var.vpc-Id

  ingress {
    description     = "Opening port 80 for a single ip"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.asg1-sg.id]
  }

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

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


resource "aws_security_group" "asg2-sg" {
  name   = "asg2-sg"
  vpc_id = var.vpc-Id

  ingress {
    description     = "Opening port 80 for a single ip"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb2-sg.id]
  }

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

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

5.3: Create Security-Group/vars.tf

variable "whitelisted-ip" {
  type        = list(string)
  description = "IP address allowed to access ALB at port 80"
  default     = ["0.0.0.0/0"]
}
variable "vpc-Id" {
  type    = string
  default = null
}

5.4: Create Security-Group/output.tf

output "alb1SG" {
  value = aws_security_group.alb1-sg.id
}
output "asg1Sg" {
  value = aws_security_group.asg1-sg.id
}
output "alb2Sg" {
  value = aws_security_group.alb2-sg.id
}
output "asg2Sg" {
  value = aws_security_group.asg2-sg.id
}

Step 6 : Create application bation server

6.1: Create directory for BastionHost/

mkdir BastionHost
touch BastionHost/main.tf BastionHost/output.tf BastionHost/vars.tf

6.2: Create BastionHost/main.tf

resource "aws_instance" "bastion" {
  ami                         = var.ami
  instance_type               = var.instance_type
  associate_public_ip_address = var.associate_public_ip
  subnet_id                   = var.subnet2id
  key_name                    = var.key_name
  vpc_security_group_ids      = [aws_security_group.bastion_sg.id]
  tags = {
    Name = "Bastion_Host"
  }
}

#Bashion seccurity group
resource "aws_security_group" "bastion_sg" {
  name   = var.bastion_sg
  vpc_id = var.vpc-id
  ingress {
    description = "Opening port 22 for bastion host"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] #Allowed for all IP ranges
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name = "bastion_sg"
  }
}

6.3: Create BastionHost/vars.tf

variable "ami" {
  type        = string
  description = "Bashion AMI"
  default     = "ami-0d382e80be7ffdae5"
}
variable "instance_type" {
  type        = string
  description = "type of instance"
  default     = "t2.micro"
}
variable "associate_public_ip" {
  type        = bool
  description = "Specifies whether to associate public IP or not"
  default     = true
}
variable "key_name" {
  type        = string
  description = "key name"
  default     = "test"
}
variable "bastion_sg" {
  type        = string
  description = "Name of Security Group for bastion host"
  default     = "Bastion_Security_Group"
}
variable "vpc-id" {
  type    = string
  default = null
}
variable "subnet2id" {
  type    = string
  default = null
}

Step 7 : Create 2 Frontend Ec2 servers

7.1: Create directory for frontend-server/

mkdir frontend-server
touch frontend-server/main.tf frontend-server/output.tf frontend-server/vars.tf

7.2: Create frontend-server/main.tf

#Frontend server security group
resource "aws_security_group" "frontend_sg" {
  name   = var.frontend_sg
  vpc_id = var.vpc-id
  ingress {
    description = "Opening port 22 for frontend host"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] #Allowed for all IP ranges
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name = "frontend_sg"
  }
}

#Frontend server
resource "aws_instance" "frontend-1" {
  ami                         = var.ami
  instance_type               = var.instance_type
  associate_public_ip_address = var.associate_public_ip
  subnet_id                   = var.subnetid1
  key_name                    = var.key_name
  vpc_security_group_ids      = [aws_security_group.frontend_sg.id]
  tags = {
    Name = "frontend_Host"
  }
}

resource "aws_lb_target_group_attachment" "frontend-1-tg-attachment" {
  target_group_arn = var.target_group_arn
  target_id        = aws_instance.frontend-1.id
  port             = 80
}

resource "aws_instance" "frontend-2" {
  ami                         = var.ami
  instance_type               = var.instance_type
  associate_public_ip_address = var.associate_public_ip
  subnet_id                   = var.subnetid2
  key_name                    = var.key_name
  vpc_security_group_ids      = [aws_security_group.frontend_sg.id]
  tags = {
    Name = "frontend_Host"
  }
}

resource "aws_lb_target_group_attachment" "frontend-2-tg-attachment" {
  target_group_arn = var.target_group_arn
  target_id        = aws_instance.frontend-2.id
  port             = 80
}

7.3: Create frontend-server/vars.tf

variable "ami" {
  type        = string
  description = "frontend AMI"
  default     = "ami-0d382e80be7ffdae5"
}
variable "instance_type" {
  type        = string
  description = "type of instance"
  default     = "t2.micro"
}
variable "associate_public_ip" {
  type        = bool
  description = "Specifies whether to associate public IP or not"
  default     = true
}
variable "key_name" {
  type        = string
  description = "key name"
  default     = "test"
}
variable "frontend_sg" {
  type        = string
  description = "Name of Security Group for frontend host"
  default     = "frontend_Security_Group"
}
variable "vpc-id" {
  type    = string
  default = null
}
variable "subnetid1" {
  type    = string
  default = null
}
variable "subnetid2" {
  type    = string
  default = null
}
variable "target_group_arn" {
  type    = string
  default = ""
}

Step 8 : Create 2 Salary Ec2 servers

8.1: Create directory for salary-server/

mkdir salary-server
touch salary-server/main.tf salary-server/output.tf salary-server/vars.tf

8.2: Create salary-server/main.tf

#salary server security group
resource "aws_security_group" "salary_sg" {
  name   = var.salary_sg
  vpc_id = var.vpc-id
  ingress {
    description = "Opening port 22 for salary host"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] #Allowed for all IP ranges
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name = "salary_sg"
  }
}

#salary server
resource "aws_instance" "salary-1" {
  ami                         = var.ami
  instance_type               = var.instance_type
  associate_public_ip_address = var.associate_public_ip
  subnet_id                   = var.subnetid1
  key_name                    = var.key_name
  vpc_security_group_ids      = [aws_security_group.salary_sg.id]
  tags = {
    Name = "salary_Host"
  }
}
resource "aws_instance" "salary-2" {
  ami                         = var.ami
  instance_type               = var.instance_type
  associate_public_ip_address = var.associate_public_ip
  subnet_id                   = var.subnetid2
  key_name                    = var.key_name
  vpc_security_group_ids      = [aws_security_group.salary_sg.id]
  tags = {
    Name = "salary_Host"
  }
}

8.3: Create salary-server/vars.tf

variable "ami" {
  type        = string
  description = "salary AMI"
  default     = "ami-0d382e80be7ffdae5"
}
variable "instance_type" {
  type        = string
  description = "type of instance"
  default     = "t2.micro"
}
variable "associate_public_ip" {
  type        = bool
  description = "Specifies whether to associate public IP or not"
  default     = true
}
variable "key_name" {
  type        = string
  description = "key name"
  default     = "test"
}
variable "salary_sg" {
  type        = string
  description = "Name of Security Group for salary host"
  default     = "salary_Security_Group"
}
variable "vpc-id" {
  type    = string
  default = null
}
variable "subnetid1" {
  type    = string
  default = null
}
variable "subnetid2" {
  type    = string
  default = null
}

Step 9 : Create application load balancer and target groups

9.1: Create directory for ALB/

mkdir ALB
touch ALB/main.tf ALB/output.tf ALB/vars.tf

9.2: Create ALB/main.tf

# External ALB
resource "aws_lb" "ALB" {
  name               = var.ALB
  internal           = var.external
  load_balancer_type = "application"
  security_groups    = [var.alb1-sgid]
  subnets            = [var.subnet2id, var.subnet2-AZ2id]

  enable_deletion_protection = false

  tags = {
    Environment = "External"
  }
}

resource "aws_lb_listener" "listener" {
  load_balancer_arn = aws_lb.ALB.arn
  port              = "80"
  protocol          = "HTTP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.alb-targetGroup.arn
  }
}


#ALB Target Group
resource "aws_lb_target_group" "alb-targetGroup" {
  vpc_id           = var.vpc-ID
  port             = 80
  protocol_version = "HTTP1"
  protocol         = "HTTP"
  target_type      = "instance"
  health_check {
    unhealthy_threshold = 2
    healthy_threshold   = 2
    timeout             = 3
    interval            = 30
    path                = "/"
    protocol            = "HTTP"
  }
  tags = {
    Name = "tf_target_group"
  }
}

9.3: Create ALB/vars.tf

variable "ALB" {
  type        = string
  description = "Name of external ALB"
  default     = "aws-ALB"
}
variable "external" {
  type        = bool
  description = "Specify whether ALB is internal or not"
  default     = false
}
variable "alb" {
  type        = string
  description = "Name of the application load balancer"
  default     = "aws-alb"
}
variable "vpc-ID" {
  type    = string
  default = null
}

variable "alb1-sgid" {
  type    = string
  default = null
}
variable "subnet2id" {
  type    = string
  default = null
}
variable "subnet2-AZ2id" {
  type    = string
  default = null
}

9.4: Create ALB/output.tf

output "external_lb" {
  value = aws_lb.ALB.id
}
output "external_lb_arn" {
  value = aws_lb.ALB.arn
}

output "target-group-external" {
  value = aws_lb_target_group.alb-targetGroup.id
}

output "target-group-external-arn" {
  value = aws_lb_target_group.alb-targetGroup.arn
}

Step 10 : Execute terraform code

10.1: Initialize terraform directory

terraform init

10.2: Lint and format terraform code

terraform fmt #Optional

10.3: Validate terraform code for syntax check and correct refrencing of resources.

terraform validate #Optional

10.4: Get execution plan

terraform plan

10.5: Execute terraform code

terraform apply

You can validate the creation of resources by visiting the AWS console

10.6: Cleanup resources created by terraform code

terraform destroy

Step 11 : Automating resource setup using terraform IAC code

11.1: Create Jenkinsfile file in your terraform directory

cd terraform/
touch Jenkinsfile

11.2: Create Jenkinsfile

pipeline {
    agent 'any'
    stages {
        stage('Initialize Terraform') {
            steps {
                script {
                    try {
                        sh "terraform init"
                    }
                    catch(Exception e) {
                        def buildStatus = 'ERROR'
                        def colorName = 'RED'
                        def colorCode = '#FF0000'
                        def subject = "ENVIRONMENT: QA \nJOB_NAME: ${env.JOB_NAME} \nBUILD_ID: ${env.BUILD_NUMBER} \nBUILD_STATUS: ${buildStatus}"
                        def summary = "Terraform build was not successful \nBUILD_URL: ${env.BUILD_URL}"
                        if (buildStatus == 'STARTED') {
                            color = 'YELLOW'
                            colorCode = '#FFFF00'
                        } else if (buildStatus == 'SUCCESSFUL') {
                            color = 'GREEN'
                            colorCode = '#00FF00'
                        } else {
                            color = 'RED'
                            colorCode = '#FF0000'
                        }
                        slackSend (channel: "test-notification", color: colorCode, message: summary)
                        throw e
                    }
                }
            }
        }

        stage('Lint terragrunt') {
            steps {
                script {
                    try {
                        sh "terraform fmt -list=true -write=false -diff=true"
                    }
                    catch(Exception e) {
                        def buildStatus = 'ERROR'
                        def colorName = 'RED'
                        def colorCode = '#FF0000'
                        def subject = "ENVIRONMENT: QA \nJOB_NAME: ${env.JOB_NAME} \nBUILD_ID: ${env.BUILD_NUMBER} \nBUILD_STATUS: ${buildStatus}"
                        def summary = "Terraform build was not successful \nBUILD_URL: ${env.BUILD_URL}"
                        if (buildStatus == 'STARTED') {
                            color = 'YELLOW'
                            colorCode = '#FFFF00'
                        } else if (buildStatus == 'SUCCESSFUL') {
                            color = 'GREEN'
                            colorCode = '#00FF00'
                        } else {
                            color = 'RED'
                            colorCode = '#FF0000'
                        }
                        slackSend (channel: "test-notification", color: colorCode, message: summary)
                        throw e
                    }
                }
            }
        }


        stage('Plan Terraform') {
            steps {
                script {
                    try {
                        sh "terraform plan"
                    }
                    catch(Exception e) {
                        def buildStatus = 'ERROR'
                        def colorName = 'RED'
                        def colorCode = '#FF0000'
                        def subject = "ENVIRONMENT: QA \nJOB_NAME: ${env.JOB_NAME} \nBUILD_ID: ${env.BUILD_NUMBER} \nBUILD_STATUS: ${buildStatus}"
                        def summary = "Terraform build was not successful \nBUILD_URL: ${env.BUILD_URL}"
                        if (buildStatus == 'STARTED') {
                            color = 'YELLOW'
                            colorCode = '#FFFF00'
                        } else if (buildStatus == 'SUCCESSFUL') {
                            color = 'GREEN'
                            colorCode = '#00FF00'
                        } else {
                            color = 'RED'
                            colorCode = '#FF0000'
                        }
                        slackSend (channel: "test-notification", color: colorCode, message: summary)
                        throw e
                    }
                }
            }
        }

        stage('Conformation to Setup instance') {
            steps {
                input 'Please check the creation of resources in previous stage and confirm configurations to setup AWS instance / instances:'
            }
        }

        stage('Execute Terraform') {
            steps {
                script {
                    try {
                        sh "terraform apply --auto-approve"
                    }
                    catch(Exception e) {
                        def buildStatus = 'ERROR'
                        def colorName = 'RED'
                        def colorCode = '#FF0000'
                        def subject = "ENVIRONMENT: QA \nJOB_NAME: ${env.JOB_NAME} \nBUILD_ID: ${env.BUILD_NUMBER} \nBUILD_STATUS: ${buildStatus}"
                        def summary = "Terraform build was not successful \nBUILD_URL: ${env.BUILD_URL}"
                        if (buildStatus == 'STARTED') {
                            color = 'YELLOW'
                            colorCode = '#FFFF00'
                        } else if (buildStatus == 'SUCCESSFUL') {
                            color = 'GREEN'
                            colorCode = '#00FF00'
                        } else {
                            color = 'RED'
                            colorCode = '#FF0000'
                        }
                        slackSend (channel: "test-notification", color: colorCode, message: summary)
                        throw e
                    }
                }
            }
        }
        
        stage('Successful Notification') {
            steps {
                script {
                    try {
                        echo "Successfully executed the pipeline"
                        def buildStatus = 'SUCCESSFUL'
                        def colorName = 'RED'
                        def colorCode = '#FF0000'
                        def subject = "ENVIRONMENT: QA \nJOB_NAME: ${env.JOB_NAME} \nBUILD_ID: ${env.BUILD_NUMBER} \nBUILD_STATUS: ${buildStatus}"
                        def summary = "Terraform build was successful \nBUILD_URL: ${env.BUILD_URL}"
                        if (buildStatus == 'STARTED') {
                            color = 'YELLOW'
                            colorCode = '#FFFF00'
                        } else if (buildStatus == 'SUCCESSFUL') {
                            color = 'GREEN'
                            colorCode = '#00FF00'
                        } else {
                            color = 'RED'
                            colorCode = '#FF0000'
                        }
                        slackSend (channel: "test-notification", color: colorCode, message: summary)
                    }
                    catch(Exception e) {
                        throw e
                    }
                }
            }
        }
    }
}

11.3: Go to jenkins and now create a pipeline job named Terraform-Architecture-CI

11.4: Configure the Jenkins job

11.5: Execute the Jenkins job

Clone this wiki locally