
Building and Configuring Fargate with RDS Using Terraform
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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.]













