Budget no Azure com Terraform: Alertas de Custo Automatizados

Criar um budget no Azure com Terraform substitui dezenas de cliques manuais no portal — que ninguém documenta e que você repete do zero na próxima subscription — por um módulo revisável, reaproveitável e com o histórico de alterações no Git, usando o recurso azurerm_consumption_budget_subscription.

Neste guia você vai ver como criar um módulo Terraform reutilizável para budgets de subscription, instanciá-lo com um valor mensal e múltiplos thresholds de alerta, e conferir o resultado com terraform plan/apply reais.

Segurança: os comandos deste artigo utilizam variáveis de ambiente para credenciais. Nunca insira IDs de subscription, senhas ou chaves diretamente nos comandos — use TF_VAR_*, um arquivo .tfvars fora do Git, ou o Azure Key Vault.

Sumário

Pré-requisitos

Antes de criar o budget no Azure com Terraform, você vai precisar do Azure CLI autenticado (az login), do Terraform >= 1.5 e de uma role com permissão de escrita em Cost Management na subscription (Cost Management Contributor ou Owner/Contributor). O provider usado é o azurerm ~> 4.0.

az account show --query "{name:name, id:id}" -o table

Criando o módulo terraform-budget-modules

Esse módulo é a base de qualquer budget no Azure com Terraform: ele encapsula o recurso azurerm_consumption_budget_subscription e gera um bloco notification por threshold usando dynamic, para não repetir código a cada percentual de alerta:

# main.tf
resource "azurerm_consumption_budget_subscription" "this" {
  name            = var.name
  subscription_id = "/subscriptions/${var.subscription_id}"
  amount          = var.amount
  time_grain      = var.time_grain

  time_period {
    start_date = var.start_date
    end_date   = var.end_date
  }

  dynamic "notification" {
    for_each = var.thresholds
    content {
      enabled        = true
      threshold      = notification.value
      operator       = "GreaterThanOrEqualTo"
      threshold_type = "Actual"
      contact_emails = var.contact_emails
    }
  }
}

threshold_type = "Actual" dispara o alerta com base no consumo já realizado. Trocando para "Forecasted" o alerta passa a considerar a projeção de custo do mês, o que avisa antes do limite ser efetivamente estourado.

# variables.tf
variable "name"            { type = string }
variable "subscription_id" { type = string }
variable "amount"          { type = number }

variable "time_grain" {
  type    = string
  default = "Monthly" # Monthly, Quarterly, Annually, BillingMonth, BillingQuarter, BillingAnnual
}

variable "start_date" { type = string } # primeiro dia do mês, RFC3339: "2026-08-01T00:00:00Z"
variable "end_date"   { type = string; default = null }

variable "thresholds" {
  type    = list(number)
  default = [80, 100]
}

variable "contact_emails" { type = list(string) }

start_date precisa ser sempre o primeiro dia do mês — o provider rejeita qualquer outro dia. Se end_date não for informado, o Azure assume 10 anos de vigência a partir do início.

Instanciando o módulo

Com o módulo pronto, o ambiente que efetivamente cria o budget no Azure com Terraform fica pequeno — só passa os valores específicos deste caso: R$ 20/mês, alertas em 50%, 80% e 100%, notificando um único e-mail:

module "budget" {
  source = "../../terraform-budget-modules"

  name            = "budget-finops-blog-castilho"
  subscription_id = var.subscription_id
  amount          = 20
  thresholds      = [50, 80, 100]
  contact_emails  = ["<YOUR_EMAIL>"]
  start_date      = "2026-08-01T00:00:00Z"
}

O subscription_id nunca vai hardcoded — é passado via variável marcada como sensitive = true e populada por TF_VAR_subscription_id no ambiente de execução, nunca commitado:

export TF_VAR_subscription_id="<SUBSCRIPTION_ID>"
export TF_VAR_start_date="2026-08-01T00:00:00Z"

terraform plan e apply

Com o backend remoto inicializado (terraform init -backend-config=backend.hcl), o plan do budget no Azure com Terraform mostra exatamente o que vai ser criado — um recurso, três blocos de notificação:

$ terraform plan -out=tfplan.out

