Na parte um colocamos orçamentos, alocações, alertas e relatórios sob controle de versão. De lá para cá, o DoiT Terraform provider lançou cinco releases minor em cerca de dois meses (da v1.6 à v1.10).
Tanto o número de organizações que usam o provider quanto o tráfego de API cresceram consideravelmente desde então. As alocações respondem pela maior parte desse tráfego, e toda organização que lê seus orçamentos pelo provider também o usa para escrevê-los — seja criando, atualizando ou excluindo orçamentos.
Por isso, este post segue em duas direções:
- Mais fundo nos recursos principais. São padrões que você descobre depois de alguns meses de FinOps as code: gerar alocações, orçamentos e alertas a partir de um único mapa de times, health checks em tempo de plan e relatórios que saem do console como PDFs.
- Mais amplo, cobrindo o próprio tenant. Configurações no nível do cliente, roles customizadas, onboarding de usuários e service accounts com tokens rotativos. No final, um relatório semanal de FinOps vai rodar no GitHub Actions sem a chave de API pessoal de ninguém.
O provider agora cobre 20 resources e 85 data sources. Cada trecho de código abaixo foi testado em um tenant real da DoiT antes de entrar neste post (aplicado, replanejado para detectar drift e destruído onde era seguro fazer isso), e a maioria também está nos exemplos da documentação do provider.
Atualizando a partir de uma release v1.x anterior
Se você configurou tudo depois do primeiro post, provavelmente está na v1.5 ou anterior. A maioria dos times atualiza rápido (a grande maioria das organizações ativas já está na v1.6 ou superior), e o upgrade em si costuma ser tranquilo. No entanto, alguns atributos obsoletos foram removidos pelo caminho, e estes são os que podem te pegar de surpresa:
| Versão | Mudança | O que fazer |
|---|---|---|
| v1.7 | max_results nos data sources de listagem é um número |
Remova as aspas: max_results = 50 |
| v1.8 | doit_budget.scope removido |
Use scopes: um filtro cuja lista values contém todos os IDs de alocação |
| v1.8 | doit_alert.config.attributions removido |
Use config.scopes |
| v1.8 | doit_report.config.metric removido |
Use config.metrics (até quatro) |
| v1.8 | Componente inverse_selection de doit_allocation removido |
Use inverse |
| v1.8 | Layouts de relatório csv_export / sheets_export removidos |
Escolha um layout de visualização, como table |
| v1.8 | request_timeout precisa ser maior que 120s (padrão 150s) |
Aumente ou remova valores customizados |
| v1.8 | Resources e data sources de Insights removidos | A API de Insights upstream foi descontinuada |
| v1.10 | Operadores de limit_by_change usam códigos curtos |
gt, gte, lt, lte, b, nb em vez de >, >=, … |
| v1.10 | doit_user.job_function removido |
Use job_title |
O guia de remoção de atributos obsoletos traz os detalhes de migração para cada remoção.
Desde a v1.8, você também ganha mais robustez de graça. Os relatórios rodam pela API de execução assíncrona, em que o provider envia a requisição, consulta o status e busca o resultado para você. Requisições limitadas por rate limit fazem backoff corretamente, e requisições lentas exibem o erro da própria API em vez de estourarem o timeout local primeiro.
Padrões avançados para os recursos principais de FinOps
Alocações, orçamentos, alertas e relatórios são os quatro recursos principais da parte um, e respondem pela maior parte do tráfego de API do provider.
Alocações em escala: um mapa, todos os times
Se a sua configuração tem uma alocação, um orçamento e um alerta para cada time, não copie e cole. Em vez disso, defina seus times uma única vez:
locals { teams = { payments = { budget = 12000, label = "payments" } search = { budget = 8000, label = "search" } platform = { budget = 20000, label = "platform" } }}
# One single allocation per team, with anomaly detection switched onresource "doit_allocation" "team" { for_each = local.teams
name = "Team: ${each.key}" description = "All costs labeled owner=${each.value.label}" anomaly_detection = true rule = { formula = "A" components = [{ type = "label" key = "owner" mode = "is" values = [each.value.label] }] }}
# The same teams as one group allocation that selects the singles aboveresource "doit_allocation" "by_team" { name = "Cost by Team" description = "Team allocations combined into one group" unallocated_costs = "Unowned" rules = [for team in doit_allocation.team : { action = "select" id = team.id }]}anomaly_detection passou a ser gravável em alocações individuais na v1.7, então o monitoramento de anomalias vive na sua configuração em vez de ser um checkbox que alguém precisa lembrar de marcar. A alocação de grupo também seleciona as alocações dos times em vez de redefini-las.
Dica: sempre monte grupos com
select. Você também pode definir regras de grupo inline comaction = "create", e a API da DoiT cria exatamente as mesmas alocações individuais nos bastidores. Mas essas alocações não são resources próprios do Terraform, então regras inline não conseguem definir coisas comoanomaly_detectionoufolder_id. Alocações individuais separadas maisselectte dão resources de verdade, gerenciados.
Como cada time agora tem uma alocação real com detecção de anomalias, você pode perguntar quais times estão gerando anomalias e obter uma explicação de IA para a mais cara:
# All anomalies (no max_results, so every page is fetched), sorted so the# costliest one comes firstdata "doit_anomalies" "by_cost" { sort_by = "costOfAnomaly" sort_order = "desc"}
locals { # An anomaly can belong to several allocations; count it under each one anomaly_cost_by_allocation = { for name, costs in { for pair in flatten([ for a in data.doit_anomalies.by_cost.anomalies : [ for al in a.allocations : { name = al.name, cost = a.cost_of_anomaly } ] ]) : pair.name => pair.cost... } : name => { anomalies = length(costs), total_cost = sum(costs) } }}
data "doit_anomaly_explanation" "costliest" { count = length(data.doit_anomalies.by_cost.anomalies) > 0 ? 1 : 0 id = data.doit_anomalies.by_cost.anomalies[0].id}
output "costliest_anomaly_explanation" { value = one(data.doit_anomaly_explanation.costliest[*].explanation.text) sensitive = true}O atributo allocations nas anomalias (v1.10) lista todas as alocações às quais uma anomalia pertence, e doit_anomaly_explanation (v1.8) retorna a explicação gerada junto com os fatos por trás dela. No nosso tenant de teste, a principal anomalia voltou como "provavelmente causada por um pico no volume de requisições do Amazon SNS Tier 1, especialmente nas regiões US West 2 e US West 1".
Orçamentos que acompanham as alocações
Com o mapa de times pronto, orçamentos são só mais um for_each. Orçamentos também podem notificar no Slack, e desde a v1.8 o provider valida a configuração do Slack antes do apply:
resource "doit_budget" "team" { for_each = local.teams
name = "Team: ${each.key}" type = "recurring" time_interval = "month" currency = "USD" amount = each.value.budget start_period = 1767225600000 # 2026-01-01T00:00:00Z in milliseconds
alerts = [{ percentage = 80 }, { percentage = 100 }]
scopes = [{ type = "allocation_rule" id = "allocation_rule" mode = "is" values = [doit_allocation.team[each.key].id] }]
collaborators = [{ email = data.doit_current_user.me.email, role = "owner" }] recipients = [data.doit_current_user.me.email] recipients_slack_channels = [{ id = "C0123456789" customer_id = "your-customer-id" workspace = "your-slack-workspace" }]}Adicionar um time agora é uma mudança de uma linha em local.teams, e a alocação, o orçamento e (na próxima seção) o alerta vêm junto automaticamente.
Desde a v1.8, doit_budgets também expõe a classificação de risco calculada pela DoiT no lado do servidor: um risk_status por orçamento (onTrack, atRisk ou unknown), e dá para filtrar por ele. Combine isso com um check block do Terraform e cada terraform plan avisa quando o orçamento de um time está entrando em zona de risco:
check "team_budgets_on_track" { data "doit_budgets" "team_at_risk" { name_contains = "Team: " filter = "riskStatus:atRisk" }
assert { condition = data.doit_budgets.team_at_risk.row_count == 0 error_message = "Budgets at risk: ${join(", ", [for b in data.doit_budgets.team_at_risk.budgets : b.budget_name])}" }}Check blocks geram warnings, não erros, então nunca bloqueiam um apply — mas quem revisa o plan vê o problema. Para orçamentos sugeridos por IA, o data source doit_budget_suggestions (v1.7) lista as recomendações pendentes com o nível de confiança e o valor sugerido, para que um humano possa revisá-las em um pull request.
Alertas que valem a pena ativar
Nos nossos dados de uso, alertas são o menos usado dos quatro recursos principais, e bem menos organizações gerenciam alertas pelo Terraform do que orçamentos. É uma lacuna que vale a pena fechar, porque os alertas receberam duas das adições mais práticas desde julho.
O problema clássico dos alertas de variação percentual é o ruído. Um serviço que custa US$ 2 por dia pula para US$ 10 e você recebe um alerta de "+400%" por oito dólares. ignore_values_range (v1.9) pula qualquer avaliação em que o valor da métrica esteja dentro dos limites, e recipients_slack_channels envia o alerta para onde o seu time realmente olha:
resource "doit_alert" "team_spike" { for_each = local.teams
name = "Team ${each.key}: daily cost spike" config = { metric = { type = "basic", value = "cost" } time_interval = "day" condition = "percentage-change" operator = "gt" value = 30 # percent currency = "USD" ignore_values_range = { lower_bound = 0 upper_bound = 50 # USD per day } # Evaluate each service separately, so one service spiking is enough evaluate_for_each = "fixed:service_description" scopes = [{ type = "label" id = "owner" mode = "is" values = [each.value.label] }] } recipients = [data.doit_current_user.me.email] recipients_slack_channels = [{ id = "C0123456789" shared = true }]}Note que os limites são em unidades da métrica (aqui, USD por dia), não em porcentagem, e só se aplicam a condition = "percentage-change". Um canal compartilhado do Slack usa o ID e shared = true; um canal em um workspace conectado usa o ID e workspace = "<name>".
Relatórios: Top Movers, previsões e novos visuais
Duas adições de relatórios da v1.6 se destacam.
Top Movers. limit_by_change filtra linhas pela variação entre períodos, e limit_aggregation controla como as linhas excluídas são exibidas. Este relatório mantém apenas serviços cujo custo total está acima de US$ 1.000 e que cresceram 50% ou mais:
resource "doit_report" "top_movers" { name = "Top Movers" description = "Services with significant month-over-month growth" config = { metrics = [{ type = "basic", value = "cost" }] aggregation = "total" data_source = "billing" time_interval = "month" dimensions = [{ id = "service_description", type = "fixed" }] time_range = { mode = "last" amount = 3 include_current = true unit = "month" } metric_filter = { metric = { type = "basic", value = "cost" } operator = "gt" values = [1000] operand = "series_total" } limit_by_change = { metric = { type = "basic", value = "cost" } change_type = "percentage" operator = "gte" values = [50] include_incomplete_data = false } limit_aggregation = "top" display_values = "actuals_only" layout = "table" currency = "USD" }}Previsões e temas. forecast_settings projeta gastos futuros a partir de dados históricos, e os layouts da v1.8 (column_and_line_chart, sankey_chart, trend_board e cumulative_comparison) oferecem mais formas de exibi-los. Adicione display_settings.theme_id e o relatório usa as cores da sua marca:
resource "doit_custom_theme" "brand" { name = "Brand" primary_color = "#1A73E8" colors = { light = ["#1A73E8", "#34A853", "#FBBC04", "#EA4335", "#A142F4", "#24C1E0"] dark = ["#8AB4F8", "#81C995", "#FDD663", "#F28B82", "#C58AF9", "#78D9EC"] }}
resource "doit_report" "forecast" { name = "Spend Forecast" description = "Last 12 months of spend with a 3-month forecast" config = { metrics = [{ type = "basic", value = "cost" }] aggregation = "total" time_interval = "month" currency = "USD" data_source = "billing" time_range = { mode = "last" amount = 12 unit = "month" include_current = true } dimensions = [ { id = "year", type = "datetime" }, { id = "month", type = "datetime" }, ] forecast_settings = { mode = "totals" historical_time_intervals = 12 future_time_intervals = 3 } layout = "column_and_line_chart" display_values = "actuals_only" sort_dimensions = "a_to_z" display_settings = { theme_id = doit_custom_theme.brand.id number_scale = "thousands" } }}Uma ressalva: os valores de previsão ainda não entram nos resultados nem nas exportações dos relatórios. O tema, por outro lado, é aplicado do início ao fim, como mostra a próxima seção.
Relatórios saem do console: exportação em PDF e PNG
Agora você consegue tirar um relatório da DoiT como arquivo, sem ninguém abrir o console. Desde a v1.10, doit_report_result e doit_report_query aceitam file_output = "pdf" ou "png" e retornam uma URL de download assinada:
data "doit_report_result" "forecast_pdf" { id = doit_report.forecast.id file_output = "pdf"
lifecycle { postcondition { condition = self.file_output_url != null error_message = "PDF rendering failed. Try applying again." } }}
data "http" "forecast_pdf" { url = data.doit_report_result.forecast_pdf.file_output_url}
resource "local_sensitive_file" "forecast_pdf" { filename = "${path.module}/spend-forecast.pdf" content_base64 = data.http.forecast_pdf.response_body_base64}O PDF é um documento caprichado, com várias páginas e o nome da sua organização no cabeçalho, e o seu tema customizado vem junto: como o relatório de previsão acima define display_settings.theme_id, o PDF também sai nas cores da sua marca.
Troque local_sensitive_file por um aws_s3_object (que aceita o mesmo content_base64) e o PDF vai parar em um bucket. As execuções de relatório passam pela API de execução assíncrona da DoiT, então relatórios pesados podem demorar: aumente o timeout de leitura do data source com timeouts = { read = "30m" } se precisar.
Você nem precisa de um relatório salvo. doit_report_query renderiza uma consulta ad-hoc igualmente bem, o que é ótimo para um gráfico em um README, um comentário de pull request ou uma página de wiki:
data "doit_report_query" "top_services_png" { file_output = "png" config = { metrics = [{ type = "basic", value = "cost" }] aggregation = "total" time_interval = "month" currency = "USD" time_range = { mode = "last", amount = 3, unit = "month", include_current = false } dimensions = [ { id = "year", type = "datetime" }, { id = "month", type = "datetime" }, ] # Top 5 services by cost group = [{ id = "service_description" type = "fixed" limit = { value = 5, sort = "desc", metric = { type = "basic", value = "cost" } } }] layout = "stacked_column_chart" sort_dimensions = "a_to_z" }}
Dica: defina
sort_dimensions = "a_to_z"nas exportações. Sem isso, o gráfico renderizado ordena o eixo de tempo por valor, e setembro pode acabar aparecendo antes de agosto. Com isso, os meses ficam em ordem de calendário.
A URL de download é sensível e válida por até sete dias. Cada plan renderiza um arquivo novo, que é exatamente o que você quer para um relatório recorrente. Vamos colocar isso em prática no grande final.
Seu tenant como código
Até aqui, falamos só de dados de custo. Mas um dos recursos mais adotados do provider é o onboarding do AWS CloudConnect, com o resource e o módulo do post anterior. Os times claramente querem o setup da DoiT em código, não só os relatórios, e as três últimas releases cobrem o resto do tenant.
Configurações no nível do cliente
doit_customer (v1.8) gerencia as configurações no nível da organização. Você não cria um customer, você adota o que já tem — então comece com um import:
import { to = doit_customer.main id = "your-customer-id"}
resource "doit_customer" "main" { url_slug = "my-company"
settings = { currency = "USD" allowed_invite_domains = ["mycompany.com"] mfa_required = true }
contact = { emails = ["finops@mycompany.com"] }}Seu primeiro plan mostra exatamente como as configurações de hoje diferem do que você escreveu, o que já é uma auditoria útil por si só. Depois disso, desligar o MFA ou liberar um novo domínio de convite vira um pull request que alguém precisa aprovar, não um botão que qualquer um pode acionar.
Roles customizadas e usuários
doit_role (v1.10) gerencia roles customizadas. Os IDs de permissão são opacos, então não os digite na mão: derive-os das roles predefinidas. Esta role combina dois presets em um:
data "doit_roles" "all" {}
locals { preset_permissions = { for r in data.doit_roles.all.roles : r.name => r.permissions if r.type == "preset" }}
resource "doit_role" "finops_analyst" { name = "FinOps Analyst" description = "View Only plus everything a Finance User can see" permissions = sort(distinct(concat( local.preset_permissions["View Only"], local.preset_permissions["Finance User"], )))}O onboarding vira então um mapa, igual ao dos times lá atrás:
locals { analysts = { "ana@mycompany.com" = { first_name = "Ana", last_name = "Lopez" } "ravi@mycompany.com" = { first_name = "Ravi", last_name = "Patel" } }}
resource "doit_user" "analyst" { for_each = local.analysts
email = each.key first_name = each.value.first_name last_name = each.value.last_name job_title = "Finance / Accounting" role_id = doit_role.finops_analyst.id}Configurações assim precisam de uma identidade com direitos suficientes para gerenciar usuários. Desde a v1.9, doit_current_user expõe as permissions efetivas de quem está chamando, então você pode falhar cedo com uma mensagem legível em vez de um 403 no meio do apply:
data "doit_current_user" "me" { lifecycle { postcondition { condition = alltrue([for p in ["UsersManager", "ServiceAccountManager"] : contains(self.permissions, p)]) error_message = "The identity running Terraform needs the UsersManager and ServiceAccountManager permissions." } }}Service accounts e tokens rotativos
Service accounts são o grande destaque da v1.10. Pipelines que falam com a API da DoiT muitas vezes rodam com a chave de API pessoal de alguém, com todas as permissões dessa pessoa. Uma service account dá à automação uma identidade própria, apenas com as permissões necessárias, e doit_service_account e doit_service_account_token gerenciam tanto a conta quanto seus tokens no Terraform.
Derive o nome e a expiração do token de um resource time_rotating e o Terraform emite um token novo sempre que a data de rotação tiver passado:
resource "doit_service_account" "ci" { name = "ci-pipeline" description = "Runs Terraform from CI" permissions = ["cloudAnalyticsUser", "budgetsReadOnly"]}
resource "time_rotating" "ci_token" { rotation_days = 90}
resource "doit_service_account_token" "ci" { service_account_id = doit_service_account.ci.id name = "ci-${formatdate("YYYYMMDD-hhmmss", time_rotating.ci_token.rfc3339)}" expires_time = timeadd(time_rotating.ci_token.rotation_rfc3339, "168h")
lifecycle { create_before_destroy = true }}Quatro detalhes fazem isso funcionar:
- Mudar
nameouexpires_timesubstitui o token, então é o timestamp de rotação no nome que dispara a emissão de um token novo. Isso também mantém os nomes únicos — o que é obrigatório enquanto o token antigo e o novo coexistem por um breve período. create_before_destroygarante que o token novo existe antes de o antigo ser excluído. Forçamos uma rotação durante os testes: o Terraform criou o token novo, excluiu o antigo e o plan seguinte veio limpo.- A expiração é uma semana depois da data de rotação, então o token continua funcionando mesmo se ninguém rodar o próximo apply na hora certa.
access_tokené retornado uma única vez, na criação. Ele é marcado como sensível. Entregue-o para onde for necessário dentro do mesmo apply.
O token é um token de API normal. Defina-o como DOIT_API_TOKEN e o provider (ou qualquer outro cliente da API da DoiT) se autentica como a service account e enxerga apenas as permissões que você concedeu.
Dica: service accounts não podem ter mais poder do que quem as criou. Solicitar permissões que o chamador não possui falha com 403. Repare também na nomenclatura: permissões de service account usam camelCase (
cloudAnalyticsUser), enquantodoit_current_user.permissionsas reporta em PascalCase (CloudAnalyticsUser).
Cloud bill shouldn't be a mystery
One platform for AI and Cloud optimization.
Notas rápidas
Mais algumas adições que valem a pena conhecer:
doit_service_quotasmostra a utilização de quotas da AWS e do GCP. Combine com um check block e cada plan avisa antes de você esbarrar em um limite:
check "service_quota_headroom" { data "doit_service_quotas" "hot" { min_utilization_percent = 80 }
assert { condition = length(data.doit_service_quotas.hot.items) == 0 error_message = "Quotas at or above 80%: ${join(", ", [for q in data.doit_service_quotas.hot.items : "${q.cloud_provider}/${q.service}/${q.quota} (${q.utilization_percent}%)"])}" } }No nosso tenant de teste, isso disparou imediatamente para uma quota de VPC da AWS em 80%. Note que a leitura de quotas exige a permissão Manage Settings.
doit_billing_explainerdetalha uma fatura por payer, incluindo créditos, descontos, economias e cobranças de marketplace.doit_widget/doit_widgetsexpõem os dados dos widgets de dashboard do Cloud Analytics.doit_cloudconnect_supported_featuresmostra quais recursos e permissões estão ativos em cada conta AWS conectada.- Os filtros
name_containsemdoit_alerts,doit_allocations,doit_budgets,doit_labelsedoit_reportstransformam "encontrar tudo que pertence ao time X" em uma linha só. - Os data sources do PerfectScale for Commitments (
doit_ps4c_*) e o resourcedoit_support_request_tagscompletam as releases.
Juntando tudo: um relatório semanal de FinOps sem chave de API humana
O último exemplo combina uma service account, um token rotativo, um check block, uma exportação em PDF e a Ava, a assistente de IA da DoiT, em um pipeline do GitHub Actions. Ele roda toda segunda-feira e publica um resumo de FinOps com o relatório completo anexado, usando duas pequenas configurações Terraform:
- Bootstrap, executada uma única vez por um admin: cria uma service account com permissões mínimas, um token rotativo e o relatório, e então entrega o token e o ID do relatório ao GitHub. É o snippet de service account de cima mais o relatório, com dois resources do provider do GitHub no final:
resource "github_actions_secret" "doit_api_token" { repository = "doit-finops-demo" secret_name = "DOIT_API_TOKEN" plaintext_value = doit_service_account_token.reporting.access_token }
resource "github_actions_variable" "report_id" { repository = "doit-finops-demo" variable_name = "DOIT_REPORT_ID" value = doit_report.weekly.id }Reexecutar o bootstrap depois da data de rotação emite um token novo e atualiza o secret no mesmo apply, então o pipeline nem percebe.
- Reporting, executada pelo pipeline: um check block de orçamento, a exportação em PDF de antes e uma pergunta para a Ava. Não contém resources além do arquivo baixado, então não precisa de remote state:
data "doit_ava" "summary" { question = "Summarize report ${var.report_id} in three short bullet points for an engineering audience. Mention the biggest week-over-week change." }
output "summary" { value = <<-EOT ## Weekly FinOps report
${data.doit_ava.summary.answer}
_The full report is attached to this run as `weekly-cost-report`._ EOT }O workflow em si é curto:
name: Weekly FinOps report
on: schedule: - cron: "0 7 * * 1" # Mondays 07:00 UTC workflow_dispatch:
jobs: report: runs-on: ubuntu-latest defaults: run: working-directory: reporting env: DOIT_API_TOKEN: ${{ secrets.DOIT_API_TOKEN }} TF_VAR_report_id: ${{ vars.DOIT_REPORT_ID }} steps: - uses: actions/checkout@v7 - uses: hashicorp/setup-terraform@v4 with: terraform_wrapper: false - run: terraform init -input=false - run: terraform apply -auto-approve -input=false - run: terraform output -raw summary >> "$GITHUB_STEP_SUMMARY" - uses: actions/upload-artifact@v7 with: name: weekly-cost-report path: reporting/weekly-cost-report.pdfAqui está uma execução real. O check block sinalizou um orçamento em risco no log, o resumo da Ava está na página da execução e o PDF está anexado como artefato:


Olhe o terceiro bullet do resumo da Ava e depois o gráfico: o salto do Cloud SQL na semana de 7 de setembro é exatamente o pico roxo na semana 37. O resumo da Ava é gerado por IA, então o texto muda de uma execução para outra, mas rende um primeiro parágrafo útil para uma manhã de segunda-feira.
O que aprendemos construindo isso:
- Mantenha as permissões do robô no mínimo. A service account do pipeline tem só duas permissões:
cloudAnalyticsUserebudgetsReadOnly. É também por isso que o check de quotas fica fora deste pipeline, já que exigiria Manage Settings. - O robô só enxerga orçamentos públicos. Service accounts não têm endereço de e-mail, então não dá para adicionar uma como colaboradora de orçamento. Orçamentos que o pipeline deve verificar precisam de
public = "viewer"ou superior nodoit_budget. - Ninguém copia token na mão. O token vai da API da DoiT direto para o secret do GitHub dentro de um único apply. A única outra cópia é o state do Terraform do bootstrap, então guarde esse state em um lugar seguro.
Para encerrar
Recapitulando:
- Os recursos principais ganharam profundidade: gere alocações, orçamentos e alertas a partir de um único mapa, monte alocações de grupo com
selecte deixe os check blocks revisarem seus orçamentos a cada plan. - Relatórios saem do console: renderize relatórios salvos e consultas ad-hoc como PDFs e PNGs, com o seu próprio tema.
- Seu tenant agora é código: configurações, roles, usuários e service accounts com tokens rotativos, para que seus pipelines não precisem mais emprestar a chave de API de uma pessoa.
Para onde ir a partir daqui:
- Atualize para a v1.10 com a ajuda da tabela no início deste post.
- Comece pelo mapa de times. Ele reúne alocações, orçamentos e alertas em uma única configuração.
- Tire seus pipelines das chaves de API pessoais e migre para uma service account com token rotativo.
- Acompanhe as releases do repositório (Watch → Custom → Releases). O provider suporta tudo o que a API da DoiT oferece, então novas funcionalidades da API aparecem lá conforme são adicionadas.
O provider é open source em github.com/doitintl/terraform-provider-doit, e a documentação traz a maioria dos exemplos deste post, e muitos outros. Se algo estiver faltando ou parecer estranho, abra uma issue e conte para a gente.