Automatizando FinOps na AWS: Como construímos um módulo de Monitoramento de Custos com Terraform e incident.io
Source: Dev.to
Gerenciar custos em nuvem: módulo Terraform reutilizável para alertas de orçamento na AWS
A disciplina de FinOps busca trazer visibilidade e accountability para os gastos em nuvem, mas sem as ferramentas certas é fácil ser surpreendido pela fatura no final do mês.
Neste artigo compartilho a experiência e os detalhes técnicos da criação de um módulo Terraform reutilizável que automatiza alertas de orçamento na AWS, integrando‑o diretamente com a plataforma de incidentes incident.io – eliminando intermediários desnecessários como funções Lambda.
Contexto
Precisávamos de uma maneira padronizada de criar guardrails financeiros para novos projetos. Toda nova conta ou ambiente (staging, production) na AWS deveria nascer com:
- um orçamento definido
- um canal de alerta configurado
Requisitos
| # | Requisito |
|---|---|
| 1 | Definir um limite mensal em dólares |
| 2 | Alertar quando o gasto real atingir certas porcentagens (ex.: 80 %, 100 %) |
| 3 | Alertar quando a previsão (forecast) indicar que o orçamento será estourado |
| 4 | Enviar esses alertas para nossa plataforma de gerenciamento de incidentes |
| 5 | Garantir segurança (dados criptografados em repouso) |
Inicialmente consideramos usar AWS Lambda para processar os alertas do AWS Budgets e formatar o JSON para o webhook, mas percebemos que poderíamos simplificar a arquitetura usando a capacidade nativa do SNS de realizar chamadas HTTPS (Webhooks).
Arquitetura final
flowchart LR
A[AWS Budgets] --> B[SNS Topic (criptografado)]
B --> C[HTTP POST]
C --> D[incident.io]
Essa abordagem reduz a complexidade operacional (menos código para manter) e o custo.
Implementação
1️⃣ KMS Key para criptografia do SNS
resource "aws_kms_key" "cost_alerts_key" {
description = "KMS key for encrypting FinOps SNS topics"
deletion_window_in_days = 10
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "EnableIAMUserPermissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${local.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "AllowAWSBudgetsToUseTheKey"
Effect = "Allow"
Principal = {
Service = "budgets.amazonaws.com"
}
Action = [
"kms:GenerateDataKey*",
"kms:Decrypt"
]
Resource = "*"
}
]
})
}
Observação: A permissão explícita para
budgets.amazonaws.comé essencial. Sem ela, o orçamento falha silenciosamente ao tentar publicar no tópico.
2️⃣ SNS Topic e política de publicação
resource "aws_sns_topic" "cost_alerts" {
name_prefix = "${local.name_prefix}-cost-alerts-"
kms_master_key_id = aws_kms_key.cost_alerts_key.arn
}
resource "aws_sns_topic_policy" "default" {
arn = aws_sns_topic.cost_alerts.arn
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AWSBudgets-Publish"
Effect = "Allow"
Principal = {
Service = "budgets.amazonaws.com"
}
Action = "SNS:Publish"
Resource = aws_sns_topic.cost_alerts.arn
}
]
})
}
3️⃣ Orçamento (Budget) com notificações dinâmicas
resource "aws_budgets_budget" "cost_budget" {
name = "${local.name_prefix}-budget"
budget_type = "COST"
limit_amount = var.limit_amount
limit_unit = var.limit_unit
time_period_start = "2024-01-01_00:00"
time_unit = var.time_unit
# Bloco dinâmico para notificações
dynamic "notification" {
for_each = var.notifications
content {
comparison_operator = notification.value.comparison_operator
threshold = notification.value.threshold
threshold_type = notification.value.threshold_type
notification_type = notification.value.notification_type
subscriber_sns_topic_arns = [aws_sns_topic.cost_alerts.arn]
}
}
}
Variável notifications (exemplo em terraform.tfvars)
notifications = [
{
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
},
{
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
}
]
Isso permite que o consumidor do módulo configure alertas de forma declarativa e simples.
4️⃣ Assinatura HTTPS para o webhook do incident.io
resource "aws_sns_topic_subscription" "incident_io" {
topic_arn = aws_sns_topic.cost_alerts.arn
protocol = "https" # Detectado dinamicamente no código real
endpoint = var.incident_io_webhook_url
}
5️⃣ Desafio da confirmação da assinatura
- O Terraform aplica a infraestrutura.
- O estado da assinatura fica como
PendingConfirmation. - O administrador acessa os logs do
incident.io, localiza a mensagem deSubscriptionConfirmatione clica no link manualmente.
Embora não seja 100 % automatizado (zero‑touch), esse compromisso é aceitável para uma configuração que ocorre apenas uma vez por ambiente.
Conclusão
Criar módulos de infraestrutura como código para FinOps é essencial para escalar o uso da nuvem de forma segura e econômica.
Com o módulo apresentado:
- Segurança – dados em repouso criptografados via KMS
- Simplicidade – elimina a necessidade de Lambda intermediário
- Flexibilidade – notificações configuráveis por meio de variáveis
- Integração direta – webhook HTTPS para incident.io
A abordagem reduz a complexidade operacional, diminui custos e garante que as equipes de engenharia tenham visibilidade contínua sobre os gastos da nuvem.
Responsável
Ao centralizar a lógica de orçamentos, criptografia e notificações em um único módulo, garantimos que todos os ambientes sigam as melhores práticas de segurança e observabilidade financeira.
A escolha de remover camadas intermediárias (como Lambdas) simplificou a stack, tornando‑a mais robusta e fácil de manter a longo prazo.