#2 Terraform configuration for given assignment

Suljettu
aashish-fiftyfive haluaa yhdistää 7 committia lähteestä aashish-fiftyfive/terraform kohteeseen LiveLike/main

+ 21 - 0
code_structure.txt

@@ -0,0 +1,21 @@
+.
+├── modules/
+│   ├── ec2/
+│   │   ├── outputs.tf
+│   │   ├── README.md
+│   │   ├── terraform.tf
+│   │   ├── userdata.sh
+│   │   └── variables.tf
+│   └── vpc/
+│       ├── outputs.tf
+│       ├── README.md
+│       ├── terraform.tf
+│       └── variables.tf
+├── .gitignore
+├── .terraform.lock.hcl
+├── outputs.tf
+├── provider.tf
+├── README.md
+├── terraform.tf
+├── terraform.tfvars
+└── variables.tf

+ 7 - 0
modules/ec2/README.md

@@ -0,0 +1,7 @@
+# EC2 Module Description
+
+* Create security group for load balancer and ec2 servers
+* Use `for_each` function for ingress / egress security group rules to add multiple rule config
+* Setup application load balancer with its listeners and target groups
+* Add autoscaling group with custom launch configuration
+* Provisioned servers will automatically add / remove from the target group

+ 21 - 0
modules/ec2/outputs.tf

@@ -0,0 +1,21 @@
+## Outputs
+
+output "alb_security_group_id" {
+  value = aws_security_group.alb_sg.id
+}
+
+output "ec2_security_group_id" {
+  value = aws_security_group.ec2_sg.id
+}
+
+output "alb_arn" {
+  value = aws_lb.alb_ec2.arn
+}
+
+output "alb_dns_name" {
+  value = aws_lb.alb_ec2.dns_name
+}
+
+output "autoscaling_group_arn" {
+  value = aws_autoscaling_group.ec2_autoscaling_group.arn
+}

+ 208 - 0
modules/ec2/terraform.tf

