Building and Configuring Fargate with RDS Using Terraform

2023.06.30

In the previous blog I wrote about the ALB and ECS settings, in this blog I will extend that by adding the RDS database.

Introduction:

Infrastructure as Code (IaC) has become an essential practice for automating the deployment and management of cloud resources. One common scenario is setting up a scalable and resilient application architecture using AWS Fargate and Amazon RDS. In this article, we will explore how to create and configure Fargate with RDS using Terraform, a popular IaC tool.

1. What is AWS Fargate?

AWS Fargate is a serverless compute engine for containers provided by Amazon Web Services (AWS). It allows developers to run containers without the need to manage the underlying infrastructure. Fargate offers a flexible and scalable solution for deploying containerized applications, ensuring high availability and ease of management.

2. What is Amazon RDS?

Amazon RDS (Relational Database Service) is a managed database service offered by AWS. It supports various database engines such as MySQL, PostgreSQL, and Amazon Aurora. RDS takes care of routine database administration tasks, including backups, software patching, and automatic scaling, enabling developers to focus on application development rather than database management.

Prerequisites

Before getting started, ensure that you have the following prerequisites in place: - An AWS account with appropriate permissions to create and manage Fargate and RDS resources. - Terraform installed on your local machine. - Basic knowledge of AWS services, Fargate, RDS, and Terraform.

Architeture Diagram

Setting up the Environment

To begin, you need to set up your development environment. This includes configuring AWS credentials on your machine and initializing a Terraform project.

1. Networking and Security Considerations

Networking plays a crucial role in connecting the Fargate containers with the RDS database instance. We need to define appropriate security groups, subnets, and VPC settings to allow communication between the Fargate tasks and the RDS database.

Networking

VPC

VPC Name CIDR Tenancy enable_dns_hostnames enable_dns_support Remarks
dio-dev-vpc 10.0.0.0/16 default true true

Subnets

Subnet Name Availability Zone CIDR Route Table Tag: Tier Remarks
dio-dev-private-subnet-1 ap-northeast-1a 10.0.16.0/20 Private-Route-Table Private Internet connection via Nat Gateway
dio-dev-private-subnet-2 ap-northeast-1c 10.0.32.0/20 Private-Route-Table Private Internet connection via Nat Gateway
dio-dev-public-subnet-1 ap-northeast-1a 10.0.48.0/20 Public-Route-Table Public Internet connection via Internet Gateway
dio-dev-public-subnet-2 ap-northeast-1c 10.0.64.0/20 Public-Route-Table Public Internet connection via Internet Gateway
dio-dev-isolated-Subnet-1 ap-northeast-1a 10.0.80.0/20 Isolated-Route-Table Isolated
dio-dev-isolated-Subnet-2 ap-northeast-1c 10.0.96.0/20 Isolated-Route-Table Isolated

Internet Gateway

Item Value Remarks
Name dio-dev-igw
Attached VPC dio-dev-vpc

NAT Gateway

Item Value Remarks
Name dio-dev-ngw
Attached VPC dio-dev-vpc
Subnet dio-dev-public-subnet-1

Private-Route-Table

Item Value Remarks
Name dio-dev-private-route-table
VPC dio-dev-production-vpc
Subnet Association dio-dev-private-subnet-1 dio-dev-private-subnet-2

Routes for Private Route-Table

recipient target status propagated remarks
10.0.0.0/16 local active no
0.0.0.0/0 dio-dev-ngw active no

Public-Route-Table

Item Value Remarks
Name dio-dev-public-route-table
VPC dio-dev-vpc
Subnet Association dio-dev-public-subnet-1 dio-dev-public-subnet-2

Routes for Public Route-Table

recipient target status propagated remarks
10.0.0.0/16 local active no
0.0.0.0/0 dio-dev-igw active no

Isolated-Route-Table

Item Value Remarks
Name dio-dev-isolated-route-table
VPC dio-dev-vpc
Subnet Association dio-dev-isolated-subnet-1 dio-dev-isolated-subnet-2

Routes for Isolated Route-Table

recipient target status propagated remarks
10.0.0.0/16 local active no

