<!--startmeta
custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/collector/azure_monitor/integrations/azure_key_vault.md"
meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/plugin/go.d/collector/azure_monitor/metadata.yaml"
sidebar_label: "Azure Key Vault"
learn_status: "Published"
learn_rel_path: "Collecting Metrics/Collectors/Cloud and DevOps"
keywords: ['azure', 'key', 'vault', 'secrets', 'certificates', 'security']
message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
endmeta-->

# Azure Key Vault


<img src="https://netdata.cloud/img/azure.svg" width="150"/>


Plugin: go.d.plugin
Module: azure_monitor

<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />

## Overview

:::info

This is part of the [Azure Monitor](https://github.com/netdata/netdata/blob/master/src/go/plugin/go.d/collector/azure_monitor/integrations/azure_monitor.md) collector. No separate setup is needed -- a single Azure Monitor job discovers and monitors all supported resource types automatically.

:::

Monitor Azure Key Vault with metrics covering:

- **Availability** -- overall vault availability percentage
- **API** -- API activity (hits/results), API latency
- **Saturation** -- API saturation approaching service limits


It uses the [Azure Monitor Metrics batch API](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/migrate-to-batch-api) to collect metrics, grouping requests by subscription, region, and time grain. Resources are discovered via [Azure Resource Graph](https://learn.microsoft.com/en-us/azure/governance/resource-graph/overview) queries at startup and refreshed periodically. Authentication is handled through [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/identity/) (service principal, managed identity, or default credentials).


This collector is supported on all platforms.

This collector supports collecting metrics from multiple instances of this integration, including remote instances.

The service principal or managed identity requires these Azure RBAC roles:

| Role | Purpose | Scope |
|:-----|:--------|:------|
| **Monitoring Reader** | Read Azure Monitor metrics | Subscription or resource group |
| **Reader** | Query Azure Resource Graph for resource discovery | Subscription or resource group |


### Default Behavior

#### Auto-Detection

The collector has two discovery phases:

**Bootstrap (first run)**

- With the default `profiles.mode: auto`, the collector queries Azure Resource Graph within the configured `subscription_ids` to find candidate resources.
- It matches discovered resource types against [built-in profiles](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/config/go.d/azure_monitor.profiles/default) and automatically enables the relevant ones.
- Discovery scope can be narrowed using `discovery.mode: filters` (resource groups, regions, tags) or replaced entirely with `discovery.mode: query` for a custom KQL query.
- A single job can monitor multiple subscriptions.

**Runtime (periodic refresh)**

- Periodically re-discovers resources for **already-active profile types only**.
- Controlled by `discovery.refresh_every` (default: 300 seconds, set to 0 to disable).

> **Important:** Runtime refresh does not activate new profiles. If a new resource type appears after bootstrap, restart the collector to pick it up.


#### Limits

- **Minimum collection interval:** 60 seconds (enforced). Azure Monitor metrics granularity is typically 1 minute.
- **Metrics reporting delay:** Azure Monitor metrics have a 1-3 minute reporting delay. The collector uses `query_offset` (default: 180s) as a minimum offset and automatically uses a larger effective offset for slower time-grain batches when needed.
- **API throttling:** Azure Monitor applies per-subscription rate limits. The collector uses bounded concurrency and batching to stay within limits, but monitoring many resources in a single subscription may require tuning `limits.*` options.


#### Performance Impact

The collector batches resources and metrics to minimize Azure API calls and uses bounded concurrency to avoid overwhelming the API.

**Default concurrency and batching limits:**

| Setting | Default | Description |
|:--------|:--------|:------------|
| `limits.max_concurrency` | 4 | Maximum concurrent batch queries |
| `limits.max_batch_resources` | 50 | Maximum resources per batch request |
| `limits.max_metrics_per_query` | 20 | Maximum metrics per batch request |

For large deployments, consider splitting resources across multiple jobs. If you hit Azure API rate limits, reduce `max_concurrency`.


## Setup


You can configure the **azure_monitor** collector in two ways:

| Method                | Best for                                                                                 | How to                                                                                                                                 |
|-----------------------|------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| [**UI**](#via-ui)     | Fast setup without editing files                                                         | Go to **Nodes → Configure this node → Collectors → Jobs**, search for **azure_monitor**, then click **+** to add a job. |
| [**File**](#via-file) | If you prefer configuring via file, or need to automate deployments (e.g., with Ansible) | Edit `go.d/azure_monitor.conf` and add a job.                                                                        |

:::important

UI configuration requires paid Netdata Cloud plan.

:::


### Prerequisites

#### Create an Azure monitoring principal

The collector requires a service principal or managed identity with two Azure RBAC roles:

| Role | Purpose |
|:-----|:--------|
| **Monitoring Reader** | Access Azure Monitor metrics for target resources |
| **Reader** | Query Azure Resource Graph for resource discovery |

**Option A: Service principal**

```bash
# Create service principal with Monitoring Reader role
az ad sp create-for-rbac --name "netdata-monitor" --role "Monitoring Reader" \
  --scopes /subscriptions/<subscription-id>

# Add the Reader role for resource discovery
az role assignment create --assignee <appId-from-above> \
  --role "Reader" --scope /subscriptions/<subscription-id>

# Note the appId (client_id), password (client_secret), and tenant
```

**Option B: Managed identity** (Azure VMs, VMSS, or AKS)

```bash
# Assign both roles to the VM's managed identity
az role assignment create --assignee <managed-identity-principal-id> \
  --role "Monitoring Reader" --scope /subscriptions/<subscription-id>

az role assignment create --assignee <managed-identity-principal-id> \
  --role "Reader" --scope /subscriptions/<subscription-id>
```



### Configuration

#### Options

The following options can be defined globally: `update_every`, `autodetection_retry`.

**Profile file locations:**

| Type | Path |
|:-----|:-----|
| Stock profiles | `/usr/lib/netdata/conf.d/go.d/azure_monitor.profiles/default/` |
| User overrides | `/etc/netdata/go.d/azure_monitor.profiles/` |

User profile files with the same basename as a [stock profile](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/config/go.d/azure_monitor.profiles/default) override it.
Custom profiles extend the collector's catalog -- they do not replace the discovery mechanism.


<details open><summary>Config options</summary>



| Group | Option | Description | Default | Required |
|:------|:-----|:------------|:--------|:---------:|
| **Collection** | update_every | Data collection interval (seconds). Must be at least 60. | 60 | no |
|  | autodetection_retry | Autodetection retry interval (seconds). Set 0 to disable. | 0 | no |
|  | subscription_ids | List of Azure subscription IDs to monitor. Used as the scope for resource discovery. |  | yes |
|  | cloud | Azure cloud environment: `public`, `government`, or `china`. | public | no |
|  | [query_offset](#option-collection-query-offset) | Minimum offset (seconds) subtracted from metric query windows. Increase if metrics appear incomplete. | 180 | no |
|  | timeout | Timeout for Azure Resource Graph and Azure Monitor API requests, in seconds. | 30 | no |
| **Authentication** | [auth.mode](#option-authentication-auth-mode) | Authentication method: `service_principal`, `managed_identity`, or `default`. |  | yes |
|  | auth.mode_service_principal.tenant_id | Entra ID tenant ID (required for `service_principal` mode). |  | no |
|  | auth.mode_service_principal.client_id | Entra ID application (client) ID (required for `service_principal` mode). |  | no |
|  | auth.mode_service_principal.client_secret | Entra ID client secret (required for `service_principal` mode). |  | no |
|  | auth.mode_managed_identity.client_id | Client ID for user-assigned managed identity. Leave empty for system-assigned. |  | no |
| **Discovery** | discovery.refresh_every | Interval (seconds) for refreshing discovered resources. Set `0` to disable runtime re-discovery after bootstrap. | 300 | no |
|  | [discovery.mode](#option-discovery-discovery-mode) | Resource discovery method: `filters` (structured filters) or `query` (custom KQL). | filters | no |
|  | discovery.mode_filters.resource_groups | Optional list of Azure resource groups to include in `filters` mode. | [] | no |
|  | discovery.mode_filters.regions | Optional list of Azure regions to include in `filters` mode. | [] | no |
|  | discovery.mode_filters.tags | Optional exact-match tag filters for `filters` mode. Keys are matched case-insensitively and values case-sensitively. | {} | no |
|  | [discovery.mode_query.kql](#option-discovery-discovery-mode-query-kql) | Custom Azure Resource Graph KQL for `query` mode. Must project `id`, `name`, `type`, `resourceGroup`, `location`; project `tags` too when using `virtual_nodes.by_resource_tag`. |  | no |
| **Profiles** | [profiles.mode](#option-profiles-profiles-mode) | How profiles are selected: `auto` (discover from resources), `exact` (explicit list), or `combined` (both). | auto | no |
|  | [profiles.mode_auto.entries](#option-profiles-profiles-mode-auto-entries) | Optional per-profile overrides applied only to profiles that auto-activate at bootstrap. | [] | no |
|  | [profiles.mode_exact.entries](#option-profiles-profiles-mode-exact-entries) | Explicit profile entries used by `exact` mode. | [] | no |
|  | [profiles.mode_combined.entries](#option-profiles-profiles-mode-combined-entries) | Explicit profile entries merged with auto-discovered profiles in `combined` mode. | [] | no |
| **Limits** | limits.max_concurrency | Maximum concurrent batch queries to Azure Monitor. | 4 | no |
|  | limits.max_batch_resources | Maximum resources per Azure Monitor batch request. | 50 | no |
|  | limits.max_metrics_per_query | Maximum metrics per Azure Monitor batch request. | 20 | no |
| **Virtual Node** | [vnode](#option-virtual-node-vnode) | Associates this data collection job with a [Virtual Node](https://learn.netdata.cloud/docs/netdata-agent/configuration/organize-systems-metrics-and-alerts#virtual-nodes). |  | no |
|  | [virtual_nodes.by_resource_tag](#option-virtual-node-virtual-nodes-by-resource-tag) | Creates workload virtual nodes from an Azure resource tag value. |  | no |

<a id="option-collection-query-offset"></a>
##### query_offset

Azure Monitor metrics have a built-in reporting delay of 1-3 minutes. The collector subtracts this offset from the current time when building metric query windows to avoid fetching incomplete data points.

The configured `query_offset` acts as a minimum floor. For slower metric batches, the collector automatically uses a larger effective offset when the batch time grain is longer than the configured value.

- **Default (180s)** works for most services.
- **Longer time grains** (for example `PT5M`) automatically use at least one full time grain as the effective offset.
- **Increase to 240-300s** if you still see gaps or missing data points.
- **Do not set below 60s** -- metrics will likely be incomplete.


<a id="option-authentication-auth-mode"></a>
##### auth.mode

Determines how the collector authenticates with Azure.

| Mode | When to use | Required options |
|:-----|:------------|:-----------------|
| `service_principal` | Running outside Azure, or when you need explicit credentials | `tenant_id`, `client_id`, `client_secret` |
| `managed_identity` | Running on Azure VMs, VMSS, or AKS with a managed identity | Optionally `client_id` for user-assigned identity |
| `default` | Uses the Azure SDK default credential chain (environment variables, managed identity, Azure CLI, etc.) | None |


<a id="option-discovery-discovery-mode"></a>
##### discovery.mode

Controls how the collector finds candidate Azure resources.

| Mode | Behavior |
|:-----|:---------|
| `filters` | Builds an Azure Resource Graph query from the structured `mode_filters.*` options (resource groups, regions, tags). This is the default. |
| `query` | Uses the raw KQL you provide in `discovery.mode_query.kql`. The query must project `id`, `name`, `type`, `resourceGroup`, and `location`. Project `tags` too when using `virtual_nodes.by_resource_tag`. |


<a id="option-discovery-discovery-mode-query-kql"></a>
##### discovery.mode_query.kql

A raw Azure Resource Graph KQL query used when `discovery.mode` is `query`.
See the [Azure Resource Graph query language documentation](https://learn.microsoft.com/en-us/azure/governance/resource-graph/concepts/query-language) for syntax and supported operators.

The query **must** project these five columns:

| Column | Description |
|:-------|:------------|
| `id` | Full Azure resource ID (ARM format) |
| `name` | Resource name |
| `type` | Resource type (e.g., `microsoft.sql/servers/databases`) |
| `resourceGroup` | Resource group name |
| `location` | Azure region |

If `virtual_nodes.by_resource_tag` is set, also project `tags`. Missing `tags` or non-object-shaped `tags` do not fail discovery; affected resources fall back to the default job host scope and the collector logs a warning once per successful discovery refresh.

:::info

- Returned resource `type` values should match the Azure resource types expected by the active profiles. 
- Resources with unsupported or non-matching types are ignored and do not activate profiles. 
- For the stock catalog, see [built-in profiles](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/config/go.d/azure_monitor.profiles/default).

:::

Example:

```
resources
| where tags.env =~ "prod"
| project id, name, type, resourceGroup, location, tags
```


<a id="option-profiles-profiles-mode"></a>
##### profiles.mode

Controls how the collector decides which metric profiles to activate.

| Mode | Behavior |
|:-----|:---------|
| `auto` | Discovers resource types in your subscriptions and enables matching [built-in profiles](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/config/go.d/azure_monitor.profiles/default) automatically. This is the default. |
| `exact` | Uses only the profile entries listed under `profiles.mode_exact.entries`. No auto-discovery. |
| `combined` | Merges auto-discovered profiles with the explicit entries listed under `profiles.mode_combined.entries`. |

Each profile entry uses `name` as the canonical profile basename. The basename is the profile filename without the `.yaml` / `.yml` suffix and must be lowercase.

Filter layering:

- `discovery.mode_filters.*` defines the job-wide discovery scope.
- `profiles.mode_*.entries[].filters.*` narrows resources for one profile only.
- The effective resource set for a profile is the intersection of both filter sets.
- Per-profile filters never widen or bypass the global discovery scope.
- In `discovery.mode: query`, per-profile filters can use only `resource_groups` and `regions`. If you need per-profile tag filtering there, encode it in the KQL.


<a id="option-profiles-profiles-mode-auto-entries"></a>
##### profiles.mode_auto.entries

Each entry has:

| Field | Description |
|:------|:------------|
| `name` | Canonical profile basename. |
| `filters.resource_groups` | Optional resource-group narrowing for this profile. |
| `filters.regions` | Optional region narrowing for this profile. |
| `filters.tags` | Optional tag narrowing for this profile in `discovery.mode: filters`. |

Auto-mode entries do not activate profiles on their own. They only override filtering for profiles that were auto-selected at bootstrap.
In `discovery.mode: query`, only `filters.resource_groups` and `filters.regions` apply.


<a id="option-profiles-profiles-mode-exact-entries"></a>
##### profiles.mode_exact.entries

Each entry has:

| Field | Description |
|:------|:------------|
| `name` | Canonical profile basename. |
| `filters.resource_groups` | Optional resource-group narrowing for this profile. |
| `filters.regions` | Optional region narrowing for this profile. |
| `filters.tags` | Optional tag narrowing for this profile in `discovery.mode: filters`. |

Per-profile filters only narrow the globally discovered resource set. They never widen it.
In `discovery.mode: query`, only `filters.resource_groups` and `filters.regions` apply.


<a id="option-profiles-profiles-mode-combined-entries"></a>
##### profiles.mode_combined.entries

Each entry has:

| Field | Description |
|:------|:------------|
| `name` | Canonical profile basename. |
| `filters.resource_groups` | Optional resource-group narrowing for this profile. |
| `filters.regions` | Optional region narrowing for this profile. |
| `filters.tags` | Optional tag narrowing for this profile in `discovery.mode: filters`. |

If an explicit `combined` entry matches an auto-selected profile, the collector keeps one runtime profile and overlays the explicit entry filters onto it.
In `discovery.mode: query`, only `filters.resource_groups` and `filters.regions` apply.


<a id="option-virtual-node-vnode"></a>
##### vnode

This job-level virtual node is used for metrics written to the default host scope.
If `virtual_nodes.by_resource_tag` is also set, resources that have a safe value for the configured tag are routed to workload virtual nodes instead. Resources without that tag, with an empty value, or with an unsafe hostname value continue to use the job-level `vnode` when configured.


<a id="option-virtual-node-virtual-nodes-by-resource-tag"></a>
##### virtual_nodes.by_resource_tag

Set this to an Azure resource tag key, for example `workload`, to route resource metrics under virtual nodes named after that tag value.

- Tag keys are matched case-insensitively; tag values keep their Azure case and are trimmed.
- Empty or missing tag values use the default job host scope.
- Unsafe hostname values use the default job host scope and are summarized in a warning once per successful discovery refresh.
- The virtual node GUID is deterministic from `azure_monitor:` plus the trimmed tag value, so the same Azure workload value maps to the same Azure Monitor virtual node across jobs and subscriptions.
- Virtual node metadata includes `_vnode_type=azure_workload`. Netdata also adds `_hostname` when emitting the virtual node definition.
- Workload virtual nodes increase host and alert cardinality. Health alerts defined for Azure Monitor charts attach per workload virtual node.
- The metrix scoped vec cache grows with observed workload scope and label cardinality during the collector runtime; keep the configured tag's cardinality bounded by operator policy.



</details>


#### via UI

Configure the **azure_monitor** collector from the Netdata web interface:

1. Go to **Nodes**.
2. Select the node **where you want the azure_monitor data-collection job to run** and click the :gear: (**Configure this node**). That node will run the data collection.
3. The **Collectors → Jobs** view opens by default.
4. In the Search box, type _azure_monitor_ (or scroll the list) to locate the **azure_monitor** collector.
5. Click the **+** next to the **azure_monitor** collector to add a new job.
6. Fill in the job fields, then click **Test** to verify the configuration and **Submit** to save.
    - **Test** runs the job with the provided settings and shows whether data can be collected.
    - If it fails, an error message appears with details (for example, connection refused, timeout, or command execution errors), so you can adjust and retest.


#### via File

The configuration file name for this integration is `go.d/azure_monitor.conf`.

The file format is YAML. Generally, the structure is:

```yaml
update_every: 1
autodetection_retry: 0
jobs:
  - name: some_name1
  - name: some_name2
```
You can edit the configuration file using the [`edit-config`](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#edit-configuration-files) script from the
Netdata [config directory](https://github.com/netdata/netdata/blob/master/docs/netdata-agent/configuration/README.md#locate-your-config-directory).

```bash
cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
sudo ./edit-config go.d/azure_monitor.conf
```

##### Examples

###### Service principal with structured discovery

Authenticate with a service principal and auto-discover resources across two subscriptions, filtered to the `production-rg` resource group in `eastus` with the tag `env=prod`.

```yaml
jobs:
  - name: prod
    subscription_ids:
      - "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
      - "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
    discovery:
      mode: filters
      mode_filters:
        resource_groups:
          - production-rg
        regions:
          - eastus
        tags:
          env:
            - prod
    profiles:
      mode: auto
    auth:
      mode: service_principal
      mode_service_principal:
        tenant_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        client_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        client_secret: "your-client-secret"

```
###### Workload virtual nodes from resource tags

Route resources with `tags.workload` to virtual nodes named after the workload value. Resources without the tag stay on the default job host scope.

<details open><summary>Config</summary>

```yaml
jobs:
  - name: prod-workloads
    subscription_ids:
      - "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    virtual_nodes:
      by_resource_tag: workload
    discovery:
      mode: filters
    profiles:
      mode: auto
    auth:
      mode: service_principal
      mode_service_principal:
        tenant_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        client_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        client_secret: "your-client-secret"

```
</details>

###### Custom KQL with workload virtual nodes

In `query` discovery mode, project `tags` so `virtual_nodes.by_resource_tag` can derive workload virtual nodes.

<details open><summary>Config</summary>

```yaml
jobs:
  - name: prod-query-workloads
    subscription_ids:
      - "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    virtual_nodes:
      by_resource_tag: workload
    discovery:
      mode: query
      mode_query:
        kql: |
          resources
          | where tags.env =~ "prod"
          | project id, name, type, resourceGroup, location, tags
    profiles:
      mode: auto
    auth:
      mode: default

```
</details>

###### Job virtual node plus workload virtual nodes

Use a job-level `vnode` as the default host scope while tagged resources route to workload virtual nodes.

<details open><summary>Config</summary>

```yaml
jobs:
  - name: prod-with-fallback-node
    vnode: azure-fallback-node
    subscription_ids:
      - "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    virtual_nodes:
      by_resource_tag: workload
    discovery:
      mode: filters
    profiles:
      mode: auto
    auth:
      mode: managed_identity

```
</details>

###### Managed identity with exact profiles

Use a managed identity (on an Azure VM, VMSS, or AKS) and monitor only SQL Database and PostgreSQL Flexible Server resources -- skip auto-discovery of other services.

<details open><summary>Config</summary>

```yaml
jobs:
  - name: databases
    subscription_ids:
      - "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    profiles:
      mode: exact
      mode_exact:
        entries:
          - name: sql_database
          - name: postgres_flexible
    auth:
      mode: managed_identity

```
</details>

###### Global and per-profile filters together

Apply a global discovery boundary to the whole job, then narrow only the SQL Database profile further with a per-profile tag filter.

<details open><summary>Config</summary>

```yaml
jobs:
  - name: prod-databases
    subscription_ids:
      - "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    discovery:
      mode: filters
      mode_filters:
        resource_groups:
          - production-rg
        regions:
          - eastus
    profiles:
      mode: combined
      mode_combined:
        entries:
          - name: sql_database
            filters:
              tags:
                env:
                  - prod
    # Effective scope:
    # 1. Discovery first keeps only resources in production-rg and eastus.
    # 2. Auto-selected profiles use that global scope as-is.
    # 3. The sql_database profile is narrowed further to resources tagged env=prod.
    # 4. The sql_database profile cannot see resources outside production-rg/eastus,
    #    because per-profile filters only narrow the globally discovered set.
    auth:
      mode: managed_identity

```
</details>

###### Custom Azure Resource Graph KQL

Replace the built-in discovery filters with your own KQL query. Useful when you need joins, computed columns, or filtering logic that structured filters cannot express.

<details open><summary>Config</summary>

```yaml
jobs:
  - name: prod-query
    subscription_ids:
      - "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    discovery:
      mode: query
      mode_query:
        kql: |
          resources
          | where tags.env =~ "prod"
          | project id, name, type, resourceGroup, location, tags
    profiles:
      mode: auto
    auth:
      mode: default

```
</details>

###### Azure Government cloud

Connect to an Azure Government environment. Set `cloud: government` to use the correct authentication and API endpoints.

<details open><summary>Config</summary>

```yaml
jobs:
  - name: gov
    subscription_ids:
      - "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    cloud: government
    auth:
      mode: service_principal
      mode_service_principal:
        tenant_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        client_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        client_secret: "your-client-secret"

```
</details>



## Alerts


The following alerts are available:

| Alert name  | On metric | Description |
|:------------|:----------|:------------|
| [ am_key_vault_availability ](https://github.com/netdata/netdata/blob/master/src/health/health.d/azure_monitor_key_vault.conf) | azure_monitor.key_vault.availability | Key Vault availability on ${label:resource_name} |
| [ am_key_vault_api_latency ](https://github.com/netdata/netdata/blob/master/src/health/health.d/azure_monitor_key_vault.conf) | azure_monitor.key_vault.api_latency | Key Vault API latency on ${label:resource_name} |
| [ am_key_vault_saturation ](https://github.com/netdata/netdata/blob/master/src/health/health.d/azure_monitor_key_vault.conf) | azure_monitor.key_vault.saturation | Key Vault saturation on ${label:resource_name} |



## Metrics

Metrics grouped by *scope*.

The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.




### Per resource

These metrics refer to each monitored Azure resource.

Labels:

| Label      | Description     |
|:-----------|:----------------|
| resource_name | The Azure resource name. |
| resource_group | The Azure resource group. |
| region | The Azure region where the resource is deployed. |
| resource_type | The Azure resource type identifier. |
| profile | The Azure Monitor profile basename. |
| subscription_id | The Azure subscription identifier. |
| resource_uid | The unique Azure resource identifier. |

Metrics:

| Metric | Description | Dimensions | Unit |
|:------|:------------|:----------|:----|
| azure_monitor.key_vault.availability | Azure Key Vault Availability | average | percentage |
| azure_monitor.key_vault.api_latency | Azure Key Vault API Latency | average | milliseconds |
| azure_monitor.key_vault.saturation | Azure Key Vault Saturation | average | percentage |
| azure_monitor.key_vault.api_activity | Azure Key Vault API Activity | hits, results | operations/s |



## Troubleshooting

### Debug Mode

**Important**: Debug mode is not supported for data collection jobs created via the UI using the Dyncfg feature.

To troubleshoot issues with the `azure_monitor` collector, run the `go.d.plugin` with the debug option enabled. The output
should give you clues as to why the collector isn't working.

- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
  your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.

  ```bash
  cd /usr/libexec/netdata/plugins.d/
  ```

- Switch to the `netdata` user.

  ```bash
  sudo -u netdata -s
  ```

- Run the `go.d.plugin` to debug the collector:

  ```bash
  ./go.d.plugin -d -m azure_monitor
  ```

  To debug a specific job:

  ```bash
  ./go.d.plugin -d -m azure_monitor -j jobName
  ```

### Getting Logs

If you're encountering problems with the `azure_monitor` collector, follow these steps to retrieve logs and identify potential issues:

- **Run the command** specific to your system (systemd, non-systemd, or Docker container).
- **Examine the output** for any warnings or error messages that might indicate issues.  These messages should provide clues about the root cause of the problem.

#### System with systemd

Use the following command to view logs generated since the last Netdata service restart:

```bash
journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep azure_monitor
```

#### System without systemd

Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name:

```bash
grep azure_monitor /var/log/netdata/collector.log
```

**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues.

#### Docker Container

If your Netdata runs in a Docker container named "netdata" (replace if different), use this command:

```bash
docker logs netdata 2>&1 | grep azure_monitor
```

### No metrics are collected

Check the following:

- **Permissions** -- The principal has both **Monitoring Reader** and **Reader** roles on the target subscription.
- **Subscription IDs** -- The `subscription_ids` list includes the correct subscription(s).
- **Resources are active** -- Verify in Azure Portal > Metrics that the resources are producing metrics.
- **Collector logs** -- Check for authentication or API errors:
  ```bash
  # systemd
  journalctl -u netdata --namespace=netdata --grep azure_monitor --since "5 minutes ago"
  # non-systemd
  grep azure_monitor /var/log/netdata/collector.log
  ```


### Missing metrics for some resource types

Profiles are matched by Azure resource type. If a resource type exists but metrics are missing:

- **Check profile mode** -- Ensure `profiles.mode: auto` (default), or explicitly list the profile basename under `profiles.mode_exact.entries` or `profiles.mode_combined.entries`.
- **Verify a [built-in profile](https://github.com/netdata/netdata/tree/master/src/go/plugin/go.d/config/go.d/azure_monitor.profiles/default) exists** -- List available profiles:
  ```bash
  ls /usr/lib/netdata/conf.d/go.d/azure_monitor.profiles/default/
  ```
- **Check resource activity** -- Some metrics only appear when the resource is actively processing data (e.g., IoT Hub telemetry metrics require devices to be sending messages).
- **New resource types after startup** -- Runtime discovery does not activate new profiles. Restart the collector if new resource types were added after bootstrap.


### Charts have gaps or incomplete data

Azure Monitor metrics have a built-in reporting delay of **1-3 minutes**.

- The collector uses `query_offset` (default: **180 seconds**) as the minimum offset for metric query windows.
- Slower time-grain batches automatically use a larger effective offset when needed.
- If metrics are still missing or incomplete, increase `query_offset` to **240** or **300** seconds.


### Workload virtual nodes are not created

When `virtual_nodes.by_resource_tag` is set, check the discovered resource tags:

- The configured tag key is matched case-insensitively, but the value must be non-empty after trimming.
- In `discovery.mode: query`, the KQL must project `tags`. If the `tags` column is missing or not an object, the collector logs a warning and affected resources use the default job host scope.
- Tag values must be safe Netdata hostnames. Unsafe values are summarized in a warning once per successful discovery refresh and affected resources use the default job host scope.
- The same Azure workload value intentionally maps to the same Azure Monitor virtual node across subscriptions and jobs. Use distinct tag values when tenants should remain separate.
- Azure Monitor namespaces workload GUID input with `azure_monitor:` to avoid accidental collisions with other collectors that derive virtual node GUIDs from hostnames.


### More alerts appear after enabling workload virtual nodes

Workload virtual nodes multiply the host scope of Azure Monitor charts. Health alerts attached to those charts evaluate per workload virtual node, so alert volume can increase with the number of distinct workload tag values.


### Authentication errors in sovereign clouds

For Azure Government or Azure China clouds, set the `cloud` parameter:

- Azure Government: `cloud: government`
- Azure China (21Vianet): `cloud: china`

Ensure the service principal is registered in the correct cloud tenant.