@@ -0,0 +1,208 @@
+## Security Group for ALB
+resource "aws_security_group" "alb_sg" {
+  name        = "alb-security-group"
+  description = "ALB security group to allow HTTP and HTTPS traffic"
+  vpc_id      = var.vpc_id
+
+  tags = var.tags
+}
+
+## Ingress Security Group Rule for ALB Security Group
+resource "aws_security_group_rule" "ingress_alb_sg_rule" {
+  for_each = var.ingress_alb_sg_rule
+
+  type                     = "ingress"
+  from_port                = each.value.from_port
+  to_port                  = each.value.to_port
+  protocol                 = each.value.protocol
+  cidr_blocks              = try(each.value.cidr_blocks, null)
+  source_security_group_id = try(each.value.source_security_group_id, null)
+  security_group_id        = aws_security_group.alb_sg.id
+}
+
+## Egress Security Group Rule for ALB Security Group
+resource "aws_security_group_rule" "egress_alb_sg_rule" {
+  for_each = var.egress_alb_sg_rule
+
+  type                     = "egress"
+  from_port                = each.value.from_port
+  to_port                  = each.value.to_port
+  protocol                 = each.value.protocol
+  cidr_blocks              = try(each.value.cidr_blocks, null)
+  source_security_group_id = try(each.value.cidr_blocks, null) == null ? try(each.value.source_security_group_id, aws_security_group.ec2_sg.id, null) : null
+  security_group_id        = aws_security_group.alb_sg.id
+}
+
+## Security Group for EC2
+resource "aws_security_group" "ec2_sg" {
+  name        = "ec2-security-group"
+  description = "EC2 security group to allow traffic only from ALB"
+  vpc_id      = var.vpc_id
+
+  tags = var.tags
+}
+
+## Ingress Security Group Rule for EC2 Security Group
+resource "aws_security_group_rule" "ingress_ec2_sg_rule" {
+  for_each = var.ingress_ec2_sg_rule
+
+  type                     = "ingress"
+  from_port                = each.value.from_port
+  to_port                  = each.value.to_port
+  protocol                 = each.value.protocol
+  cidr_blocks              = try(each.value.cidr_blocks, null)
+  source_security_group_id = try(each.value.cidr_blocks, null) == null ? try(each.value.source_security_group_id, aws_security_group.alb_sg.id, null) : null
+  security_group_id        = aws_security_group.ec2_sg.id
+}
+
+## Egress Security Group Rule for EC2 Security Group
+resource "aws_security_group_rule" "egress_ec2_sg_rule" {
+  for_each = var.egress_ec2_sg_rule
+
+  type                     = "egress"
+  from_port                = each.value.from_port
+  to_port                  = each.value.to_port
+  protocol                 = each.value.protocol
+  cidr_blocks              = try(each.value.cidr_blocks, null)
+  source_security_group_id = try(each.value.source_security_group_id, null)
+  security_group_id        = aws_security_group.ec2_sg.id
+}
+
+## Fetch Ubuntu Family AMI Id
+
+data "aws_ami" "ec2_ami" {
+  filter {
+    name   = "name"
+    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
+  }
+
+  filter {
+    name   = "virtualization-type"
+    values = ["hvm"]
+  }
+
+  most_recent = true
+
+  owners = ["amazon"]
+}
+
+## Target Group
+resource "aws_lb_target_group" "ec2_alb_target_group" {
+  name                 = var.ec2_alb_target_group.name
+  port                 = var.ec2_alb_target_group.port
+  protocol             = var.ec2_alb_target_group.protocol
+  vpc_id               = var.vpc_id
+  deregistration_delay = var.ec2_alb_target_group.deregistration_delay
+
+  tags = var.tags
+}
+
+## ALB
+resource "aws_lb" "alb_ec2" {
+  name                       = var.alb_ec2.name
+  enable_deletion_protection = var.alb_ec2.enable_deletion_protection
+  internal                   = var.alb_ec2.internal
+  load_balancer_type         = var.alb_ec2.load_balancer_type
+  security_groups            = [aws_security_group.alb_sg.id]
+  subnets                    = var.alb_subnet_ids
+
+  tags = var.tags
+
+  depends_on = [
+    aws_lb_target_group.ec2_alb_target_group
+  ]
+}
+
+## ALB Listener
+resource "aws_lb_listener" "alb_listener" {
+  for_each = var.alb_listener
+
+  load_balancer_arn = aws_lb.alb_ec2.arn
+  port              = each.value.port
+  protocol          = each.value.protocol
+  certificate_arn   = each.value.protocol == "HTTPS" ? try(each.value.certificate_arn, null) : null
+  ssl_policy        = each.value.protocol == "HTTPS" ? try(each.value.ssl_policy, null) : null
+
+  default_action {
+    type             = each.value.action_type
+    target_group_arn = each.value.action_type == "forward" ? aws_lb_target_group.ec2_alb_target_group.arn : null
+
+    dynamic "redirect" {
+      for_each = each.value.action_type == "redirect" ? [each.value.redirect] : []
+
+      content {
+        status_code = redirect.value.status_code
+        port        = redirect.value.port
+        protocol    = redirect.value.protocol
+      }
+    }
+  }
+
+  tags = var.tags
+
+  depends_on = [
+    aws_lb.alb_ec2
+  ]
+}
+
+## Launch Template
+resource "aws_launch_template" "ec2_launch_template" {
+  name = var.ec2_launch_template.name
+
+  block_device_mappings {
+    device_name = var.ec2_launch_template.device_name
+
+    ebs {
+      volume_size           = var.ec2_launch_template.ebs_volume_size
+      delete_on_termination = true
+      volume_type           = var.ec2_launch_template.ebs_volume_type
+    }
+  }
+
+  ebs_optimized          = var.ec2_launch_template.ebs_optimized
+  image_id               = data.aws_ami.ec2_ami.id
+  instance_type          = var.ec2_launch_template.instance_type
+  key_name               = var.ec2_launch_template.key_name
+  vpc_security_group_ids = [aws_security_group.ec2_sg.id]
+  update_default_version = true
+
+  user_data = filebase64("${path.module}/userdata.sh")
+
+  tag_specifications {
+    resource_type = "instance"
+
+    tags = var.tags
+  }
+}
+
+## AutoScaling Group
+resource "aws_autoscaling_group" "ec2_autoscaling_group" {
+  name = var.ec2_autoscaling_group.name
+
+  min_size                  = var.ec2_autoscaling_group.min_size
+  max_size                  = var.ec2_autoscaling_group.max_size
+  desired_capacity          = var.ec2_autoscaling_group.desired_capacity
+  vpc_zone_identifier       = [var.ec2_subnet_id]
+  default_cooldown          = var.ec2_autoscaling_group.default_cooldown
+  health_check_grace_period = var.ec2_autoscaling_group.health_check_grace_period
+
+  launch_template {
+    id      = aws_launch_template.ec2_launch_template.id
+    version = "$Latest"
+  }
+
+  target_group_arns = [aws_lb_target_group.ec2_alb_target_group.arn]
+
+  dynamic "tag" {
+    for_each = var.tags
+    content {
+      key                 = tag.key
+      value               = tag.value
+      propagate_at_launch = true
+    }
+  }
+
+  depends_on = [
+    aws_lb.alb_ec2
+  ]
+}

