feat: Add App Insights AI Monitoring Agent Azure Function (C#)#28
feat: Add App Insights AI Monitoring Agent Azure Function (C#)#28devin-ai-integration[bot] wants to merge 1 commit into
Conversation
- Add Azure Function project (C#, .NET 10, isolated worker) for AI-powered monitoring - Implement timer-triggered functions: AnomalyDetector (5min), HealthMonitor (30min) - Implement HTTP-triggered functions: GetHealthReport, GetServiceTelemetry, GetAnomalies, AlertWebhook, TriggerManualAnalysis - Add AppInsightsQueryService for KQL-based telemetry queries (requests, exceptions, dependencies) - Add AiAnalysisService with rule-based + statistical z-score + Azure OpenAI anomaly detection - Add AlertService with Teams/Slack adaptive card webhook notifications - Add Dockerfile and docker-compose service definition - Update solution file and README with monitoring agent documentation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| var query = $""" | ||
| exceptions | ||
| | where cloud_RoleName == '{serviceName}' | ||
| | where timestamp > ago({FormatTimeSpan(period)}) | ||
| | summarize Count=count(), LastOccurrence=max(timestamp) by type, outerMessage | ||
| | top {top} by Count desc | ||
| """; |
There was a problem hiding this comment.
🔴 KQL injection via unsanitized serviceName in query string interpolation
The serviceName parameter is interpolated directly into KQL queries (e.g., | where cloud_RoleName == '{serviceName}') at 5 locations in AppInsightsQueryService.cs. This value originates from user-controllable sources: the {serviceName} HTTP route parameter in MonitoringDashboardFunction.cs:87-88 and AlertWebhookFunction.cs:83-84, as well as the ResourceName field from external webhook payloads at AlertWebhookFunction.cs:46. A malicious serviceName containing a single quote (e.g., foo' | where 1==1 //) can break out of the string literal and inject arbitrary KQL, potentially exfiltrating data from the Log Analytics workspace.
Affected query locations in AppInsightsQueryService.cs
Lines 72, 103, 132, 161, 197 all use the pattern:
| where cloud_RoleName == '{serviceName}'
with no escaping or parameterization of serviceName.
Prompt for agents
The serviceName parameter from HTTP route parameters and webhook payloads is directly string-interpolated into KQL queries at multiple locations in AppInsightsQueryService.cs (lines 72, 103, 132, 161, 197), creating a KQL injection vulnerability.
To fix this, either:
1. Sanitize/validate serviceName before use: strip or reject any input containing single quotes, pipes, or other KQL metacharacters. A simple allowlist regex like ^[a-zA-Z0-9-]+$ would work since service names should only contain alphanumeric characters and hyphens.
2. Add a validation method (e.g., ValidateServiceName) in AppInsightsQueryService or as a shared utility, and call it at the start of GetServiceTelemetryAsync and any other public method that accepts serviceName.
3. Also validate in the HTTP trigger functions (MonitoringDashboardFunction.GetServiceTelemetryAsync, AlertWebhookFunction.TriggerManualAnalysisAsync, AlertWebhookFunction.RunAsync) before passing to the query service.
All 5 query methods that interpolate serviceName need protection: GetTopExceptionsAsync, GetRequestRateTimeSeriesAsync, GetResponseTimeTimeSeriesAsync, GetDependencyMetricsAsync, and QueryRequestMetricsAsync.
Was this helpful? React with 👍 or 👎 to provide feedback.
| ports: | ||
| - "7071:80" | ||
| environment: | ||
| - AzureWebJobsStorage=UseDevelopmentStorage=true |
There was a problem hiding this comment.
🔴 monitoring-agent will fail in docker-compose: UseDevelopmentStorage=true without Azurite container
The monitoring-agent service sets AzureWebJobsStorage=UseDevelopmentStorage=true which expects Azurite (Azure Storage Emulator) to be available at 127.0.0.1:10000/10001/10002. However, no Azurite container is defined in docker-compose, and in a containerized environment, 127.0.0.1 refers to the container's own loopback — not the host. The Azure Functions runtime requires storage for timer trigger lease management (used by both AnomalyDetector and HealthMonitor functions). Without accessible storage, the function host will fail to start or timer triggers will not fire.
Prompt for agents
The monitoring-agent docker-compose service sets AzureWebJobsStorage=UseDevelopmentStorage=true but there is no Azurite container in the compose file. Azure Functions timer triggers (AnomalyDetector and HealthMonitor) require Azure Storage for lease management.
To fix this, add an Azurite container to docker-compose.yml and update the connection string:
1. Add an Azurite service:
azurite:
image: mcr.microsoft.com/azure-storage/azurite
ports:
- "10000:10000"
- "10001:10001"
- "10002:10002"
2. Change the monitoring-agent AzureWebJobsStorage from UseDevelopmentStorage=true to an explicit connection string pointing to the azurite container:
AzureWebJobsStorage=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;QueueEndpoint=http://azurite:10001/devstoreaccount1;TableEndpoint=http://azurite:10002/devstoreaccount1
3. Add depends_on: azurite to the monitoring-agent service.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Adds a new Azure Function (C#, .NET 10, isolated worker) that serves as an AI-powered monitoring agent for all microservices using Azure Application Insights and Azure OpenAI.
New Project:
Monitoring.FunctionsLocated at
src/Functions/Monitoring/Monitoring.Functions/, this Azure Function app provides:Timer-Triggered Functions:
AnomalyDetector(every 5 min) — Queries App Insights telemetry and detects anomalies using rule-based thresholds + statistical z-score analysis + Azure OpenAI enrichmentHealthMonitor(every 30 min) — Generates comprehensive health reports with AI summaries and sends webhook notificationsHTTP-Triggered Functions:
GET /api/monitoring/health— Full platform health report with per-service AI insightsGET /api/monitoring/services/{name}— Detailed telemetry and AI analysis for a specific serviceGET /api/monitoring/anomalies— Lists all detected anomalies across servicesPOST /api/monitoring/alerts/webhook— Receives App Insights alert webhooks, enriches with AI analysisPOST /api/monitoring/analyze/{name}— Triggers on-demand AI analysis for a specific serviceServices:
AppInsightsQueryService— KQL queries against Log Analytics (requests, exceptions, dependencies, time-series)AiAnalysisService— Rule-based + z-score statistical + Azure OpenAI anomaly detection and health insight generationAlertService— Adaptive card webhook notifications for Teams/SlackOther changes:
Microservices.slnwith Functions/Monitoring solution folderREADME.mdwith monitoring agent documentation, configuration reference, and architectureReview & Testing Checklist for Human
local.settings.jsonAppInsightsQueryService.csto ensure they match your actual App Insights telemetry schema (cloud_RoleName values must match your deployed service names)AlertService.csfollows Microsoft Teams formatFailureRateThresholdPercent: 5%,ResponseTimeThresholdMs: 2000ms) are appropriate for your SLAsfunc startafter configuringlocal.settings.jsonwith real Azure credentialsNotes
DefaultAzureCredentialfor authentication — works with managed identity in Azure, or Azure CLI / VS Code credentials locallylocal.settings.jsoncontains placeholder values that need to be replaced with actual Azure resource details before runningLink to Devin session: https://partner-workshops.devinenterprise.com/sessions/e2fc58357f724243b98d9ea2cc72e7dd