Azure Monitor Integration
The Azure Monitor integration exports approved nats-sinks metrics as Azure
Monitor custom metrics for one explicitly configured Azure resource. It is
intended for Microsoft cloud deployments that already use Azure Monitor,
Metrics Explorer, alert rules, or downstream Azure observability tooling.
The connector is part of the observability plane, not the delivery plane. It reads a local metrics snapshot, applies the shared observability policy, and uses a bounded HTTPS request only when live export is requested. It does not connect to NATS, does not read sink payloads, does not inspect Oracle Database, Oracle MySQL, Oracle Coherence Community Edition, file-sink output, or other destination records, and never affects ACK, NAK, DLQ, retry, fan-out, idempotency, or sink-write decisions.
Azure API Shape
Microsoft documents Azure Monitor custom metrics ingestion through a REST API where metric data is posted to a regional Azure Monitor endpoint for a concrete Azure resource:
Important Azure-side constraints are reflected in the local connector:
- custom metrics are emitted for a specific Azure resource ID, not a whole subscription or resource group;
- the regional endpoint must match the Azure resource location;
- authentication uses a Microsoft Entra bearer token for the
https://monitoring.azure.com/audience; - dimensions are optional but capped locally at 10;
- metric values are sent as pre-aggregated
min,max,sum, andcountfields.
The current implementation uses the custom metrics REST path. Operators should evaluate Azure Monitor workspace based custom metrics separately where that is the preferred enterprise standard, because that is a different ingestion model.
Security Model
Azure Monitor export is disabled by default. Enable it only after reviewing the metric allow list, the Azure resource scope, the identity used by the separate observability service, and any dimensions.
The connector deliberately does not export:
- message payloads;
- NATS subjects;
- message IDs;
- stream names or consumer names;
- NATS server URLs;
- Oracle connection strings;
- table names;
- file paths;
- classification values;
- labels unless prepared metric labels are explicitly enabled as dimensions;
- mission metadata;
- Azure tenant IDs;
- client IDs or client secrets;
- bearer tokens;
- Azure resource IDs in dry-run output or result summaries;
- Azure regional endpoints in dry-run output or result summaries;
- exception messages from the HTTP client.
Prepared subject-family labels can be exported as Azure dimensions only when
subject_metrics.enabled and
azure_monitor.include_metric_labels_as_dimensions are both explicitly
enabled. The default is to suppress prepared labels so an Azure custom metric
does not accidentally become high-cardinality or reveal routing structure.
Configuration
Azure Monitor configuration lives inside the same observability policy JSON as the other connectors:
{
"schema": "nats_sinks.observability.policy.v1",
"enabled": true,
"namespace": "mission_ops",
"allowed_metrics": [
"messages_fetched_total",
"messages_acked_total"
],
"allowed_metric_patterns": [],
"denied_metrics": [],
"denied_metric_patterns": [],
"include_observations": false,
"include_legacy": false,
"subjects": [],
"azure_monitor": {
"enabled": true,
"resource_id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-observability/providers/Microsoft.Storage/storageAccounts/natssinks",
"location": "westeurope",
"metric_namespace": "nats-sinks/metrics",
"token_env": "AZURE_MONITOR_BEARER_TOKEN",
"dimensions": {
"deployment": "edge",
"environment": "prod"
},
"include_metric_labels_as_dimensions": false,
"timeout_seconds": 5,
"max_retries": 0,
"retry_backoff_seconds": 0.25,
"stale_after_seconds": 60,
"max_request_bytes": 1048576
}
}
Options
| Option | Default | Description |
|---|---|---|
azure_monitor.enabled |
false |
Enables Azure Monitor export when the top-level observability policy is also enabled. |
azure_monitor.resource_id |
null |
Azure resource ID that owns the custom metrics. Required when Azure Monitor export is enabled. It must identify a concrete resource below /subscriptions/.../resourceGroups/.../providers/.... Dry-run output does not include this value. |
azure_monitor.location |
null |
Azure location for the monitored resource, such as westeurope or eastus2. Required when Azure Monitor export is enabled. Dry-run output does not include this value. |
azure_monitor.metric_namespace |
nats-sinks/metrics |
Azure custom metric namespace. It must not use reserved Azure prefixes, must not contain colons, and is limited to bounded safe ASCII characters. |
azure_monitor.token_env |
null |
Environment variable that contains the Microsoft Entra bearer token. Required when Azure Monitor export is enabled. The token value is never stored in policy JSON. |
azure_monitor.dimensions |
{} |
Static low-cardinality dimensions added to every metric. Dimension names and values that look sensitive or high-cardinality, such as subject, classification, label, message, table, file, host, user, subscription, tenant, token, secret, or key, are rejected. |
azure_monitor.include_metric_labels_as_dimensions |
false |
When true, prepared labeled_metrics rows can export their bounded labels as Azure dimensions. Keep disabled unless subject-family sharing has been reviewed. |
azure_monitor.timeout_seconds |
5 |
HTTP timeout for each Azure Monitor request. |
azure_monitor.max_retries |
0 |
Bounded connector-level retries after the first failed request set. |
azure_monitor.retry_backoff_seconds |
0.25 |
Delay between connector-level retry attempts. |
azure_monitor.stale_after_seconds |
null |
Optional maximum metrics snapshot age. When set, stale snapshots fail closed unless --allow-stale is used. |
azure_monitor.max_request_bytes |
1048576 |
Maximum rendered request size. Oversized requests fail closed before any HTTP request is made. |
azure_monitor.verify_tls |
true |
TLS verification is always enabled. This field is intentionally not configurable to false. |
Identity
Prefer short-lived or platform-managed Azure identity:
- managed identity for Azure resources;
- workload identity in supported Kubernetes deployments;
- service principal tokens obtained by a separate protected credential process;
- Azure CLI access tokens for local operator testing only.
The token must be issued for the https://monitoring.azure.com/ audience. One
local testing pattern is:
export AZURE_MONITOR_BEARER_TOKEN="$(az account get-access-token \
--resource https://monitoring.azure.com/ \
--query accessToken \
--output tsv)"
Do not put tenant IDs, client secrets, access tokens, or private endpoint values in policy files, shell history, GitHub issues, test reports, or release evidence. The policy stores only the environment variable name.
Dry Run
Dry-run mode renders the Azure custom metric request bodies without loading a bearer token or calling Azure:
nats-sink-observe azure-monitor-export \
/var/lib/nats-sink/metrics.json \
/etc/nats-sinks/observability.prometheus.json \
--dry-run
Example output:
[
{
"data": {
"baseData": {
"dimNames": [
"deployment",
"environment"
],
"metric": "mission_ops_messages_fetched_total",
"namespace": "nats-sinks/metrics",
"series": [
{
"count": 1,
"dimValues": [
"edge",
"prod"
],
"max": 256.0,
"min": 256.0,
"sum": 256.0
}
]
}
},
"time": "2026-05-28T12:00:00.000Z"
}
]
The request body does not contain the Azure location, resource ID, bearer-token environment variable, NATS subject, payload, classification, labels, file path, table name, or destination address.
Live Export
Live export reads the bearer token from azure_monitor.token_env and posts
the policy-approved custom metric request bodies to Azure Monitor:
nats-sink-observe azure-monitor-export \
/var/lib/nats-sink/metrics.json \
/etc/nats-sinks/observability.prometheus.json
Successful output is intentionally short:
Azure Monitor export: attempted=true delivered=true attempts=1 requests=2 metrics=2 status=200 message=Azure Monitor export delivered
If the connector exhausts its bounded retries, the CLI exits with status 3
and prints a sanitized category:
Azure Monitor export: attempted=true delivered=false attempts=3 requests=2 metrics=2 status=none message=Azure Monitor export failed with TimeoutError
The message does not include tokens, tenant IDs, client IDs, resource IDs, locations, endpoints, exception messages, payloads, or dimension values.
Service Deployment
Run Azure Monitor export separately from the sink worker. The sink worker writes the local metrics snapshot. The Azure Monitor service reads that snapshot and the observability policy.
Example systemd service:
[Unit]
Description=nats-sinks Azure Monitor export
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=nats-sink-observe
Group=nats-sink-observe
EnvironmentFile=/etc/nats-sinks/azure-monitor.env
ExecStart=/usr/local/bin/nats-sink-observe azure-monitor-export /var/lib/nats-sink/metrics.json /etc/nats-sinks/observability.prometheus.json
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/lib/nats-sink
[Install]
WantedBy=multi-user.target
Example timer:
[Unit]
Description=Run nats-sinks Azure Monitor export periodically
[Timer]
OnBootSec=30s
OnUnitActiveSec=30s
AccuracySec=5s
[Install]
WantedBy=timers.target
Keep the EnvironmentFile readable only by the service account and root. It
must contain a short-lived bearer token or a local command should refresh the
token before the timer runs.
Testing
The default test suite uses fake HTTP clients and dry-run request rendering. It does not need an Azure subscription, Microsoft Entra credentials, or live network access.
Useful local checks:
python -m pytest tests/unit/test_azure_monitor_observability.py -q
python -m pytest tests/unit/test_observability_cli.py -q
python -m pytest tests/unit/test_public_api.py -q
Optional live validation should be performed only in a non-production Azure subscription with a disposable resource, a least-privilege identity, and a metric namespace approved for testing. Keep live details out of test reports and issue comments.
Limitations
The Azure Monitor connector does not:
- acquire Microsoft Entra tokens itself;
- manage Azure resources, resource groups, subscriptions, alert rules, action groups, workspaces, or dashboards;
- export raw subjects, payloads, table names, file paths, classifications, or mission metadata;
- provide a durable telemetry queue;
- guarantee Azure Monitor ingestion, retention, alert evaluation, or regional availability;
- participate in sink delivery success, ACK, NAK, DLQ, retry, fan-out, or idempotency decisions.