+ 4 - 0
modules/ec2/userdata.sh

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+apt update -y
+apt upgrade -y

+ 66 - 0
modules/ec2/variables.tf

@@ -0,0 +1,66 @@
+## Variables
+
+variable "tags" {
+  type    = map(string)
+  default = {}
+}
+
+variable "vpc_id" {
+  type    = string
+  default = ""
+}
+
+variable "ingress_alb_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "egress_alb_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "ingress_ec2_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "egress_ec2_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "alb_subnet_ids" {
+  type    = list(string)
+  default = [""]
+}
+
+variable "ec2_subnet_id" {
+  type    = string
+  default = ""
+}
+
+variable "ec2_launch_template" {
+  type    = map(string)
+  default = {}
+}
+
+variable "ec2_alb_target_group" {
+  type    = map(string)
+  default = {}
+}
+
+variable "alb_ec2" {
+  type    = map(string)
+  default = {}
+}
+
+variable "alb_listener" {
+  type    = any
+  default = {}
+}
+
+variable "ec2_autoscaling_group" {
+  type    = map(string)
+  default = {}
+}

+ 7 - 0
modules/vpc/README.md

@@ -0,0 +1,7 @@
+# VPC Module Description
+
+* Create VPC resource with its public and private subnets
+* Use `count` function to create multiple public subnets in different availability zones for ALB
+* User data file name `userdata.sh` to run set of commands at instance launch time
+* Internet gateway and NAT gateway for public and private subnets
+* Route tables to configure routes of internet gateway with public subnets and NAT gateway with private subnets

+ 13 - 0
modules/vpc/outputs.tf

@@ -0,0 +1,13 @@
+## Outputs
+
+output "vpc_id" {
+  value = aws_vpc.infra_vpc.id
+}
+
+output "public_subnet_id" {
+  value = aws_subnet.infra_public_subnet[*].id
+}
+
+output "private_subnet_id" {
+  value = aws_subnet.infra_private_subnet.id
+}

+ 92 - 0
modules/vpc/terraform.tf

@@ -0,0 +1,92 @@
+## VPC
+resource "aws_vpc" "infra_vpc" {
+  cidr_block           = var.cidr_block
+  enable_dns_support   = var.enable_dns_support
+  enable_dns_hostnames = var.enable_dns_hostnames
+
+  tags = var.tags
+}
+
+## Public Subnet for ALB
+resource "aws_subnet" "infra_public_subnet" {
+  count = length(var.public_subnet_az)
+
+  vpc_id            = aws_vpc.infra_vpc.id
+  availability_zone = var.public_subnet_az[count.index]
+  cidr_block        = var.public_subnet_cidr[count.index]
+
+  tags = var.tags
+}
+
+## Private Subnet for EC2
+resource "aws_subnet" "infra_private_subnet" {
+  vpc_id            = aws_vpc.infra_vpc.id
+  availability_zone = var.private_subnet_az
+  cidr_block        = var.private_subnet_cidr
+
+  tags = var.tags
+}
+
+## Internet Gateway
+resource "aws_internet_gateway" "infra_internet_gateway" {
+  vpc_id = aws_vpc.infra_vpc.id
+
+  tags = var.tags
+}
+
+## Route Table for Public Subnet and attach Internet Gateway to it
+resource "aws_route_table" "infra_public_rt" {
+  vpc_id = aws_vpc.infra_vpc.id
+
+  route {
+    cidr_block = "0.0.0.0/0"
+    gateway_id = aws_internet_gateway.infra_internet_gateway.id
+  }
+
+  tags = var.tags
+}
+
+## Route Table Public Subnet Association
+resource "aws_route_table_association" "infra_public_subnet_rt_association" {
+  count = length(var.public_subnet_az)
+
+  subnet_id      = aws_subnet.infra_public_subnet[count.index].id
+  route_table_id = aws_route_table.infra_public_rt.id
+}
+
+## Elastic IP for NAT Gateway
+resource "aws_eip" "infra_nat_gateway_eip" {
+  domain = "vpc"
+
+  tags = var.tags
+}
+
+## NAT Gateway
+resource "aws_nat_gateway" "infra_nat_gateway" {
+  allocation_id = aws_eip.infra_nat_gateway_eip.id
+  subnet_id     = aws_subnet.infra_public_subnet[0].id
+
+  tags = var.tags
+
+  depends_on = [
+    aws_internet_gateway.infra_internet_gateway
+  ]
+}
+
+## Route Table for Private Subnet and attach NAT Gateway to it
+resource "aws_route_table" "infra_private_rt" {
+  vpc_id = aws_vpc.infra_vpc.id
+
+  route {
+    cidr_block     = "0.0.0.0/0"
+    nat_gateway_id = aws_nat_gateway.infra_nat_gateway.id
+  }
+
+  tags = var.tags
+}
+
+## Route Table Private Subnet Association
+resource "aws_route_table_association" "infra_private_subnet_rt_association" {
+  subnet_id      = aws_subnet.infra_private_subnet.id
+  route_table_id = aws_route_table.infra_private_rt.id
+}