main.tf(networking)


terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
    }
  }
  backend s3 {
        key="PROD/infrastructure.tfstate"
        bucket="developersio-ecs-farget"
        region="ap-northeast-1"
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

## Data
data "aws_availability_zones" "az" {
  state = "available"
  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
}

## VPC
resource "aws_vpc" "vpc" {
  cidr_block           = var.vpc_cidr_block
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name = "dio-dev-vpc"
  }
}


resource "aws_subnet" "private_subnets" {
  vpc_id            = aws_vpc.vpc.id
  for_each          = var.private_subnets
  cidr_block        = cidrsubnet(aws_vpc.vpc.cidr_block, 4, each.value)
  availability_zone = each.key

  tags = {
    Name   = "dio-dev-private-subnet-${each.value == 1 ? "1" : "2"}"
        Tier = "Private"
  }
}

resource "aws_subnet" "public_subnets" {
  vpc_id            = aws_vpc.vpc.id
  for_each          = var.public_subnets
  cidr_block        = cidrsubnet(aws_vpc.vpc.cidr_block, 4, each.value)
  availability_zone = each.key

  tags = {
    Name   = "dio-dev-public-subnet-${each.value == 3 ? "1" : "2"}"
    Tier = "Public"
  }
}

## Internet Gateway

resource "aws_internet_gateway" "internet_gateway" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "dio-dev-igw"
  }
}

## Elastic IP for Nat Gateway

resource "aws_eip" "eip_natgateway" {
  vpc        = true
  depends_on = [aws_internet_gateway.internet_gateway]
  tags = {
    Name = "dio-dev-ngw-eip"
  }
}

## Nat Gateway

resource "aws_nat_gateway" "nat_gateway" {
  allocation_id = aws_eip.eip_natgateway.id
  subnet_id  = aws_subnet.public_subnets[element(keys(aws_subnet.public_subnets), 0)].id #Accessing an specific value inside a for_each
  depends_on = [aws_internet_gateway.internet_gateway]
  tags = {
    Name = "dio-dev-ngw"
  }
}

## Route Tables

resource "aws_route_table" "private_subnets_route_table" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block     = local.internet
    nat_gateway_id = aws_nat_gateway.nat_gateway.id
  }
  tags = {
    Name = "    dio-dev-private-route-table"
  }
}

resource "aws_route_table_association" "private_subnet_route_association" {
  for_each       = aws_subnet.private_subnets
  subnet_id      = each.value.id
  route_table_id = aws_route_table.private_subnets_route_table.id
}

resource "aws_route_table" "public_subnets_route_table" {
  vpc_id = aws_vpc.vpc.id
  route {
    cidr_block = local.internet
    gateway_id = aws_internet_gateway.internet_gateway.id
  }
  tags = {
    Name = "    dio-dev-public-route-table"
  }
}

resource "aws_route_table_association" "public_subnet_route_association" {
  for_each       = aws_subnet.public_subnets
  subnet_id      = each.value.id
  route_table_id = aws_route_table.public_subnets_route_table.id
}

# new Subnet for RDS
resource  "aws_subnet" "isolated_subnets" {
  vpc_id            = aws_vpc.vpc.id
  for_each          = var.isolated_subnets
  cidr_block        = cidrsubnet(aws_vpc.vpc.cidr_block, 4, each.value)
  availability_zone = each.key

  tags = {
    Name   = "dio-dev-isolated-subnet-${each.value == 5 ? "1" : "2"}"
    Tier = "Isolated"
  }
}

## Route Tables
resource "aws_route_table" "isolated_subnets_route_table" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "    dio-dev-isolated-route-table"
  }
}

resource "aws_route_table_association" "isolated_subnet_route_association" {
  for_each       = aws_subnet.isolated_subnets
  subnet_id      = each.value.id
  route_table_id = aws_route_table.isolated_subnets_route_table.id
}

Creating the Fargate Task Definition

The Fargate task definition defines the containerized application that will run on Fargate. It specifies details such as the Docker image, CPU and memory requirements, network configuration, and environment variables.

