· Denison Caldeira · DevOps · 3 min read
Terraform na AWS: deploy de containers no ECS Fargate na prática
Como provisionar um cluster ECS Fargate com Terraform de forma organizada — módulos, variáveis por ambiente e state remoto. Guia direto, com código.
Subir container na AWS no clique funciona uma vez. Repetir isso em staging e produção, com rastreabilidade e sem quebrar nada, é outra história. É aí que entra Terraform + ECS Fargate.
Este guia mostra a estrutura que uso em projeto real: organizada, versionada e pronta pra escalar.
Por que ECS Fargate
Fargate roda container sem você gerenciar EC2. Sem patch de instância, sem capacity planning manual. Você define CPU/memória da task e a AWS resolve o resto. Pra time pequeno que quer container em produção sem virar SRE full-time, é o caminho mais curto.
Estrutura de pastas
Nada de um main.tf gigante. Separe por responsabilidade:
infra/
├── modules/
│ └── ecs-service/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── envs/
│ ├── staging/
│ │ ├── main.tf
│ │ └── terraform.tfvars
│ └── prod/
│ ├── main.tf
│ └── terraform.tfvars
└── backend.tf
O módulo descreve o que é um serviço ECS. Os envs/ dizem com quais valores em cada ambiente.
State remoto (não pule isso)
State local é bomba-relógio em time. Use S3 + DynamoDB pra lock:
# backend.tf
terraform {
backend "s3" {
bucket = "decro-tfstate"
key = "ecs/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "tf-lock"
encrypt = true
}
}
Sem dynamodb_table, dois apply simultâneos corrompem o state. Com ele, o segundo espera.
O módulo do serviço
Trecho do que importa — a task definition e o service:
# modules/ecs-service/main.tf
resource "aws_ecs_task_definition" "app" {
family = var.name
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = var.cpu
memory = var.memory
execution_role_arn = var.execution_role_arn
container_definitions = jsonencode([
{
name = var.name
image = var.image
essential = true
portMappings = [{ containerPort = var.container_port }]
}
])
}
resource "aws_ecs_service" "app" {
name = var.name
cluster = var.cluster_id
task_definition = aws_ecs_task_definition.app.arn
desired_count = var.desired_count
launch_type = "FARGATE"
network_configuration {
subnets = var.subnets
security_groups = [var.security_group_id]
}
}
Variáveis por ambiente
Staging roda magro, produção roda redundante. Mesmo módulo, valores diferentes:
# envs/prod/terraform.tfvars
cpu = 512
memory = 1024
desired_count = 3
# envs/staging/terraform.tfvars
cpu = 256
memory = 512
desired_count = 1
Fluxo de trabalho
cd infra/envs/staging
terraform init
terraform plan -out=tfplan
terraform apply tfplan
Sempre plan antes de apply. O plan é o seu code review da infra: ele te diz exatamente o que vai mudar antes de mudar.
Erros que custam caro
- State sem lock → corrupção em time.
- Hardcode de secret no
.tf→ vaza no git. Use SSM Parameter Store ou Secrets Manager. - Um ambiente só → você testa em produção. Separe staging desde o dia 1.
Próximo passo
Terraform + ECS é a base. O passo seguinte é amarrar isso numa esteira de CI/CD que faz plan no PR e apply no merge — sem ninguém rodando comando na mão.
Se você quer montar isso do zero, com Docker, Kubernetes, CI/CD e monitoramento num projeto real pra portfólio, é exatamente o que ensino no DevOps na Prática. Dúvida pontual? Chama no WhatsApp.