+ 41 - 0
modules/vpc/variables.tf

@@ -0,0 +1,41 @@
+## Variables
+
+variable "tags" {
+  type    = map(string)
+  default = {}
+}
+
+variable "cidr_block" {
+  type    = string
+  default = ""
+}
+
+variable "enable_dns_support" {
+  type    = bool
+  default = true
+}
+
+variable "enable_dns_hostnames" {
+  type    = bool
+  default = true
+}
+
+variable "public_subnet_az" {
+  type    = list(string)
+  default = [""]
+}
+
+variable "public_subnet_cidr" {
+  type    = list(string)
+  default = [""]
+}
+
+variable "private_subnet_az" {
+  type    = string
+  default = ""
+}
+
+variable "private_subnet_cidr" {
+  type    = string
+  default = ""
+}

+ 33 - 0
outputs.tf

@@ -0,0 +1,33 @@
+## Outputs
+
+output "vpc_id" {
+  value = module.vpc.vpc_id
+}
+
+output "public_subnet_id" {
+  value = module.vpc.public_subnet_id
+}
+
+output "private_subnet_id" {
+  value = module.vpc.private_subnet_id
+}
+
+output "alb_security_group_id" {
+  value = module.ec2.alb_security_group_id
+}
+
+output "ec2_security_group_id" {
+  value = module.ec2.ec2_security_group_id
+}
+
+output "alb_arn" {
+  value = module.ec2.alb_arn
+}
+
+output "alb_dns_name" {
+  value = module.ec2.alb_dns_name
+}
+
+output "autoscaling_group_arn" {
+  value = module.ec2.autoscaling_group_arn
+}

+ 5 - 0
provider.tf

@@ -0,0 +1,5 @@
+## AWS Provider Configuration
+
+provider "aws" {
+  region = var.region
+}

+ 34 - 1
terraform.tf

@@ -1 +1,34 @@
-provider "aws" {}
+## VPC Module
+
+module "vpc" {
+  source = "./modules/vpc"
+
+  cidr_block          = var.cidr_block
+  public_subnet_az    = var.public_subnet_az
+  public_subnet_cidr  = var.public_subnet_cidr
+  private_subnet_az   = var.private_subnet_az
+  private_subnet_cidr = var.private_subnet_cidr
+
+  tags = var.tags
+}
+
+## EC2 Module
+
+module "ec2" {
+  source = "./modules/ec2"
+
+  vpc_id                = module.vpc.vpc_id
+  ingress_alb_sg_rule   = var.ingress_alb_sg_rule
+  egress_alb_sg_rule    = var.egress_alb_sg_rule
+  ingress_ec2_sg_rule   = var.ingress_ec2_sg_rule
+  egress_ec2_sg_rule    = var.egress_ec2_sg_rule
+  ec2_launch_template   = var.ec2_launch_template
+  ec2_alb_target_group  = var.ec2_alb_target_group
+  alb_subnet_ids        = module.vpc.public_subnet_id
+  ec2_subnet_id         = module.vpc.private_subnet_id
+  alb_ec2               = var.alb_ec2
+  alb_listener          = var.alb_listener
+  ec2_autoscaling_group = var.ec2_autoscaling_group
+
+  tags = var.tags
+}

+ 126 - 0
terraform.tfvars

@@ -0,0 +1,126 @@
+region = "us-east-1"
+
+cidr_block = "10.0.0.0/16"
+
+public_subnet_az   = ["us-east-1a", "us-east-1b"]
+public_subnet_cidr = ["10.0.0.0/20", "10.0.16.0/20"]
+
+private_subnet_az   = "us-east-1b"
+private_subnet_cidr = "10.0.32.0/20"
+
+tags = {
+  "Infra" = "LiveLike"
+}
+
+ingress_alb_sg_rule = {
+  inbound_80 = {
+    from_port   = 80
+    to_port     = 80
+    protocol    = "TCP"
+    cidr_blocks = ["0.0.0.0/0"]
+  },
+
+  inbound_443 = {
+    from_port   = 443
+    to_port     = 443
+    protocol    = "TCP"
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+}
+
+egress_alb_sg_rule = {
+  ec2_egress = {
+    from_port = 0
+    to_port   = 0
+    protocol  = "-1"
+  }
+}
+
+ingress_ec2_sg_rule = {
+  alb_ingress = {
+    from_port = 0
+    to_port   = 0
+    protocol  = "-1"
+  },
+  allow_ssh_from_vpc = {
+    from_port   = 22
+    to_port     = 22
+    protocol    = "TCP"
+    cidr_blocks = ["10.0.0.0/16"]
+  }
+}
+
+egress_ec2_sg_rule = {
+  egress_80 = {
+    from_port   = 80
+    to_port     = 80
+    protocol    = "TCP"
+    cidr_blocks = ["0.0.0.0/0"]
+  },
+  egress_443 = {
+    from_port   = 443
+    to_port     = 443
+    protocol    = "TCP"
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+}
+
+ec2_alb_target_group = {
+  name                 = "ec2-alb-target-group"
+  port                 = 80
+  protocol             = "HTTP"
+  deregistration_delay = "60"
+}
+
+alb_listener = {
+  # redirect_80 = {
+  #   port        = "80"
+  #   protocol    = "HTTP"
+  #   action_type = "redirect"
+
+  #   redirect = {
+  #     status_code = "HTTP_301"
+  #     port        = "443"
+  #     protocol    = "HTTPS"
+  #   }
+  # },
+  # listener_443 = {
+  #   port            = "443"
+  #   protocol        = "HTTPS"
+  #   ssl_policy      = "ELBSecurityPolicy-TLS13-1-2-2021-06"
+  #   certificate_arn = ""
+  #   action_type     = "forward"
+  # }
+
+  listener_80 = {
+    port        = "80"
+    protocol    = "HTTP"
+    action_type = "forward"
+  }
+}
+
+alb_ec2 = {
+  name                       = "alb-for-ec2"
+  enable_deletion_protection = false
+  internal                   = false
+  load_balancer_type         = "application"
+}
+
+ec2_launch_template = {
+  name            = "ec2-launch-template"
+  device_name     = "/dev/sda1"
+  ebs_volume_size = 20
+  ebs_volume_type = "gp3"
+  instance_type   = "t3.micro"
+  ebs_optimized   = true
+  key_name        = "livelike"
+}
+
+ec2_autoscaling_group = {
+  name                      = "ec2-autoscaling-group"
+  min_size                  = 1
+  max_size                  = 2
+  desired_capacity          = 1
+  default_cooldown          = 60
+  health_check_grace_period = 120
+}

+ 81 - 0
variables.tf

@@ -0,0 +1,81 @@
+## Variables
+
+variable "region" {
+  type    = string
+  default = ""
+}
+
+variable "tags" {
+  type    = map(string)
+  default = {}
+}
+
+variable "cidr_block" {
+  type    = string
+  default = ""
+}
+
+variable "public_subnet_az" {
+  type    = list(string)
+  default = [""]
+}
+
+variable "public_subnet_cidr" {
+  type    = list(string)
+  default = [""]
+}
+
+variable "private_subnet_az" {
+  type    = string
+  default = ""
+}
+
+variable "private_subnet_cidr" {
+  type    = string
+  default = ""
+}
+
+variable "ingress_alb_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "egress_alb_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "ingress_ec2_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "egress_ec2_sg_rule" {
+  type    = any
+  default = {}
+}
+
+variable "ec2_launch_template" {
+  type    = map(string)
+  default = {}
+}
+
+variable "ec2_alb_target_group" {
+  type    = map(string)
+  default = {}
+}
+
+variable "alb_ec2" {
+  type    = map(string)
+  default = {}
+}
+
+variable "alb_listener" {
+  type    = any
+  default = {}
+}
+
+variable "ec2_autoscaling_group" {
+  type    = map(string)
+  default = {}
+}