Terraform will perform the following actions:

  # module.budget.azurerm_consumption_budget_subscription.this will be created
  + resource "azurerm_consumption_budget_subscription" "this" {
      + amount          = 20
      + id              = (known after apply)
      + name            = "budget-finops-blog-castilho"
      + subscription_id = (sensitive value)
      + time_grain      = "Monthly"

      + notification {
          + contact_emails = ["<YOUR_EMAIL>"]
          + enabled        = true
          + operator       = "GreaterThanOrEqualTo"
          + threshold      = 50
          + threshold_type = "Actual"
        }
      + notification {
          + threshold = 80
          # ...
        }
      + notification {
          + threshold = 100
          # ...
        }

      + time_period {
          + end_date   = (known after apply)
          + start_date = "2026-08-01T00:00:00Z"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Note que o Terraform marca subscription_id como (sensitive value) no plano — reflexo direto do sensitive = true declarado na variável, que evita vazar o ID em logs de CI/CD. Aprovado o plano, o apply cria o recurso em segundos:

$ terraform apply tfplan.out

module.budget.azurerm_consumption_budget_subscription.this: Creating...
module.budget.azurerm_consumption_budget_subscription.this: Creation complete after 6s [id=/subscriptions/<SUBSCRIPTION_ID>/providers/Microsoft.Consumption/budgets/budget-finops-blog-castilho]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

budget_id   = "/subscriptions/<SUBSCRIPTION_ID>/providers/Microsoft.Consumption/budgets/budget-finops-blog-castilho"
budget_name = "budget-finops-blog-castilho"

Os outputs desse budget no Azure com Terraform devolvem o id completo do recurso e o nome — úteis para referenciar o budget em outro módulo (um dashboard ou uma automação de resposta a alerta, por exemplo) sem precisar redigitar a string.

Confirmando o budget no Azure com Terraform

Depois do apply, o budget no Azure com Terraform aparece em Cost Management + Billing → Cost Management → Budgets na subscription de destino, já com os três thresholds configurados:

Budget no Azure com Terraform: budget-finops-blog-castilho listado em Cost Management + Billing

Abrindo esse budget no Azure com Terraform, a página de detalhe confirma o valor (R$20,00), o consumo avaliado até o momento e os três alertas configurados — 50%, 80% e 100% do valor, todos do tipo Actual cost:

Detalhe do budget no Azure mostrando os três thresholds de alerta em 50%, 80% e 100%

Vale conferir ali a barra de progresso do consumo atual contra o valor definido nesse budget no Azure com Terraform — é a mesma visualização que o Azure usa para dispar os e-mails de alerta quando um threshold é cruzado.

Se quiser consultar o consumo diário via linha de comando antes de decidir o valor do budget, a extensão costmanagement do Azure CLI expõe a API de Cost Management:

az extension add --name costmanagement

az rest --method post \
  --url "https://management.azure.com/subscriptions/<SUBSCRIPTION_ID>/providers/Microsoft.CostManagement/query?api-version=2023-11-01" \
  --body '{
    "type": "ActualCost",
    "timeframe": "MonthToDate",
    "dataset": {
      "granularity": "Daily",
      "aggregation": { "totalCost": { "name": "PreTaxCost", "function": "Sum" } }
    }
  }'

Em subscriptions com pouquíssimo consumo (labs, contas MPN/dev), a API de forecast (/providers/Microsoft.CostManagement/forecast) costuma responder 429 Too Many Requests com mais frequência — o rate limit dela é mais agressivo que o da query de custo real. Nesses casos, uma estimativa simples por média diária × dias do mês já é suficiente para calibrar o valor do budget.

Troubleshooting comum

Problemas mais frequentes de quem está configurando um budget no Azure com Terraform pela primeira vez:

Sintoma Causa Solução
start_date must be the first day of the month start_date informado com dia diferente de 01 Ajustar para o primeiro dia do mês em RFC3339 (ex: 2026-08-01T00:00:00Z)
Alerta não chega no e-mail enabled = false na notification, ou threshold ainda não atingido Conferir enabled no bloco e o consumo atual no portal
429 Too Many Requests na API de Cost Management Rate limit mais agressivo em subscriptions de baixo consumo (comum em contas MPN/dev) Aguardar alguns minutos e repetir a chamada; evitar polling em loop curto
subscription_id aparece em texto puro no state Comportamento esperado do Terraform — o state guarda o valor real mesmo com sensitive = true Usar backend remoto com criptografia e controle de acesso (Storage Account + RBAC), nunca versionar o .tfstate

Conclusão

Um budget criado pelo portal resolve o problema uma vez; um budget no Azure com Terraform resolve o problema para toda subscription nova que você abrir depois. Com o módulo terraform-budget-modules pronto, adicionar um budget a uma subscription nova é só instanciar o módulo com nome, valor e e-mail de contato — sem repetir cliques no portal nem esquecer de configurar os thresholds de alerta.

Referências oficiais

Documentação oficial usada como base para este guia de budget no Azure com Terraform:

Interessado em saber mais sobre artigos relacionados ao Microsoft Azure CLIQUE AQUI

🚀 Vamos nos conectar?

Não perca nenhuma oportunidade! Cadastre-se nas minhas redes e no canal do YouTube para receber conteúdos de TI, Cloud, Azure, Kubernetes e DevOps em primeira mão.

Dica: No Facebook, todos os artigos do blog são publicados automaticamente. Vale a pena curtir!


💬 Dúvidas ou Problemas?

Com o intuito de ajudar a comunidade, caso você tenha dúvidas ou encontre problemas na execução dos comandos deste artigo, deixe um comentário abaixo. Responderei o mais breve possível!

Muito obrigado pela visita e até o próximo post!

Jefferson Castilho Especialista em Cloud & DevOps.

Este guia técnico é exclusivo do Blog do Castilho. Explore mais conteúdos sobre Cloud e DevOps.

 

Deixe uma resposta

Descubra mais sobre Blog do Castilho - Tecnologia | FinOps | DevOps | Cloud

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo