Skip to content

Azure Monitor

Monitor your Azure estate — Virtual Machines, App Service apps, Azure SQL databases, Storage accounts, and Service Bus namespaces — through Azure Monitor, with curated dashboards and alerts you import in one step.

Azure Monitor is a polled API rather than a scrape endpoint, but no adapter is needed: the LeanSignal agent gateway is an OpenTelemetry Collector distribution and bundles the azuremonitor receiver, which polls the Azure Monitor metrics API with a read-only Entra ID identity, discovers resources per subscription and resource group, and emits one series per metric. The gateway keeps full fidelity locally and forwards only the demanded subset to your central dataplane — so polling broadly stays cheap, and importing a demand is what decides which Azure series are stored centrally.

The panels and alerts cover the five services most Azure subscriptions run on — Virtual Machines, App Service, Azure SQL, Storage, and Service Bus — with the thresholds Azure operators actually page on: memory headroom, 5xx ratios, database storage and worker saturation, storage availability, and message backlog.

  • A LeanSignal agent gateway deployed and connected — one per cluster or environment. If you haven’t deployed it yet, follow Install the agent (source: LeanSignal/leansignal-agent). It should show as Connected under Agents in the LeanSignal app.
  • Network reachability to the gateway’s OTLP port4317 (gRPC) or 4318 (HTTP). See Agent configuration.
  • A read-only Azure identity — an Entra ID app registration or managed identity with the built-in Monitoring Reader role on each subscription you poll — and an OpenTelemetry Collector running the azuremonitor receiver, which the LeanSignal agent gateway already bundles. Both configured in Setup.
  • Editor or admin role in the LeanSignal app (importing a demand creates dashboards and alert rules).

Two pieces: a read-only Azure identity, and a collector running the azuremonitor receiver that polls Azure Monitor and forwards OTLP to the gateway.

1. Azure — a read-only identity. Create a service principal with the built-in Monitoring Reader role on every subscription you poll (repeat --scopes per subscription):

Terminal window
az ad sp create-for-rbac --name leansignal-monitor \
--role "Monitoring Reader" \
--scopes /subscriptions/<subscription-id>

The output gives you appId (client ID), password (client secret), and tenant. Managed identity and workload identity work too — set credentials: managed_identity (or workload_identity) instead of the client secret below.

2. Collector — poll Azure Monitor, forward OTLP to the gateway (not a SaaS endpoint). One services entry per resource type; metrics limits the poll to the metrics and aggregations the dashboards actually chart:

receivers:
azuremonitor:
subscription_ids: ["${env:AZURE_SUBSCRIPTION_ID}"] # list every subscription
tenant_id: "${env:AZURE_TENANT_ID}"
client_id: "${env:AZURE_CLIENT_ID}"
client_secret: "${env:AZURE_CLIENT_SECRET}"
collection_interval: 60s
# One series per resource per metric. Turn this on to break metrics down by
# their Azure dimensions (API name, entity name, …) — panels aggregate over
# them either way, at the cost of many more series.
dimensions:
enabled: false
# Off by default; the dashboards use it to tell subscriptions apart.
resource_attributes:
azuremonitor.subscription_id:
enabled: true
services:
- Microsoft.Compute/virtualMachines
- Microsoft.Web/sites
- Microsoft.Sql/servers/databases
- Microsoft.Storage/storageAccounts
- Microsoft.ServiceBus/namespaces
metrics:
"microsoft.compute/virtualmachines":
Percentage CPU: [average]
Available Memory Bytes: [average]
Network In Total: [total]
Network Out Total: [total]
Disk Read Bytes: [total]
Disk Write Bytes: [total]
CPU Credits Remaining: [average]
OS Disk IOPS Consumed Percentage: [average]
Data Disk IOPS Consumed Percentage: [average]
"microsoft.web/sites":
Requests: [total]
Http4xx: [total]
Http5xx: [total]
HttpResponseTime: [average]
MemoryWorkingSet: [average]
CpuTime: [total]
"microsoft.sql/servers/databases":
cpu_percent: [average]
storage_percent: [average]
connection_successful: [total]
connection_failed: [total]
deadlock: [total]
physical_data_read_percent: [average]
log_write_percent: [average]
workers_percent: [average]
sessions_percent: [average]
"microsoft.storage/storageaccounts":
Availability: [average]
Transactions: [total]
UsedCapacity: [average]
SuccessE2ELatency: [average]
SuccessServerLatency: [average]
Ingress: [total]
Egress: [total]
"microsoft.servicebus/namespaces":
ActiveMessages: [average]
DeadletteredMessages: [average]
IncomingMessages: [total]
OutgoingMessages: [total]
ServerErrors: [total]
UserErrors: [total]
ThrottledRequests: [total]
processors:
# Required — see "Azure units" below. Keeps names as azure_<metric>_<aggregation>.
transform/azure_units:
error_mode: ignore
metric_statements:
- set(metric.unit, "") where IsMatch(metric.name, "^azure_")
batch: {}
exporters:
otlp:
endpoint: leansignal-agent:4317 # per-cluster gateway (use :4318 for HTTP)
tls:
insecure: true # in-cluster plaintext; provide ca_file for TLS
service:
pipelines:
metrics:
receivers: [azuremonitor]
processors: [transform/azure_units, batch]
exporters: [otlp]

