cost optmization
2 TopicsDetectando Anomalias de Custo no Azure com FinOps Hub + SRE Agent
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 #AIOps41Views1like0CommentsFinOps Hub Privado: Implementação Manual em Ambientes Corporativos
O que é o FinOps Hub? O FinOps Hub é uma solução open source da Microsoft, parte do FinOps Toolkit, que fornece uma base escalável e extensível para análise, governança e otimização de custos em nuvem. A solução centraliza dados de custos por meio de recursos de armazenamento e pipelines de dados, permitindo que as organizações padronizem a forma como analisam seus gastos na nuvem, adotando o modelo de dados FOCUS definido pela FinOps Foundation. Além de disponibilizar relatórios e dashboards prontos, o FinOps Hub expõe os dados de custos de forma estruturada por meio do Azure Data Explorer ou do Microsoft Fabric, possibilitando a criação de relatórios customizados no Power BI e o desenvolvimento de soluções internas sob medida, de acordo com as necessidades do negócio. Objetivo Esse artigo visa orientar a implementação manual do acesso privado ao FinOps Hub após uma implementação pública, sem usar diretamente a opção de implementação privada oferecida pelo template. Por que implementar o FinOps Hub com acesso privado de forma manual? O template disponibilizado para a implementação do FinOps Hub permite que selecionemos duas formas de implementação: Pública: forma de implementação mais simples e comum. Os recursos são provisionados com acesso público habilitado, sendo acessados por meio de seus endpoints públicos padrões do Azure. O controle de acesso é feito exclusivamente via permissões RBAC. Privada: forma de implementação mais segura. Os recursos são provisionados com acesso público desabilitado e expostos apenas por meio de private endpoints, ficando acessíveis somente através de redes privadas que possuem conectividade com a rede onde esses endpoints estão implementados. Embora a comunicação privada traga ganhos significativos de segurança, sua habilitação através do template do FinOps Hub introduz algumas implicações importantes no processo de implantação. O template, de forma automática, tenta: Criar uma rede virtual (/26); Provisionar Private DNS Zones; Criar os registros DNS associados aos private endpoints; Configurar os links entre as VNets e as zonas DNS. Em muitas organizações, esse tipo de configuração é restrito por políticas internas. A criação de redes, zonas DNS e seus respectivos vínculos costuma ser responsabilidade de times especializados, que garantem a padronização e a aderência às diretrizes arquiteturais definidas pela empresa. Em cenários onde a organização adota, por exemplo, uma topologia Hub & Spoke, com landing zones bem definidas para workloads e conectividade, é fundamental considerar alguns pontos adicionais: Garantir que a rede criada para os private endpoints não tenha sobreposição (overlap) com outras redes existentes; Assegurar que exista peering com a VNet de hub; Integrar os registros DNS dos novos private endpoints às Private DNS Zones centralizadas; Configurar corretamente os VNet links; Garantir que a resolução de nomes esteja funcional no ambiente on-premises, permitindo o acesso ao FinOps Hub por meio de VPN, ExpressRoute ou outras formas de conectividade híbrida. Esses fatores explicam por que, em muitos casos, o uso do template com configuração privada se torna inviável quando utilizado de forma isolada. Isso ocorre porque, frequentemente, o time de FinOps não é o mesmo time responsável por redes e DNS, possuindo permissão para implantar apenas parte dos recursos necessários. Nesses cenários, a implementação privada exige a orquestração entre diferentes times, cada um atuando dentro de suas responsabilidades. Como resultado, algumas etapas que o template tenta executar automaticamente precisam ser realizadas manualmente, conforme descrito nas próximas seções. Passo a Passo para Implementação Faça a implementação do template seguindo o tutorial documentado, com “Networking” em modo público. Assim que o template é implementado, alguns scripts de configuração são executados. Aguarde até que esses scripts sejam executados, ou seja, desapareçam do grupo de recursos onde o FinOps Hub foi implementado para seguir com os próximos passos. Exemplos dos “Deployment Scripts” que desaparecerão: Crie uma rede de tamanho mínimo /26 – valide com o time de infraestrutura o espaçamento que pode ser utilizado. Dentro dessa rede implemente uma subrede: private-endpoint-subnet (/28) – subrede para os private endpoints. Garanta a existência das Private DNS Zones: se seu ambiente possui zonas de DNS privadas centralizadas pré-configuradas. Garanta que as seguintes zonas já existam na subscription padrão para os seus recursos de rede: privatelink.blob.core.windows.net– para o Data Explorer e storage privatelink.dfs.core.windows.net– para o Data Explorer and o data lake hospedando os dados de FinOps data e configuração das pipelines privatelink.table.core.windows.net– para Data Explorer privatelink.queue.core.windows.net– para o Data Explorer privatelink.{location}.kusto.windows.net– para Data Explorer Configurando a Storage Account com Acesso Privado O primeiro recurso que a ser configurado será a Storage Account. A configuração a seguir deve ser realizada duas vezes uma para o target sub-resource “blob” e outra para o target sub-resource “dfs”. Acesse “Networking” e a aba “Private Endpoints” Na aba “Basics” defina: Subscription: mesma que o FinOps Hub foi implementado Resource Group: mesmo que o FinOps Hub foi implementado Name: use um nome que remeta ao recurso criado (private endpoint) e o sub recurso que ele fará exposição (blob/dfs). Exemplo: pe-storageaccount-blob/ pe-storageaccount-dfs Na aba “Resource” defina o target sub-resource para o qual a configuração está sendo feita. No caso, primeiro foi feito para “blob” e depois “dfs”. Na aba “Virtual Network”, deve-se selecionar a rede criada para os private endpoints bem como a subrede (private-endpoint-subnet). Na aba “DNS” selecione a private DNS zone na qual são realizados os registros do seus private endpoints. Quando estiver configurando o private endpoint para blob será privatelink.blob.core.windows.net e para dfs privatelink.dfs.core.windows.net. Adicione Tags caso seja necessário. Clique em “Review + create”. Ao final, dois private endpoints devem estar criados para a storage account: Agora será necessário configurar o private endpoint usado pelo Data Factory para acessar a Storage Account. Acesse o Data Factory e clique em "Launch Studio". No menu lateral, selecione a opção "Manage" -> "Linked services": Selecione o serviço correspondente ao Azure Data Lake Storage Gen2. Em “Connect via integration runtime” substitua “AutoResolveIntegrationRuntime” por New+: Em “Integration runtime setup” selecione “Azure” -> Botão “Continue”: Na aba “Settings”, apenas altere o campo “Name”. Use algo como “ManagedIntegrationRuntime”. Os demais campos, mantenha como estão: Na aba “Virtual Network”, configure conforme a imagem abaixo: Por fim, na aba “Data flow runtime”, use as configurações abaixo: Clique em “Create”. Agora, voltando a página “Edit linked Service”, adicione o “Managed Private Endpoint”. Ele ficará em estado “Pending”. Clique em “Save”. Além disso, use a managed identity fornecida na página “Edit linked Service”, e lhe dê os seguintes acessos na storage account: Reader Storage Account Contributor Storage Blob Data Contributor Por fim, acesse a seção “Networking” da Storage Account novamente, selecione o private endpoint criado pelo Data Factory e clique no botão “Approve”. Configurando o Linked Service ftkRepo no Data Factory Voltando na seção “Manage” do Azure Data Factory, será necessário editar o serviço “ftkRepo”. Nele, o único campo a ser alterado é o “Connect via integration runtime” usando o “ManagedIntegrationRuntime” criado na etapa de configuração da storage account. Clique em “Save”. Configurando o Azure Data Explorer com Acesso Privado Acesse a seção “Networking” cluster do Data Explorer criado pelo script do FinOps Hub e acesse a aba “Private Endpoint Connections” Clique em “+ Private Endpoint”. Em “Basics” inclua um nome para o private endpoint e garanta que a região selecionada é a de implementação do seu FinOps Hub e da vnet criada para ele. Na aba “Virtual Network” selecione a rede e subrede criadas para os private endpoints. Na aba “DNS”, associar cada configuração a sua private DNS zone correspondente. Se essas zonas já existirem no ambiente e forem utilizadas de forma centralizada, usar as existentes. Caso contrário, a configuração poderá criar private DNS zones novas: Novamente será preciso configurar o private endpoint gerenciado pelo Data Factory. Para isso, acesse “Managed” -> “Linked Services” e selecione o serviço referente ao Azure Data Explorer. Nessa seção, o único campo a ser editado é o “Connect via integration runtime” usando o “ManagedIntegrationRuntime” criado na etapa de configuração da storage account. Agora ainda em “Manage” no Data Factory, selecione a opção “Managed Private Endpoints”. Clique em “+New” -> “Azure Data Explorer (Kusto)” Forneça um nome para o private endpoint, marque a subscription na qual o Data Explorer foi implementado e em "Cluster" selecione o recurso implementado via template do FinOps Hub: Ao final, deve-se ter dois private endpoints configurados no Azure Data Explorer: Ajustes Finais e Validações de Conectividade Tanto para a Storage Account quanto para o Data Explorer, acesse as configurações de rede e altere o Public network access para “Disabled”. Por fim, no seu servidor local de DNS, crie o registro para o Data Explorer apontando para o forwarder correto no Azure seja ele uma máquina virtual ou o inbound endpoint do Azure Private Resolver. Não use o domínio com privatelink, mas sim o padrão, no caso do Data Explorer a forma geral é <localização>.kusto.windows.net conforme o exemplo abaixo: Ao configurar os apontamentos corretamente, será possível configurar os dashboards em máquinas locais/on-premises que tenham acesso privado ao ambiente Azure. Os dashboards precisam ser capazes de resolver a URL do cluster Data Explorer que é apontado como parâmetro do Dashboard em PowerBI. Se desejar validar a comunicação antes de configurar o dashboard de Power BI, teste da sua máquina local a resolução da URI do Data Explorer implementado para o FinOps Hub, usando nslookup conforme o exemplo abaixo.297Views4likes0Comments