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
-
ECS Task Definitions
- Define container settings (CPU, memory, image, etc.).
- Use variables to make definitions reusable across multiple services.
-
ECS Services
- Manage the number of tasks, scaling policies, and integration with ALBs.
-
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
}
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" {}
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]
}
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) }
Pass secrets as environment variables in the task definition:
environment = [
{
name = "SECRET_KEY"
valueFrom = aws_secretsmanager_secret.app_secret.arn
}
]
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
}
Best Practices
Use Modules: Organize your Terraform code into reusable modules.
Version Locking: Use version constraints for Terraform providers.
State Management: Use remote state storage (e.g., S3 with DynamoDB locking).
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)