Running this on the gateway itself instead? Add the azuremonitor receiver and the transform/azure_units processor to its own config, then add them to its metrics/all pipeline — the otlp exporter and the separate service block above are only for a standalone collector.

To confirm metrics are arriving, look for the azure_ family in the LeanSignal app under Metrics with the source set to Available, or on the gateway host:

Terminal window
curl -s http://127.0.0.1:8428/api/v1/label/__name__/values | grep -o 'azure_[a-z_]*' | sort -u | head

Azure resource logs reach the gateway through Event Hubs, the path Azure itself is built around: add a diagnostic setting on each resource (or a subscription-level policy) that streams its log categories to an Event Hub, then point the gateway’s bundled azureeventhub receiver at it — the streams then behave like any other logs.

receivers:
azureeventhub:
connection: "${env:AZURE_EVENTHUB_CONNECTION_STRING}" # …;EntityPath=<hub-name>
group: leansignal # consumer group
format: azure

Add azureeventhub to a logs pipeline exporting OTLP to the gateway (or to the gateway’s own logs/all pipeline). Traces are an application concern, not an Azure Monitor one — instrument the workloads running on Azure with the OpenTelemetry SDKs (see the runtime integrations) and point them at the gateway; apps already wired for Application Insights export OTLP the same way.

Azure Monitor ships as a ready-made demand in three variants. You don’t copy any JSON — in the LeanSignal app go to Demands, press the ˅ arrow on the Add Demand button (the arrow opens the import menu), choose Import from catalog…, and pick the variant. Each variant below lists the slug that identifies its published bundle. See Integrations for the full import flow.

The variants are nested — Standard is a superset of Essential, Extended a superset of Standard. Pick whichever is closest to what you need and treat it as a starting point: after import everything is a normal, editable copy, so retune thresholds, add or drop panels, and adjust queries for your environment — your edits reshape the demand automatically. Start small and re-import a larger variant later if you outgrow it.

The failure signals. 1 dashboard · 8 panels · 5 alerts — CPU, memory headroom, 5xx responses, storage headroom, availability, and message backlog at a glance.

Import this variant from the catalog. Its demand slug:

Demand slug
azure-monitor-otel-demand-essential

Dashboard — CPU utilization, Available memory, Requests (per period), 4xx / 5xx responses (per period), CPU utilization, Storage used, Availability, and Active and dead-lettered messages.

Alerts (5)

AlertSeverityFires when
App Service high 5xx ratiowarning5xx > 5% of requests for 10m
Azure SQL storage nearly fullcritical> 90% of allocated storage for 15m
Storage account availability degradedcriticalavailability < 99% for 10m
Service Bus dead-lettered messageswarningany dead-lettered message for 15m
VM low available memorycritical< 256 MiB available for 15m

Review the bundle JSON →

Each variant imports one Azure Monitor dashboard, its panels grouped into collapsible sections. Every dashboard carries an Instance filter (on the resource_group label), so one import covers the whole fleet — view every instance at once, or focus on one. Legends are sortable tables showing last/mean/max per series, so the outlier instance stands out. Dashboards are demand-driven: importing one tells your agents to forward exactly the timeseries its panels query, and nothing else. Panels are ordinary Perses panels — edit queries, add panels, or retune them after import, and your changes reshape the demand automatically.

Thresholds are conservative starting points — tune them to your workload.

Was this page helpful?