Cluster

Property Value Remarks
Cluster Name dio-dev-ecs-cluster
VPC dio-dev-vpc
Subnets dio-dev-private-subnet-1, dio-dev-private-subnet-2
Capacity Provider No default found
Container Insights true
Services 1
Registered container instances 0

Service

Property Value Remarks
Service Name dio-dev-ecs-service
Service type REPLICA
Launch Type Fargate
Application Type Service Deployment method for tasks (whether to include tasks in a service)
Task Definition dio-dev-td
Desired Tasks 1 To be deployed in 1 Availability Zones
VPC dio-dev-vpc
Subnets dio-dev-private-subnet-1, dio-dev-private-subnet-2
Security Group dio-dev-task
Auto assign Public IP false
Load Balancer Type Application Load Balancer
Load Balancer Name dio-dev-ecs-alb
Target Group dio-dev-ecs-tg
Container Name dio-container Container name to be connected to ALB
Port Number 80 Port number used for connection to ALB

Task Definition

Property Value Remarks
Task Definition Name dio-dev-td

Container Configuration

Property Value Remarks
Container Name dio-dev-ecs-container
Image URI httpd:latest
Port 80 Port for connection with ALB
Protocol TCP Protocol for connection with ALB
Essential Containers Yes
Application Environment Fargate
Operating System/Architecture Linux/ARM64
CPU 1 vCPU
Memory 2 GB
Task Role dio-dev-task-role
Execution Role dio-dev-task-exec-role
Network Mode awsvpc

IAM Role

Property Value Remarks
Role Name dio-dev-task-exec-role
Policy Name AmazonECSTaskExecutionRolePolicy AWS managed policy

2nd Role

Property Value Remarks
Role Name dio-dev-task-role
Policy Name AmazonECSTaskExecutionRolePolicy , AmazonSSMManagedInstanceCore AWS managed policy

Trust Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"


    }
  ]
}

Security Group

Property Value Remarks
Security Group Name dio-ecs-cluster-sg
Inbound Rules
Rule 1 Protocol: TCP, Port Range: 80, Source: Anywhere (0.0.0.0/0) Allows inbound traffic on port 80 from any source
Outbound Rules
Rule 1 Protocol: All Traffic, Destination: 0.0.0.0/0 Allows outbound traffic to any destination
Rule 2 Protoco: All Traffic, IP version :IPv6 ,Destination: ::/0 Allows outbound traffic to any destination

2nd Security Group

Property Value Remarks
Security Group Name dio-dev-task
Inbound Rules
Rule 1 Protocol: TCP, Port Range: 80, Source:dio-dev-ecs-sg Allows inbound traffic on port 80 from any source
Outbound Rules
Rule 1 Protocol: All Traffic, IP version :IPv4 ,Destination: 0.0.0.0/0 Allows outbound traffic to any destination
Rule 2 Protoco: All Traffic, IP version :IPv6 ,Destination: ::/0 Allows outbound traffic to any destination

Configure the Load Balancer this will distribute the task between multiple task or cluster

Application Load Balancer

Item Value Remarks
Type ALB
ELB Name dio-dev-ecs-alb
Subnet dio-dev-public-subnet-1 , dio-dev-public-subnet-2
Security Group dio-ecs-cluster-sg
Listener HTTP:80
Deletion Protection Disabled
Idle Timeout 60 seconds
HTTP/2 Enabled
Desync Mitigation Mode Defensive
Drop Invalid Header Fields Disabled
Access Logs Disabled
Preserve host header Disabled
Client port preservation Disabled
TLS version and cipher headers off

Listners

Path Target Security Policy SSL Certificate Notes
Default dio-dev-ecs-tg NA NA

TargetGroup

Item Configuration Value Notes
Target Group Name dio-dev-ecs-tg
Port http:80
Protocol version HTTP1
Deregistration Delay 300 seconds
Stickiness Disabled
Targets IP Address

main.tf(ECS)


terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
  backend s3 {
        key="PROD/ECS.tfstate"
        bucket="developersio-ecs-farget"
        region="ap-northeast-1"
  }
}



