DEV Community

Tanvir Ahmed
Tanvir Ahmed

Posted on

Best Practices for ECS Deployment Using Terraform

Deploying ECS services using Terraform allows you to maintain a modular, reusable, and parameter-driven infrastructure. In this guide, we will focus on creating and reusing ECS task definitions, configuring services, and integrating other AWS components effectively with Terraform.

Problem Statement

Creating a robust ECS deployment often involves repetitive configurations and complex setups. By utilizing Terraform, you can:

  • Simplify the creation of reusable ECS task definitions.
  • Pass dynamic variables to tasks and services.
  • Seamlessly integrate with other AWS services like Secrets Manager and CloudWatch.

Terraform-Based ECS Deployment

Key Components

  1. ECS Task Definitions

    • Define container settings (CPU, memory, image, etc.).
    • Use variables to make definitions reusable across multiple services.
  2. ECS Services

    • Manage the number of tasks, scaling policies, and integration with ALBs.
  3. AWS Services Integration

    • Connect to Secrets Manager for sensitive data.
    • Enable CloudWatch logging and metrics.

Step 1: Reusable Task Definition

Define a parameterized task definition in Terraform:

resource "aws_ecs_task_definition" "app" {
  family                   = var.task_family
  container_definitions    = jsonencode([
    {
      name              = "${var.container_name}"
      image             = "${var.container_image}"
      cpu               = var.cpu
      memory            = var.memory
      essential         = true
      environment       = var.environment_variables
      logConfiguration  = {
        logDriver = "awslogs"
        options   = {
          "awslogs-group"         = var.log_group
          "awslogs-region"        = var.region
          "awslogs-stream-prefix" = var.log_stream_prefix
        }
      }
    }
  ])
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  execution_role_arn       = aws_iam_role.ecs_task_execution.arn
  task_role_arn            = aws_iam_role.ecs_task.arn
  cpu                      = var.task_cpu
  memory                   = var.task_memory
}

Enter fullscreen mode Exit fullscreen mode

Variables for Flexibility

Define variables to make the task definition reusable:

variable "task_family" {}
variable "container_name" {}
variable "container_image" {}
variable "cpu" {}
variable "memory" {}
variable "environment_variables" { type = list(map(string)) }
variable "log_group" {}
variable "region" {}
variable "log_stream_prefix" {}
variable "task_cpu" {}
variable "task_memory" {}
Enter fullscreen mode Exit fullscreen mode

Step 2: Configuring ECS Service

Define a service resource and pass the task definition dynamically:

resource "aws_ecs_service" "app" {
  name            = var.service_name
  cluster         = aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.app.arn

  desired_count   = var.desired_count
  launch_type     = "FARGATE"

  network_configuration {
    subnets         = var.subnets
    security_groups = [aws_security_group.app.id]
    assign_public_ip = true
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.app.arn
    container_name   = var.container_name
    container_port   = var.container_port
  }

  depends_on = [aws_lb_listener.frontend]
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Parameterized Variables for ECS Service

Define variables for the service:

variable "service_name" {}
variable "desired_count" {}
variable "subnets" { type = list(string) }
variable "container_port" {}`

### Step 4: Secrets Manager Integration

To securely manage environment variables:

`resource "aws_secretsmanager_secret" "app_secret" {
  name = var.secret_name
}

resource "aws_secretsmanager_secret_version" "app_secret_version" {
  secret_id     = aws_secretsmanager_secret.app_secret.id
  secret_string = jsonencode(var.secret_values)
}

variable "secret_name" {}
variable "secret_values" { type = map(string) }
Enter fullscreen mode Exit fullscreen mode

Pass secrets as environment variables in the task definition:

environment = [
  {
    name = "SECRET_KEY"
    valueFrom = aws_secretsmanager_secret.app_secret.arn
  }
]
Enter fullscreen mode Exit fullscreen mode

Step 5: Outputs for Reusability

Define outputs for task definition and service:

output "task_definition_arn" {
  value = aws_ecs_task_definition.app.arn
}

output "service_name" {
  value = aws_ecs_service.app.name
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Use Modules: Organize your Terraform code into reusable modules.

  2. Version Locking: Use version constraints for Terraform providers.

  3. State Management: Use remote state storage (e.g., S3 with DynamoDB locking).

  4. Parameter Store: Integrate with SSM Parameter Store for dynamic configurations.

Conclusion

By parameterizing ECS task definitions and services in Terraform, you can create a scalable, reusable, and efficient infrastructure. This approach not only reduces duplication but also enhances the flexibility of your deployments, enabling rapid adaptation to new requirements.

Top comments (0)