Azure Monitor¶
Azure Monitor covers both Log Analytics (logs) and Azure Monitor Metrics (metrics). A single SerOps integration handles both — one service principal, one config record, both capabilities.
| Type | Logs + Metrics |
| Auth | Service principal (tenant + client + secret) |
| Log Query Language | KQL (Kusto Query Language) |
| Metric Query Language | Metric name + resource URI + aggregation |
| SDK | azure-monitor-query + azure-identity |
Prerequisites¶
- An Azure AD tenant and an App registration (service principal) with a client secret
- The service principal must have these Azure RBAC roles on the target workspace / resources:
Log Analytics Readeron the Log Analytics workspace (for logs)Monitoring Readeron the resource group or specific resources (for metrics)- The Log Analytics workspace ID (a GUID from the workspace's Overview page)
- The default resource URI for metrics (an ARM-style
/subscriptions/…/resourceGroups/…/providers/…/…path) - Network egress from the SerOps backend to
login.microsoftonline.com,api.loganalytics.io, andmanagement.azure.com
Configuration¶
{
"provider": "azure",
"display_name": "Azure · production tenant",
"capabilities": ["logs", "metrics"],
"password": "your-client-secret",
"extra_config": {
"tenant_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"client_id": "11111111-2222-3333-4444-555555555555",
"workspace_id": "66666666-7777-8888-9999-000000000000",
"default_table": "AppTraces",
"default_resource_uri": "/subscriptions/sub-id/resourceGroups/rg-name/providers/Microsoft.Compute/virtualMachines/vm-name"
}
}
Notes:
passwordis the client secret (encrypted at rest with Fernet). Rotate it in Azure AD → App registrations → Certificates & secrets, then update this field.tenant_idandclient_ididentify the service principal.workspace_idis required for logs.default_resource_uriis required for metrics — callers can override per-query by supplyingresource_uriin the labels dict.default_table(optional, defaults toAppTraces) picks the KQL table forLogQuery-driven searches. UsePerffor VM performance,Syslogfor Linux syslog,ContainerLogfor AKS, or a custom table.
Service-principal-only auth
SerOps deliberately does not support DefaultAzureCredential, ManagedIdentityCredential, or CLI-cached tokens. A multi-tenant SerOps process must never inherit host-level credentials, which is exactly what those auth flows do.
What SerOps does with your query¶
Logs (AzureLogAnalyticsProvider):
LogQuery.to_azure_kql() produces well-formed KQL:
AppTraces
| where TimeGenerated >= datetime(2026-08-27T09:00:00Z)
| where TimeGenerated <= datetime(2026-08-27T10:00:00Z)
| where Properties.project_name == "acme-checkout"
| where Properties.environment == "production"
| where Properties.service_name in ("backend-api", "payment-worker")
| where SeverityLevel >= 3
| where Message contains "timeout"
| top 5000 by TimeGenerated desc
- Severity maps to Azure's
SeverityLevelordinal (Verbose=0, Information=1, Warning=2, Error=3, Critical=4). Requesting["ERROR", "WARNING"]becomesSeverityLevel >= 2. - Labels live under
Properties.<name>in AppTraces — the canonical Application Insights schema. ForPerf,Syslog, orContainerLog, adjust the KQL manually via a raw-KQL query. - Time range is embedded as
datetime(<iso>)literals. The<iso>value is validated by_kql_datetimebefore interpolation — only digits, dashes, colons,T,Z,.,+are permitted. Any character outside that set fails loudly (an unquoted KQL identifier is executable code, so silent rewrites here are the highest-severity risk in the schema layer).
Metrics (AzureMonitorProvider):
Canonical Prometheus-style names map to Azure metric names via a small dictionary:
| Canonical | Azure metric |
|---|---|
node_cpu_seconds_total |
Percentage CPU |
node_memory_MemAvailable_bytes |
Available Memory Bytes |
container_cpu_usage_seconds_total |
CPU Usage |
container_memory_usage_bytes |
Memory Usage |
Unknown names pass through unchanged so you can query any native Azure metric directly.
Granularity is auto-picked based on the query time range — targeting ~200 data points to stay well under Azure's per-query cap.
Health Check¶
Runs the cheapest authenticated call in each capability:
- Logs:
print now()KQL query — forces service-principal auth to actually run against the workspace - Metrics:
list_metric_definitions(default_resource_uri)— proves the SP can read the target resource
KQL Table Selection¶
The default_table sets what LogQuery-driven searches query. The three you'll probably use:
| Table | Content | When to use |
|---|---|---|
AppTraces |
Application Insights trace() calls |
Default. Best for OpenTelemetry / App Insights instrumented apps. |
ContainerLog |
Log lines from AKS pods | Kubernetes workloads |
Perf |
VM performance counters | VM CPU / memory / disk baselines |
Syslog |
Linux syslog | On-prem VMs, network devices |
Callers can always supply a raw KQL string instead — the adapter accepts search(index="", query="AppExceptions | count") and passes it through unchanged.
Troubleshooting¶
| Issue | Solution |
|---|---|
| "Azure adapter requires tenant_id + client_id + password" | One of the auth fields is missing. Confirm the service principal has a valid client secret and all three IDs are populated |
| 401 / 403 on search | Service principal lacks Log Analytics Reader on the workspace (or Monitoring Reader on the resource for metrics) |
| "Invalid KQL bareword" during search | The default_table or the index= parameter contains characters outside [A-Za-z0-9_]. This is intentional strictness — a stray character in an unquoted KQL identifier is a code-injection vector |
| "Invalid KQL datetime literal" | Time-range values weren't clean ISO timestamps. Usually a caller passed something else (a Unix epoch, a formatted string). Feed a datetime to LogQuery.time_range and let the translator format it |
| Empty results with no error | Time-range window is too narrow, the AppTraces table doesn't have Properties.project_name/Properties.environment set (your instrumentation isn't populating them yet), or the workspace has no data in that period |
Kubernetes (AKS) mode¶
Not yet validated on a live AKS cluster
This mode is implemented and unit-tested, but has not yet run against a live AKS cluster. See Tested environments.
For AKS clusters, set the integration's topology_mode to
kubernetes and point default_resource_uri at the AKS cluster
resource (not a VM). SerOps will then:
- Route canonical
node_cpu_seconds_total/container_*names to the Azure Monitor Container Insights metric names (node_cpu_usage_percentage,container_cpu_usage_percentage,container_memory_working_set_bytes, …). - Build an Azure dimension
filterstring per query so metric results narrow to the right workload. Canonical SerOps labels are aliased automatically:
| SerOps label | Azure dimension |
|---|---|
service_name / service |
controllerName |
namespace |
kubernetesNamespace |
pod |
podName |
container |
containerName |
node |
node |
- Values are quoted; any value containing a double-quote is silently dropped rather than sent into the filter (defence in depth against a hostile label).
Prerequisite: Azure Monitor Container Insights must be enabled
on the AKS cluster (portal → Insights → Enable, or via
az aks enable-addons -a monitoring). The service principal needs
Monitoring Reader on the AKS resource.