# Azure Monitor Metrics

> Send Azure Monitor metrics to Last9 using OTel Collector.

Source: https://last9.io/docs/integrations/cloud-providers/azure-monitor-metrics/

Send all Azure Monitor metrics to Last9 using the OpenTelemetry Collector's `azuremonitorreceiver`. The collector polls the Azure Monitor REST API directly — no Event Hubs, no Diagnostic Settings changes, no extra Azure infrastructure.

## How it works

```
Azure Monitor REST API
        │
        │  polls every 5 min
        ▼
OTel Collector (azuremonitorreceiver)
  + azureauthextension (service principal)
        │
        │  OTLP/gRPC + gzip
        ▼
     Last9
```

The collector scrapes all metrics for resources in your configured subscription and resource groups. It requires only a service principal with the `Monitoring Reader` role — a read-only credential scoped to monitoring data.

## Prerequisites

- Azure subscription with resources emitting metrics
- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) installed
- Docker or a Kubernetes cluster (AKS)
- Last9 OTLP endpoint and authorization header (from **Settings > Integrations > OTLP**)

## Step 1 — Create a Service Principal

```shell
az ad sp create-for-rbac \
  --name "last9-monitor-reader" \
  --role "Monitoring Reader" \
  --scopes "/subscriptions/<SUBSCRIPTION_ID>"
```

Save the output — you need `appId` (CLIENT_ID), `password` (CLIENT_SECRET), and `tenant` (TENANT_ID).

:::note
`Monitoring Reader` is a read-only role scoped to monitoring data. It cannot read secrets, modify resources, or access data plane APIs.
:::

## Step 2 — Configure the Collector

Create `collector-config.yaml`:

```yaml
extensions:
  health_check:
    endpoint: 0.0.0.0:13133

  azureauth:
    service_principal:
      tenant_id: ${env:AZURE_TENANT_ID}
      client_id: ${env:AZURE_CLIENT_ID}
      client_secret: ${env:AZURE_CLIENT_SECRET}

receivers:
  azuremonitor:
    subscription_ids:
      - ${env:AZURE_SUBSCRIPTION_ID}
    resource_groups:
      - ${env:AZURE_RESOURCE_GROUP}
    collection_interval: 300s
    initial_delay: 1s
    cloud: AzureCloud
    auth:
      authenticator: azureauth

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 256
    spike_limit_mib: 64

  resource:
    attributes:
      - key: cloud.provider
        value: azure
        action: upsert
      - key: cloud.platform
        value: azure_monitor
        action: upsert
      - key: cloud.account.id
        value: ${env:AZURE_SUBSCRIPTION_ID}
        action: upsert

  batch:
    timeout: 10s
    send_batch_size: 100
    send_batch_max_size: 200

exporters:
  otlp_grpc/last9:
    endpoint: ${env:LAST9_OTLP_ENDPOINT}
    headers:
      Authorization: ${env:LAST9_OTLP_HEADER}
    compression: gzip
    timeout: 30s
    retry_on_failure:
      enabled: true
      initial_interval: 5s
      max_interval: 30s
      max_elapsed_time: 300s

service:
  extensions: [health_check, azureauth]
  pipelines:
    metrics:
      receivers: [azuremonitor]
      processors: [memory_limiter, resource, batch]
      exporters: [otlp_grpc/last9]
  telemetry:
    logs:
      level: info
```

## Step 3 — Set Environment Variables

Create a `.env` file (do not commit):

```shell
AZURE_TENANT_ID=<your-tenant-id>
AZURE_SUBSCRIPTION_ID=<your-subscription-id>
AZURE_CLIENT_ID=<service-principal-app-id>
AZURE_CLIENT_SECRET=<service-principal-password>
AZURE_RESOURCE_GROUP=<resource-group-to-monitor>

LAST9_OTLP_ENDPOINT=<otlp-endpoint>    # e.g. otlp-aps1.last9.io:443
LAST9_OTLP_HEADER=<authorization-header>
```

## Step 4 — Run

**Docker:**

```shell
docker run --rm \
  --env-file .env \
  -v $(pwd)/collector-config.yaml:/etc/otelcol-contrib/config.yaml \
  otel/opentelemetry-collector-contrib:latest
```

**AKS (Kubernetes):**

```shell
# Create secret
kubectl create secret generic last9-azure-monitor \
  --from-literal=AZURE_TENANT_ID="..." \
  --from-literal=AZURE_SUBSCRIPTION_ID="..." \
  --from-literal=AZURE_CLIENT_ID="..." \
  --from-literal=AZURE_CLIENT_SECRET="..." \
  --from-literal=AZURE_RESOURCE_GROUP="..." \
  --from-literal=LAST9_OTLP_ENDPOINT="..." \
  --from-literal=LAST9_OTLP_HEADER="..."
```

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: otelcol-azure-monitor
spec:
  replicas: 1
  selector:
    matchLabels:
      app: otelcol-azure-monitor
  template:
    metadata:
      labels:
        app: otelcol-azure-monitor
    spec:
      containers:
        - name: collector
          image: otel/opentelemetry-collector-contrib:latest
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 300Mi
          envFrom:
            - secretRef:
                name: last9-azure-monitor
          volumeMounts:
            - name: config
              mountPath: /etc/otelcol-contrib/config.yaml
              subPath: config.yaml
      volumes:
        - name: config
          configMap:
            name: otelcol-azure-monitor
```

## Tuning

| Parameter                  | Default | Guidance                                                                                 |
| -------------------------- | ------- | ---------------------------------------------------------------------------------------- |
| `collection_interval`      | 300s    | Azure Monitor refreshes most metrics every 1–5 min; lowering below 60s rarely adds value |
| `resource_groups`          | all     | Always scope to specific groups to avoid hitting Azure Monitor API rate limits           |
| `services`                 | all     | Add a `services:` filter to limit to specific `Microsoft.*` resource types               |
| `memory_limiter.limit_mib` | 256     | Increase to 512+ for subscriptions with 1000+ resources                                  |

## Metrics naming

Metrics arrive in Last9 following the pattern:

```
azure_<resource_type>_<metric_name>
```

Examples:

- `azure_microsoft_containerapp_apps_requests`
- `azure_microsoft_compute_virtualmachines_percentage_cpu`
- `azure_microsoft_sql_servers_databases_dtu_consumption_percent`

Every metric includes resource attributes for filtering:

| Attribute          | Example                                 |
| ------------------ | --------------------------------------- |
| `cloud.provider`   | `azure`                                 |
| `cloud.platform`   | `azure_monitor`                         |
| `cloud.account.id` | `77514676-...`                          |
| `resource.id`      | `/subscriptions/.../resourceGroups/...` |
| `resource.group`   | `my-resource-group`                     |
| `resource.region`  | `eastus`                                |

## Querying metrics

Once metrics are flowing, open [app.last9.io/metrics](https://app.last9.io/metrics) to browse and query your Azure Monitor metrics using PromQL.