provider "aws" {
  region  = "ap-northeast-1"
}

## Data
data "aws_vpc" "vpc_id" {
  filter {
    name   = "tag:Name"
    values = ["dio-dev-vpc"]
  }
  lifecycle {
    postcondition {
      condition     = self.enable_dns_support == true
      error_message = "The selected VPC must have DNS support enabled."
    }
  }
}

data "aws_subnets" "private_subnets" {
  filter {
    name   = "tag:Tier"
    values = ["Private"]
  }
}

data "aws_subnets" "public_subnets" {
  filter {
    name   = "tag:Tier"
    values = ["Public"]
  }
}

## ECS Cluster

resource "aws_ecs_cluster" "ecs_cluster" {
  name = var.ecs_cluster_name

  setting {
    name  = "containerInsights"
    value = "enabled"
  }
}

## ECS IAM Role

resource "aws_iam_role" "ecsTaskExecutionRole" {
  name                = var.ecsTaskExecutionRole_name
  path                = "/"
  managed_policy_arns = local.managedpolicies_AmazonECSTaskExecutionRolePolicy

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Sid    = ""
        Effect = "Allow"
        Principal = {
          Service = "ecs-tasks.amazonaws.com"
        }
      },
    ]
  })
}





## Tak role

resource "aws_iam_role" "ecsTaskRole" {
  name                = var.ecsTaskRole_name
  path                = "/"
  managed_policy_arns = local.managedpolicies_AmazonECSTaskRolePolicy

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Sid    = ""
        Effect = "Allow"
        Principal = {
          Service = "ecs-tasks.amazonaws.com"
        }
      },
    ]
  })
}


## Security Groups

resource "aws_security_group" "alb_ingress" {
  name        = var.alb_ingress_name
  description = "Ingress traffic from Internet"
  vpc_id      = data.aws_vpc.vpc_id.id

  dynamic "ingress" {
    for_each = var.alb_ingress_ports
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = local.tcp_protocol
      cidr_blocks = local.all_ips_ipv4
    }
  }

  egress {
    from_port        = local.any_port
    to_port          = local.any_port
    protocol         = local.any_protocol
    cidr_blocks      = local.all_ips_ipv4
    ipv6_cidr_blocks = local.all_ips_ipv6
  }
}

resource "aws_security_group" "ecs_fargate_task_ingress" {
  name        = var.ecs_fargate_task_sg
  description = "Ingress traffic from ALB to Fargate task"
  vpc_id      = data.aws_vpc.vpc_id.id

  ingress {
    description     = "HTTP Port"
    from_port       = 80
    to_port         = 80
    protocol        = "tcp"
    security_groups = [aws_security_group.alb_ingress.id]
  }

  egress {
    from_port        = local.any_port
    to_port          = local.any_port
    protocol         = local.any_protocol
    cidr_blocks      = local.all_ips_ipv4
    ipv6_cidr_blocks = local.all_ips_ipv6
  }
}

### Fargate Task_Definition

resource "aws_ecs_task_definition" "fargate_task_definition" {
  family                   = var.fargate_task_definition_name
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  cpu                      = var.fargate_task_definition_cpu
  memory                   = var.fargate_task_definition_memory
  execution_role_arn       = aws_iam_role.ecsTaskExecutionRole.arn
  task_role_arn            = aws_iam_role.ecsTaskRole.arn

  container_definitions = jsonencode([{
    name      = "${var.ecs_container_name}"
    image     = "${var.fargate_task_definition_image}"
    essential = true
    portMappings = [{
      protocol      = "tcp"
      containerPort = 80
      hostPort      = 80
    }]
    }]
  )

  runtime_platform {
    operating_system_family = "LINUX"
    cpu_architecture        = "ARM64"
  }
}

## Amazon ECS Service

