Um budget no Azure avisa quando o custo cruza um limite, mas não te dá o histórico pra analisar tendência, comparar mês a mês ou fazer chargeback por tag. É pra isso que serve o Cost Management Export no Azure com Terraform: um agendamento que exporta os dados de custo automaticamente em CSV para um Storage Account, usando o recurso azurerm_subscription_cost_management_export.
Neste guia você vai ver como criar um módulo Terraform reutilizável que provisiona Storage Account, Container e o export, instanciá-lo na subscription, e confirmar o resultado com terraform plan/apply reais — incluindo três erros que apareceram no caminho e como resolver cada um.
Segurança: os comandos deste guia de Cost Management Export no Azure com Terraform 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
- Criando o módulo terraform-cost-export-modules
- Instanciando o módulo
- terraform plan e apply
- Confirmando o Cost Management Export no Azure com Terraform
- Troubleshooting comum
- Conclusão
- Referências oficiais
Pré-requisitos
Antes de criar o Cost Management Export 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 e Storage na subscription (Cost Management Contributor + Storage Account 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-cost-export-modules
O módulo do Cost Management Export no Azure com Terraform encapsula três recursos: o Storage Account e o Container que recebem os arquivos, e o próprio azurerm_subscription_cost_management_export apontando pra esse container:
# main.tf
resource "azurerm_resource_group" "this" {
name = var.resource_group_name
location = var.location
}
resource "azurerm_storage_account" "this" {
name = var.storage_account_name
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "this" {
name = var.container_name
storage_account_id = azurerm_storage_account.this.id
container_access_type = "private"
}
resource "azurerm_subscription_cost_management_export" "this" {
name = var.name
subscription_id = "/subscriptions/${var.subscription_id}"
recurrence_type = var.recurrence_type
recurrence_period_start_date = var.start_date
recurrence_period_end_date = var.end_date
active = true
export_data_storage_location {
container_id = azurerm_storage_container.this.id
root_folder_path = var.root_folder_path
}
export_data_options {
type = var.export_type
time_frame = var.time_frame
}
}
Repare em container_id = azurerm_storage_container.this.id — não resource_manager_id. Esse último ainda funciona no provider 4.x, mas já sai com aviso de depreciação; a partir da v5 deixa de existir.
# variables.tf
variable "name" { type = string }
variable "subscription_id" { type = string }
variable "resource_group_name" { type = string }
variable "storage_account_name" { type = string } # globalmente único, lowercase, sem hífen
variable "location" { type = string; default = "eastus" }
variable "container_name" { type = string; default = "cost-exports" }
variable "root_folder_path" { type = string; default = "exports" }
variable "recurrence_type" { type = string; default = "Monthly" } # Daily, Weekly, Monthly, Annually
variable "start_date" { type = string } # RFC3339, não pode ser data passada: "2026-08-19T00:00:00Z"
variable "end_date" { type = string }
variable "export_type" {
type = string
default = "Usage" # ActualCost, AmortizedCost, Usage — algumas subscriptions só aceitam Usage
}
variable "time_frame" {
type = string
default = "MonthToDate" # MonthToDate, BillingMonthToDate, TheLastMonth, TheLastBillingMonth, WeekToDate
}
Diferente do budget, montar esse Cost Management Export no Azure com Terraform exige um Storage Account com nome globalmente único no Azure inteiro — e nomes de storage account não aceitam hífen, só letras minúsculas e números.
Instanciando o módulo
O ambiente que efetivamente cria o Cost Management Export no Azure com Terraform só passa os valores específicos: recorrência mensal, período coberto MonthToDate, exportando em CSV:
locals {
workload = "finops"
resource_group_name = "rg-${local.workload}-blog-castilho-eastus"
storage_account_name = "st${local.workload}blogcastilho"
export_name = "export-${local.workload}-blog-castilho"
}
module "cost_export" {
source = "../../terraform-cost-export-modules"
name = local.export_name
subscription_id = var.subscription_id
resource_group_name = local.resource_group_name
location = var.location
storage_account_name = local.storage_account_name
container_name = var.container_name
recurrence_type = var.recurrence_type
start_date = var.start_date
end_date = var.end_date
}
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-19T00:00:00Z"
export TF_VAR_end_date="2031-08-19T00:00:00Z"
terraform plan e apply
Com o backend remoto inicializado (terraform init -backend-config=backend.hcl), o plan desse Cost Management Export no Azure com Terraform mostra os quatro recursos a criar — resource group, storage account, container e o export:
$ terraform plan -out=tfplan.out
# module.cost_export.azurerm_resource_group.this will be created
# module.cost_export.azurerm_storage_account.this will be created
# module.cost_export.azurerm_storage_container.this will be created
# module.cost_export.azurerm_subscription_cost_management_export.this will be created
+ resource "azurerm_subscription_cost_management_export" "this" {
+ active = true
+ file_format = "Csv"
+ name = "export-finops-blog-castilho"
+ recurrence_period_end_date = "2031-08-19T00:00:00Z"
+ recurrence_period_start_date = "2026-08-19T00:00:00Z"
+ recurrence_type = "Monthly"
+ subscription_id = (sensitive value)
+ export_data_options {
+ time_frame = "MonthToDate"
+ type = "Usage"
}
+ export_data_storage_location {
+ container_id = (known after apply)
+ root_folder_path = "exports"
}
}
Plan: 4 to add, 0 to change, 0 to destroy.
Aprovado o plano, o apply cria os quatro recursos — o storage account é o que mais demora, por volta de 1 a 2 minutos:
$ terraform apply tfplan.out
module.cost_export.azurerm_resource_group.this: Creation complete after 27s
module.cost_export.azurerm_storage_account.this: Creation complete after 1m20s
module.cost_export.azurerm_storage_container.this: Creation complete after 12s
module.cost_export.azurerm_subscription_cost_management_export.this: Creation complete after 8s
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
export_id = "/subscriptions/<SUBSCRIPTION_ID>/providers/Microsoft.CostManagement/exports/export-finops-blog-castilho"
export_name = "export-finops-blog-castilho"
storage_account_name = "stfinopsblogcastilho"
Confirmando o Cost Management Export no Azure com Terraform
Depois do apply, o Cost Management Export no Azure com Terraform aparece em Cost Management + Billing → Cost Management → Exports, já ativo e agendado mensalmente. Como o próximo ciclo automático só roda no mês seguinte, o botão Run now força a primeira execução na hora, sem precisar esperar:

Depois do Run now, o arquivo CSV aparece no Storage Browser, dentro do container configurado, numa subpasta nomeada com o período coberto pelo export:

Esse CSV pode ser aberto direto no Excel, importado no Power BI, ou lido por qualquer pipeline que processe custo — sem precisar consultar a API de Cost Management sob demanda. A partir daqui, um segundo agendamento (Data Factory, Logic App ou Function) pode consumir o mesmo container e alimentar um dashboard de FinOps automaticamente, tudo alimentado pelo mesmo Cost Management Export no Azure com Terraform configurado nesse guia.
Troubleshooting comum
Três erros reais apareceram durante o apply desse Cost Management Export no Azure com Terraform, nessa ordem — documentados aqui porque a mensagem de erro do provider nem sempre deixa óbvia a causa:
| Sintoma | Causa | Solução |
|---|---|---|
400: RP Not Registered. Register destination storage account subscription with Microsoft.CostManagementExports | Resource provider de Cost Management Exports nunca foi usado nessa subscription | az provider register --namespace Microsoft.CostManagementExports e aguardar o estado virar Registered antes de reaplicar |
Invalid definition type 'ActualCost'; valid values: 'Usage' | Subscriptions de certos tipos (ex: MPN) só aceitam export_type = "Usage" nesse recurso | Trocar type para "Usage" em export_data_options |
Invalid schedule recurrencePeriod; 'from' value cannot be in the past | recurrence_period_start_date apontando pra uma data já passada (ex: primeiro dia do mês atual) | Usar uma data de início igual ou posterior a hoje |
Conclusão
Budget avisa quando o custo passa do limite; Cost Management Export no Azure com Terraform garante que você tem o histórico completo pra investigar o porquê, comparar tendências e montar dashboards — sem depender de consultar a API sob demanda toda vez. Com o módulo terraform-cost-export-modules pronto, replicar esse Cost Management Export no Azure com Terraform pra outra subscription é só trocar o workload e reaplicar.
Referências oficiais
Documentação oficial usada como base para este guia de Cost Management Export no Azure com Terraform:
- azurerm_subscription_cost_management_export (Terraform Registry)
- Criar e gerenciar exports no Azure Cost Management (Microsoft Learn)
- Dados exportados armazenados em Storage Account (Microsoft Learn)
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.