Guia passo a passo: agendamento automático de análise de custos com notificação no Microsoft Teams
Introdução
Um dos maiores desafios em ambientes cloud é manter a visibilidade sobre os custos. Picos inesperados podem passar despercebidos por dias e quando chegam na fatura, já é tarde demais.
Neste artigo, vou mostrar como configurei uma análise automática diária de anomalias de custo usando o Azure SRE Agent, conectado ao FinOps Hub (Azure Data Explorer), com notificação automática no Microsoft Teams quando algo fora do padrão é detectado.
🏗️ Arquitetura da Solução
FinOps Hub (ADX) → SRE Agent (Scheduled Task) → KQL Anomaly Detection
↓ anomalia detectada?
SIM → Adaptive Card → Teams Channel
NÃO → Log: 'custos dentro do esperado'
Pré-requisitos
|
|
Requisito |
Detalhe |
|
✅ |
FinOps Hub implantado |
Com dados de custo sendo ingeridos no Azure Data Explorer |
|
✅ |
Azure SRE Agent configurado |
Com acesso à subscription onde o FinOps Hub está |
|
✅ |
Canal no Microsoft Teams |
Onde as notificações serão postadas |
|
✅ |
Permissões adequadas |
Managed Identity com Viewer no database Hub do ADX |
Passo a Passo
① Identificar o Cluster ADX do FinOps Hub
O primeiro passo é localizar o cluster Azure Data Explorer onde o FinOps Hub armazena os dados de custo. No Azure SRE Agent, usamos o Azure Resource Graph:
az graph query -q "Resources | where type =~ 'Microsoft.Kusto/clusters'" --first 10 --subscription <subscription-id>
No meu caso como exemplo, o cluster retornado foi:
|
Propriedade |
Valor |
|
Nome |
finopshubadx |
|
URI |
https://finopshubadx.northcentralus.kusto.windows.net |
|
Database |
Hub |
|
SKU |
Dev(No SLA)_Standard_D11_v2 |
|
Região |
North Central US |
② Configurar Permissões (RBAC no ADX)
A managed identity do SRE Agent precisa de acesso Viewer ao database Hub do ADX. Sem isso, a query KQL falhará com erro 403.
Opção A — Azure CLI:
az kusto database-principal-assignment create \
--cluster-name finopshubadx \
--database-name Hub \
--resource-group <resource-group> \
--subscription <subscription-id> \
--principal-assignment-name "sre-agent-viewer" \
--principal-id "<managed-identity-client-id>" \
--principal-type App --role Viewer
Opção B — Portal:
ADX → Cluster → Database Hub → Permissions → Add → Viewer → Selecionar managed identity
Opção C — KQL (ADX Web UI):
.add database Hub viewers ('aadapp=<client-id>') 'SRE Agent'
③ A Query KQL de Detecção de Anomalias
Esta query utiliza series_decompose_anomalies do Kusto para detectar padrões anômalos nos custos diários ao longo dos últimos 12 meses:
let numberOfMonths = 12;
let start = startofmonth(ago(numberOfMonths * 30d));
let end = now();
let interval = 1d;
Costs()
| where ChargePeriodStart between (start .. end)
| summarize DailyCost = sum(EffectiveCost)
by bin(ChargePeriodStart, interval)
| make-series CostSeries = sum(DailyCost)
on ChargePeriodStart from start to end step interval
| extend anomalies = series_decompose_anomalies(CostSeries)
| project ChargePeriodStart, CostSeries, anomalies
💡 Como a query funciona
- Costs() → Função do FinOps Hub que retorna dados de custo consolidados
- make-series → Cria série temporal diária contínua (sem gaps)
- series_decompose_anomalies → Algoritmo ML nativo do Kusto que detecta spikes e drops usando decomposição sazonal + STL
- Score > 0 → custo acima do esperado | Score < 0 → abaixo do esperado
- Quanto maior o |score|, mais significativa a anomalia
④ Configurar Webhook no Microsoft Teams
Para receber notificações no Teams, criamos um Incoming Webhook via Power Automate Workflows (método recomendado pela Microsoft):
- No canal do Teams, clique nos 3 pontos (...) → Workflows
- Pesquise "Post to a channel when a webhook request is received"
- Selecione o Team e Channel desejados → Add workflow
- Copie a URL gerada (formato: https://prod-xx...logic.azure.com/...)
- Ative o workflow no Power Automate (My flows → Turn on)
⚠️ Atenção: O workflow pode ser criado com status 'Suspended'. Ative em: Power Automate → My flows → Turn on.
⑤ Criar a Scheduled Task no SRE Agent
Agora a parte principal: criar a tarefa agendada (Scheduled Task) no Azure SRE Agent. Basta pedir em linguagem natural:
"Crie uma tarefa diária para analisar anomalias de custo no FinOps Hub e me notificar pelo Teams quando houver anomalias detectadas."
O SRE Agent criará a task com os seguintes parâmetros:
|
Parâmetro |
Valor |
|
Nome |
Daily FinOps Cost Anomaly Analysis |
|
Schedule |
0 8 * * * (diariamente às 08:00 UTC) |
|
ADX Cluster |
https://finopshubadx.northcentralus.kusto.windows.net |
|
Database |
Hub |
|
Query |
KQL com series_decompose_anomalies (12 meses) |
|
Notificação |
Adaptive Card no Teams via webhook |
|
Escalação |
HIGH se anomalias por 3+ dias consecutivos |
|
Idempotência |
Sem anomalia = 'custos dentro do esperado' |
Task details:
Obs: Modificar os campos na task para adequar seu ambiente
Autonomous Scheduled Run - Daily FinOps Cost Anomaly Analysis
## Scope
- Subscription: xxxxxxx-xxxxxxx-xxxxxx-xxxx-xxxxx
- ADX Cluster: https://finopshub.northcentralus.kusto.windows.net
- Database: Hub
- Resource Group: finopshub-rg
## Goal
Detect cost anomalies in Azure spending using the FinOps Hub data. Run the KQL query against the ADX cluster, analyze results, and notify via Teams if anomalies are found.
## Step 1: Execute KQL Query
Use the KustoQuery subagent to run this query against the ADX cluster (https://finopshubadx.northcentralus.kusto.windows.net, database: Hub):
```kql
let numberOfMonths = 12;
let start = startofmonth(ago(numberOfMonths * 30d));
let end = now();
let interval = 1d;
Costs()
| where ChargePeriodStart between (start .. end)
| summarize DailyCost = sum(EffectiveCost) by bin(ChargePeriodStart, interval)
| make-series CostSeries = sum(DailyCost) on ChargePeriodStart from start to end step interval
| extend anomalies = series_decompose_anomalies(CostSeries)
| project ChargePeriodStart, CostSeries, anomalies
```
## Step 2: Analyze Results
- Parse the anomalies array from series_decompose_anomalies output
- Identify days where anomaly score is significantly positive (cost spike) or negative (unusual drop)
- For each anomaly found, calculate: the date, actual cost value, expected baseline, and percentage deviation
- Focus on the last 7 days of data for actionable anomalies, but use the full 12-month history for context
## Step 3: Generate Report
Create a summary with:
- Total anomalies detected in the last 7 days
- For each anomaly: date, cost value, deviation from expected, severity (high/medium/low)
- Trend analysis: whether costs are trending up, down, or stable
- Top recommendations (e.g., investigate specific resource groups, check for new deployments, review reserved instances)
## Step 4: Teams Notification (ONLY if anomalies detected in last 7 days)
If anomalies are found, send a notification to Microsoft Teams using the executing_code skill (ExecutePythonCode) to POST to this webhook URL:
https://839eace659ab424397eca5b8fcc104.e4.environment.api.powerplatform.com:443/powerautomate/automations/direct/workflows/88ec6b6c43014d8f8cb13999714dddd8/triggers/manual/paths/invoke?api-version=1&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=br2rNi4x1zKjWp6CgKWcajvEUW_7EUBkHrkbnp6Hk2g
Use Adaptive Card format for the Teams message with:
- Title: "⚠️ Azure Cost Anomaly Detected"
- Summary of anomalies (dates, values, deviations)
- Severity indicator
- Link back to this SRE Agent thread for details
Python code example for the webhook POST:
```python
import urllib.request
import json
webhook_url = "<colar-url-webhook-aqui>"
card = {
"type": "message",
"attachments": [{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{"type": "TextBlock", "text": "⚠️ Azure Cost Anomaly Detected", "weight": "Bolder", "size": "Large"},
{"type": "TextBlock", "text": "<ANOMALY_SUMMARY_HERE>", "wrap": True},
{"type": "FactSet", "facts": [
{"title": "Date", "value": "<DATE>"},
{"title": "Actual Cost", "value": "<COST>"},
{"title": "Expected", "value": "<EXPECTED>"},
{"title": "Deviation", "value": "<DEVIATION>%"},
{"title": "Severity", "value": "<SEVERITY>"}
]}
],
"actions": [{"type": "Action.OpenUrl", "title": "View in SRE Agent", "url": "https://sre.azure.com/agents/subscriptions/<sua-subscricao>/resourceGroups/finopshub-rg/providers/Microsoft.App/agents/finopshub/views/thread/7314d219-77d6-4e12-a3fb-f30606d13077"}]
}
}]
}
req = urllib.request.Request(webhook_url, data=json.dumps(card).encode('utf-8'), headers={'Content-Type': 'application/json'}, method='POST')
response = urllib.request.urlopen(req)
```
## Step 5: Report in Thread
Always post a summary in this thread with:
- Whether anomalies were found or not
- Key metrics and trends
- If no anomalies: "No material cost anomalies detected. Daily costs within expected range."
## Constraints
- Read-only operations only against ADX
- Max 5 KQL queries per execution
- Do not modify any Azure resources
## Idempotence
If no anomalies detected in the last 7 days and costs are within normal thresholds, output: "No material cost anomalies detected. Daily costs within expected range."
## Escalation
If anomalies are detected for 3+ consecutive days, flag as HIGH severity in the Teams notification and recommend immediate investigation.
⑥ Notificação no Teams (Adaptive Card)
Quando anomalias são detectadas, o SRE Agent envia um Adaptive Card rico para o Teams:
|
⚠️ AZURE COST ANOMALY DETECTED |
|
|
🔴 HIGH — 2026-04-21 |
|
|
Custo Real |
$1,847.32 |
|
Custo Esperado |
$623.15 |
|
Desvio |
+196.5% (↑ $1,224.17) |
|
Score Anomalia |
4.7 (threshold: 1.5) |
|
|
|
|
🟡 MEDIUM — 2026-04-20 |
|
|
Custo Real |
$987.44 |
|
Desvio |
+59.5% (↑ $368.54) |
|
📊 Trend (7d) |
$601 → $615 → $622 → $619 → $987 → $1,847 |
Exemplo de Adaptive Card enviado ao canal do Teams
O card inclui botões: Ver Detalhes no SRE Agent e Abrir FinOps Hub (ADX) para investigação.
⑦ Testar a Integração (Simular Anomalia)
Antes de confiar no agendamento, teste pedindo ao SRE Agent:
"Simular uma anomalia de custo para eu receber no Teams"
O agente enviará um Adaptive Card de teste com dados simulados para validar que o webhook está funcionando.
Dica: Se o webhook retornar erro 400 - WorkflowTriggerIsNotEnabled, ative o flow em: Power Automate → My flows → Turn on.
Como Funciona no Dia a Dia
|
Horário |
Ação |
Resultado |
|
08:00 UTC |
SRE Agent executa query KQL no ADX |
Dados de custo (12 meses) |
|
08:01 UTC |
Análise de anomalias (últimos 7 dias) |
Identifica spikes e drops |
|
08:02 UTC |
Se anomalia → Teams notification |
Adaptive Card no canal |
|
08:02 UTC |
Se normal → log no thread |
"Custos dentro do esperado" |
|
Contínuo |
3+ dias anomalia → escalação HIGH |
Investigação recomendada |
Gerenciamento da Task
Após criada, a tarefa pode ser gerenciada pelo SRE Agent:
- Pausar: "Pausar a tarefa de anomalia de custo"
- Retomar: "Retomar a tarefa"
- Alterar horário: "Mudar para executar às 10h UTC"
- Ver histórico: "Mostrar histórico de execuções"
- Cancelar: "Cancelar a tarefa de anomalia"
Conclusão
Com essa configuração, você tem um sistema inteligente de vigilância de custos que:
🔍 Analisa 12 meses de histórico para detectar padrões sazonais
🤖 Usa ML nativo do Kusto — sem infraestrutura adicional
📱 Notifica proativamente no Teams — só quando há anomalia
📈 Escala automaticamente a severidade em anomalias persistentes
💬 Mantém histórico completo no thread do SRE Agent
🔧 É gerenciável por linguagem natural — sem YAML, sem pipelines
A combinação FinOps Hub + Azure SRE Agent + Teams transforma a governança de custos de reativa (olhar dashboards) em proativa e automatizada.
🚀 Quer implementar na sua organização?
Se você quer testar, o Azure SRE Agent está disponível como preview. Configure um agente na sua subscription e comece a conversar em linguagem natural.
Já usa FinOps Hub? Conte nos comentários como você monitora anomalias de custo hoje!
#FinOps #Azure #SREAgent #CostManagement #AzureDataExplorer #CloudGovernance #DevOps #MicrosoftTeams #AIOps