resource "aws_ecs_service" "ecs_fargate" {
  name                   = var.ecs_service_name
  cluster                = aws_ecs_cluster.ecs_cluster.id
  task_definition        = aws_ecs_task_definition.fargate_task_definition.id
  desired_count          = var.desired_task_count
  enable_execute_command = true
  scheduling_strategy    = "REPLICA"
  launch_type            = "FARGATE"

  network_configuration {
    subnets          = data.aws_subnets.private_subnets.ids
    security_groups  = [aws_security_group.ecs_fargate_task_ingress.id]
    assign_public_ip = false
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.ecs_alb_target_group.arn
    container_name   = var.ecs_container_name
    container_port   = 80
  }
}

## ALB

resource "aws_lb" "ecs_alb" {
  name                   = var.alb_name
  internal               = false
  load_balancer_type     = "application"
  security_groups        = [aws_security_group.alb_ingress.id]
  subnets                = data.aws_subnets.public_subnets.ids
  idle_timeout           = 60
  enable_http2           = true
  desync_mitigation_mode = "defensive"
}

## ALB Target Group

resource "aws_lb_target_group" "ecs_alb_target_group" {
  name                          = var.alb_target_group_name
  target_type                   = "ip"
  port                          = 80
  protocol                      = "HTTP"
  vpc_id                        = data.aws_vpc.vpc_id.id
  load_balancing_algorithm_type = "round_robin"

  health_check {
    path                = "/"
    protocol            = "HTTP"
    healthy_threshold   = 5
    unhealthy_threshold = 2
    timeout             = 5
    interval            = 30
    matcher             = 200
  }
}

## ALB Target Group Listerner

resource "aws_alb_listener" "ecs_alb_listener" {
  load_balancer_arn = aws_lb.ecs_alb.arn
  port              = 80
  protocol          = "HTTP"

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.ecs_alb_target_group.arn
  }
}

Configuring the RDS Database Instance

Next, we configure the RDS database instance that will be used by our application. This involves specifying the database engine, instance type, storage, username, password, and other parameters.

DB Instance Settings

Item Setting Value Remarks
DB Name diodb
Storage 10GB
Automatic Scaling of Storage Disabled
Instance Type db.t3.micro
Security Group dio-dev-db-sg
Multi-AZ false
AZ ap-northeast-a
Port Number 3306
Master Username dioadmin
Database authentication Password
Performance_insights enabled false
Storage type General Purpose SSD (gp2)
Parameter Group dio-dev-db-pg
Option Group Default

DB engine settings

Parameter Name Setting Value Remarks
Database Type MySQL
Character Encoding utf-8
Backup Retention Period (1-35 days) 35
Backup Window 03:00-06:00 UTC (GMT)
Maintenance Window "mon:00:00-mon:03:00" UTC (GMT)
Auto Minor Version Upgrade false
Deletion protection Disabled

Parameter Group

Field Content Remarks
name dio-dev-db-pg
character_set_server utf8
character_set_client utf8

Subnet Group

Field Content Remarks
name dio-dev-db-subnet
subnet_ids dio-dev-isolated-subnet-1 dio-dev-isolated-subnet-2

Security Group

Property Value Remarks
Security Group Name dio-dev-db-sg
Inbound Rules
Rule 1 Protocol: TCP, Port Range: 3306, Source: 10.0.0.0/16 Allows inbound traffic on port 80 from any source
Outbound Rules
Rule 1 Protocol: All Traffic, Destination: 0.0.0.0/0 Allows outbound traffic to any destination
Rule 1 Protocol: All Traffic,IP version IPv6, Destination: ::/0 Allows outbound traffic to any destination

main.tf(RDS)


terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
  backend s3 {
        key="PROD/RDS.tfstate"
        bucket="developersio-ecs-farget"
        region="ap-northeast-1"
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

## Data

data "aws_vpc" "vpc_id" {
  filter {
    name   = "tag:Name"
    values = ["dio-dev-vpc"]
  }
}

data "aws_subnets" "isolated_subnets" {
  filter {
    name   = "tag:Name"
    values = ["Isolated"]
  }
}

data "aws_subnet_ids" "isolated_subnets_ids" {
  vpc_id = data.aws_vpc.vpc_id.id
  filter {
    name   = "tag:Tier"
    values = ["Isolated"]
  }
}

## Security Groups

resource "aws_security_group" "rdssql_ingress" {
  name        = var.rdssql_ingress_name
  description = "Ingress traffic from Private subnets"
  vpc_id      = data.aws_vpc.vpc_id.id

  dynamic "ingress" {
    for_each = var.rdssql_ingress_ports
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = [data.aws_vpc.vpc_id.cidr_block]
    }
  }

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


## DB Subnet Group

resource "aws_db_subnet_group" "rdssqldb_subnet_group" {
  name       = var.rdssql_db_subnet_group_name
  subnet_ids = data.aws_subnet_ids.isolated_subnets_ids.ids
}

resource "aws_db_instance" "rdssqldb_instance" {
  allocated_storage    = 10
  db_name              = "diodb"
  engine               = "mysql"
  engine_version       = "5.7"
  instance_class       = "db.t3.micro"
  username             = var.user_name
  password             = var.rdssql_password
  parameter_group_name = aws_db_parameter_group.rdssqldb_paramater_group.name
  db_subnet_group_name = aws_db_subnet_group.rdssqldb_subnet_group.name
  skip_final_snapshot  = true
  backup_window            = var.backup_windows_retention_maintenance[0]
  backup_retention_period  = var.backup_windows_retention_maintenance[1]
  maintenance_window       = var.backup_windows_retention_maintenance[2]
  vpc_security_group_ids = [aws_security_group.rdssql_ingress.id]
  availability_zone     = "ap-northeast-1a"
  auto_minor_version_upgrade = false
}


resource "aws_db_parameter_group" "rdssqldb_paramater_group" {
  name   = "dio-dev-db-pg"
  family = "mysql5.7"

  parameter {
    name  = "character_set_server"
    value = "utf8"
  }

  parameter {
    name  = "character_set_client"
    value = "utf8"
  }
}

Deploying with Terraform

Now comes the exciting part - deploying the infrastructure using Terraform. We create the necessary Terraform configuration files, define the resources, and leverage Terraform's AWS provider to provision the infrastructure in a reliable and automated manner.

I will add all the locals and variables in a separate file that I used while writing above terraform.

locals.tf

 
locals {
  internet = "0.0.0.0/0"

  managedpolicies_AmazonECSTaskExecutionRolePolicy = [
    "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
  ]

  managedpolicies_AmazonECSTaskRolePolicy = [
    "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy",
    "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  ]
}

locals {
  http_port    = 80
  any_port     = 0
  any_protocol = "-1"
  tcp_protocol = "tcp"
  all_ips_ipv4 = ["0.0.0.0/0"]
  all_ips_ipv6 = ["::/0"]
}

veriables.tf


## VPC CIDR BLOCK
variable "vpc_cidr_block" {
  type        = string
  default     = "10.0.0.0/16"
  description = "The IPv4 CIDR block for the VPC"
}

## Private Subnet CIDR BLOCK
variable "private_subnets" {
  type = map(number)
  default = {
    "ap-northeast-1a" = 1
    "ap-northeast-1c" = 2
  }
  description = "Map of AZ to a number that should be used for private subnets"
}

## Public Subnet CIDR BLOCK
variable "public_subnets" {
  type = map(number)
  default = {
    "ap-northeast-1a" = 3
    "ap-northeast-1c" = 4
  }
  description = "Map of AZ to a number that should be used for public subnets"
}


## for RDS

## isolated Subnet CIDR BLOCK
variable "isolated_subnets" {
  type = map(number)
  default = {
    "ap-northeast-1a" = 5
    "ap-northeast-1c" = 6
  }
  description = "Map of AZ to a number that should be used for public subnets"
}

## Security Group
variable "rdssql_db_subnet_group_name" {
  type        = string
  default     = "dio-dev-db-subnet"
  description = "Name for the DB subnet group"
}


variable "rdssql_ingress_name" {
  type        = string
  default     = "dio-dev-db-sg"
  description = "Security Group name"
}

variable "rdssql_ingress_ports" {
  type        = list(number)
  default     = [3306]
  description = "List of ports opened from Private Subnets CIDR to RDS SQL Instance"
}



variable "rdssql_password" {
  type        = string
  default     = "MyStrongPa$$w0rd"
  description = "RDS Admin password"
  sensitive   = true
  ## Terraform _ Sensitive Variables = https://learn.hashicorp.com/tutorials/terraform/sensitive_variables
}



variable "backup_windows_retention_maintenance" {
  type        = list(any)
  default     = ["03:00-06:00", "35", "Mon:00:00-Mon:03:00"]
  description = "Backup window time, desired retention in days, maitenance windows"
}

variable "rds_db_instance_class" {
  type        = string
  default     = "db.t3.micro"
  description = "Amazon RDS DB Instance class"
  # Instance type: https://aws.amazon.com/rds/sqlserver/instance_types/
}

variable "storage_allocation" {
  type        = list(any)
  default     = ["20", "100"]
  description = "Allocated storage Gb, Max allocated storage Gb"
}

variable "user_name" {
  type        = string
  default     = "dioadmin"
  description = "mySQL Admin username"
}

## ECS Cluster

variable "ecs_cluster_name" {
  type    = string
  default = "dio-dev-ecs-cluster"
}


variable "ecs_container_name"{
  type    = string
  default = "dio-container"  
}
## ECS IAM Roles and Instance Roles

variable "ecsTaskExecutionRole_name" {
  type    = string
  default = "dio-dev-task-exec-role"
}

variable "ecsTaskRole_name" {
  type    = string
  default = "dio-dev-task-role"
}

variable "alb_ingress_name" {
  type    = string
  default = "dio-ecs-cluster-sg"
}

variable "ecs_fargate_task_name" {
  type    = string
  default = "dio-dev-task"
}

variable "alb_ingress_ports" {
  type        = list(number)
  description = "List of ports opened from Internet to ALB"
  default     = [80]
}

## ECS Task Definitions

### Fargate Task_Definition

variable "fargate_task_definition_name" {
  type    = string
  default = "dio-dev-td"
}

variable "fargate_task_definition_cpu" {
  type    = number
  default = "1024"
}

variable "fargate_task_definition_memory" {
  type    = number
  default = "2048"
}

variable "fargate_task_definition_image" {
  type    = string
  default = "httpd:latest"
}

## ECS Service

variable "ecs_service_name" {
  type    = string
  default = "dio-dev-ecs-service"
}

variable "desired_task_count" {
  type    = number
  default = "1"
}

## ALB

variable "alb_name" {
  type    = string
  default = "dio-dev-ecs-alb"
}

## ALB Target Group

variable "alb_target_group_name" {
  type        = string
  default     = "dio-dev-ecs-tg"
}

Testing if its properly deployed:

We can follow the bellow link to verify the connection : https://repost.aws/knowledge-center/ecs-fargate-task-database-connection

also for troubleshooting we can use below cli command to access fargate interactively for mac you can install ssm plugin using bellow command

brew install --cask session-manager-plugin

To start the Fargate session, the following command can be used

aws ecs execute-command --cluster arn:aws:ecs:ap-northeast-1:xxxxxxxxxx:cluster/ECS-xxxxxx --task arn:aws:ecs:ap-northeast-1:xxxxxxxxx:task/ECS-xxxxxxxxx/d0xxxxxxxxxxxxxxxx --container appache_xxxxxxx --interactive --command "/bin/sh" --region ap-northeast-1

Conclusion

In this article, we explored the process of building and configuring Fargate with RDS using Terraform. We discussed the benefits of using Terraform for Infrastructure as Code, outlined the prerequisites, and covered the key steps involved in setting up the environment, creating the Fargate task definition, configuring the RDS database instance, and handling networking and security considerations. By leveraging Terraform's power, developers can streamline the deployment and management of their containerized applications, ensuring scalability, reliability, and ease of maintenance.

With this knowledge, you are now equipped to embark on your journey of building and configuring Fargate with RDS using Terraform. Happy coding and deploying!

[Disclaimer: Ensure that you follow best practices and security considerations while deploying and managing your infrastructure. The code examples provided in this article are for illustrative purposes only and may require customization based on your specific requirements and environment.]