Azure Metrics Integration

Kloudfuse collects Azure Monitor metrics using the Cloud Exporter — a scraper that runs inside your cluster and periodically pulls metrics from the Azure Monitor Metrics API. No additional agents or Azure-side configuration are required beyond the initial Service Principal setup.

For an overview of how this fits into the broader Azure integration architecture, see Azure Integration Architecture.

Overview

The Cloud Exporter polls the Azure Monitor Metrics API on a fixed interval — every minute by default — and stores the retrieved metric time series directly in Kloudfuse. It supports multiple Azure subscriptions simultaneously, so a single Cloud Exporter deployment can collect metrics from all subscriptions in your organization.

Metric collection is opt-in by resource type. You list the Azure resource types you want in custom-values.yaml; Kloudfuse owns which metrics, aggregations, time grains, and dimension filters are collected for each type — you never specify individual metric names. An empty or absent list collects nothing.

Kloudfuse supports built-in Azure Monitor metrics across Compute (Virtual Machines and Scale Sets), Containers (AKS), Networking (Load Balancers and Application Gateways), Storage (Storage Accounts, blobs, and file shares), Databases (Azure Database for PostgreSQL), Cache (Redis), Messaging (Event Hubs and IoT Hub), Cognitive Services, and Azure Data Explorer (Kusto). For the full list of supported resource types and the metrics collected under each, see Supported Azure Metrics.

For a resource type that is not in the catalog yet, define your own scrape jobs inline with customResourceTypes (see Add a Resource Type That Isn’t Supported Yet) — collected in addition to the catalog picks, without waiting for a release.

Prerequisites

  • A running Kloudfuse deployment (AKS, GKE, or EKS — the Cloud Exporter connects to Azure Monitor over HTTPS and works regardless of where the Kloudfuse cluster runs).

  • Azure CLI (az) installed and authenticated, or access to the Azure Portal.

  • Permissions to create app registrations and role assignments in your Azure Active Directory tenant.

Step 1: Create a Service Principal

The Cloud Exporter authenticates to Azure using a Service Principal. Create one using the Azure CLI:

az ad sp create-for-rbac \
  --name kloudfuse-cloud-exporter \
  --role "Monitoring Reader" \
  --scopes /subscriptions/<subscription-id>
bash

The output contains the values you need:

{
  "appId":       "<AZURE_CLIENT_ID>",
  "displayName": "kloudfuse-cloud-exporter",
  "password":    "<AZURE_CLIENT_SECRET>",
  "tenant":      "<AZURE_TENANT_ID>"
}
json

Save appId, password, and tenant — you will use them in the Helm configuration.

The --role "Monitoring Reader" flag grants read-only access to Azure Monitor metrics for the specified subscription. It does not grant access to resource configurations or any control-plane operations.

Step 2: Assign Monitoring Reader to Additional Subscriptions

If you want to collect metrics from multiple subscriptions, assign the Monitoring Reader role for each one:

az role assignment create \
  --assignee <AZURE_CLIENT_ID> \
  --role "Monitoring Reader" \
  --scope /subscriptions/<additional-subscription-id>
bash

Repeat for each subscription. The subscription IDs are listed in the subscriptions field in the Helm values (see Step 3: Configure Kloudfuse via Helm).

Step 3: Configure Kloudfuse via Helm

Enable the Cloud Exporter

In your custom-values.yaml, enable the cloud-exporter under the global section:

global:
  cloud-exporter:
    enabled: true
yaml

Option A: Inline Credentials

Add the Azure credentials and subscription list directly in custom-values.yaml:

kfuse-cloud-exporter:
  azure-metrics-exporter:
    enabled: true
    subscriptions:
      - <subscription-id-1>
      - <subscription-id-2>
    secrets:
      AZURE_CLIENT_ID: "<AZURE_CLIENT_ID>"
      AZURE_TENANT_ID: "<AZURE_TENANT_ID>"
      AZURE_CLIENT_SECRET: "<AZURE_CLIENT_SECRET>"
    resourceTypes:            # opt-in list; values from the supported catalog
      - Microsoft.ContainerService/managedClusters
      - Microsoft.Cache/Redis
    scrapeInterval: 1m        # optional, default 1m
yaml
Storing credentials in plain text in custom-values.yaml is convenient for initial setup but is not recommended for production. Use Option B in production environments.

Option B: Kubernetes Secret (Recommended)

Create a Kubernetes secret in the Kloudfuse namespace (kfuse by default — replace if your installation uses a different namespace):

kubectl create secret generic azure-cloud-exporter-credentials \
  --namespace <kloudfuse-namespace> \
  --from-literal=AZURE_CLIENT_ID="<AZURE_CLIENT_ID>" \
  --from-literal=AZURE_TENANT_ID="<AZURE_TENANT_ID>" \
  --from-literal=AZURE_CLIENT_SECRET="<AZURE_CLIENT_SECRET>"
bash

Reference the secret in custom-values.yaml:

kfuse-cloud-exporter:
  azure-metrics-exporter:
    enabled: true
    subscriptions:
      - <subscription-id-1>
      - <subscription-id-2>
    resourceTypes:                        # opt-in list; values from the supported catalog
      - Microsoft.ContainerService/managedClusters
      - Microsoft.Cache/Redis
    scrapeInterval: 1m                    # optional, default 1m
    extraEnv:
      - name: AZURE_CLIENT_ID
        valueFrom:
          secretKeyRef:
            name: azure-cloud-exporter-credentials
            key: AZURE_CLIENT_ID
      - name: AZURE_TENANT_ID
        valueFrom:
          secretKeyRef:
            name: azure-cloud-exporter-credentials
            key: AZURE_TENANT_ID
      - name: AZURE_CLIENT_SECRET
        valueFrom:
          secretKeyRef:
            name: azure-cloud-exporter-credentials
            key: AZURE_CLIENT_SECRET
yaml

Configuration Reference

The azure-metrics-exporter block accepts the following fields:

Field Required Meaning

enabled

yes

Turns Azure metric collection on.

subscriptions

yes

One or more Azure subscription IDs to scrape. Every job queries all listed subscriptions.

secrets.AZURE_CLIENT_ID

yes

Service Principal (app registration) client ID.

secrets.AZURE_TENANT_ID

yes

Azure Active Directory tenant ID.

secrets.AZURE_CLIENT_SECRET

yes

Service Principal client secret. The principal needs the Monitoring Reader role on the subscriptions.

resourceTypes

yes

Resource types to collect, using values from the supported catalog. An empty or absent list collects nothing.

customResourceTypes

no

Customer-authored jobs for resource types not in the catalog, collected in addition to resourceTypes. See Add a Resource Type That Isn’t Supported Yet.

scrapeInterval

no

How often every Azure job polls, as a Prometheus duration (30s, 1m, 5m). Default 1m.

When you reference credentials from a Kubernetes secret (Option B), supply them through extraEnv instead of the inline secrets block, as shown above.

Apply the Helm Upgrade

helm upgrade --install kfuse oci://us-east1-docker.pkg.dev/mvp-demo-301906/kfuse-helm/kfuse \
  -n kfuse \
  --version <VERSION> \ (1)
  -f custom-values.yaml
1 Replace <VERSION> with a valid Kloudfuse release value. See Release Notes for the latest release.

Select the Resource Types to Collect

The resourceTypes list is the opt-in set of Azure resource types Kloudfuse collects metrics for. Use the exact resource type strings from Supported Azure Metrics — for example Microsoft.Cache/Redis. Kloudfuse owns which metrics, aggregations, time grains, and dimension filters each type collects; you never specify individual metric names.

kfuse-cloud-exporter:
  azure-metrics-exporter:
    enabled: true
    subscriptions:
      - <subscription-id>
    resourceTypes:
      - Microsoft.ContainerService/managedClusters
      - Microsoft.Cache/Redis
      - Microsoft.DBforPostgreSQL/flexibleServers
yaml

How collection behaves:

  • Opt-in only. An empty or absent resourceTypes collects nothing.

  • Unrecognized entries are silently skipped. A type not in the catalog — a typo or an unsupported type — is dropped, while valid entries in the same list still collect. There is no warning; the supported set is the catalog.

  • Metric naming. Metrics are emitted as <prefix>_<metric>, where <prefix> is derived from the resource type (shown per type in the catalog) and <metric> is the Azure metric name, lowercased with non-alphanumeric characters replaced by _. For example, Microsoft.Cache/Redis usedmemory becomes azure_cache_redis_usedmemory. You do not control these names.

  • One or more API calls per type. Metrics are grouped into jobs by (dimension filter × aggregation × time grain); Azure Monitor rejects an entire batch if any metric in it lacks a requested aggregation or filtered dimension. A single job collects at most 20 metrics.

Add a Resource Type That Isn’t Supported Yet

If a resource type is not in the catalog, define its collection jobs inline under customResourceTypes. These jobs render in addition to resourceTypes, nothing is overwritten. The shape mirrors the internal catalog: a map of Azure resource type to a list of jobs.

The following example collects Microsoft.DocumentDB/databaseAccounts (Cosmos DB), which is not in the catalog, alongside the usual catalog picks:

kfuse-cloud-exporter:
  azure-metrics-exporter:
    enabled: true
    subscriptions:
      - <subscription-id>
    resourceTypes:
      - Microsoft.ContainerService/managedClusters
    customResourceTypes:
      Microsoft.DocumentDB/databaseAccounts:
        - jobName: Microsoft.DocumentDB/databaseAccounts_requests
          metrics: [TotalRequests, MetadataRequests]
          interval: PT1M
          aggregation: [count]
        - jobName: Microsoft.DocumentDB/databaseAccounts_consumption
          metrics: [TotalRequestUnits, NormalizedRUConsumption]
          interval: PT1M
          aggregation: [average]
    scrapeInterval: 1m
yaml

Each of the two jobs above requests a single aggregation, because Azure Monitor rejects the whole batch if any metric in a job does not support the requested aggregation. TotalRequests and MetadataRequests are collected with count, while TotalRequestUnits and NormalizedRUConsumption are collected with average.

Each job accepts the following fields:

Field Required Meaning

jobName

yes

Becomes the Prometheus job label. Convention: <resourceType>, or <resourceType>_<suffix> when a type needs several jobs.

metrics

yes

Azure metric names for this job (max 20, case-sensitive — see the notes below). All must share the job’s aggregation and dimension filter.

interval

yes

Azure Monitor time grain, for example PT1M, PT5M, or PT1H.

aggregation

yes

One or more of average, total, minimum, maximum, count.

metricFilter

no

Dimension filter, for example Database eq '*'. Splits the affected metrics into a separate job.

timespan

no

Query window. Defaults to interval.

metricTop

no

Maximum number of dimension series returned, for example 500.

validateDimensions

no

false relaxes Azure’s dimension validation for the job.

Notes:

  • Use the exact Azure metric name. The value under metrics is passed verbatim to the Azure Monitor API, so each name must match Azure’s exactly, including capitalization — for example TotalRequestUnits.

  • Check each metric’s supported aggregations on that same page. A job must request only aggregations that every metric in it supports, or Azure returns a 400 for the batch — for example, Cosmos DB TotalRequests supports only count, while ProvisionedThroughput supports only maximum.

  • One job per (aggregation × time grain × dimension filter). Azure Monitor returns a 400 for the entire batch if any metric in a job lacks the requested aggregation or a filtered dimension. Split metrics that differ on any of these into separate jobs, as with _requests and _consumption above.

  • The metric prefix is auto-derived the same way as catalog types — Microsoft.DocumentDB/databaseAccounts becomes azure_documentdb_databaseaccounts. The metric suffix is normalized per the naming rule above, so TotalRequestUnits lands as azure_documentdb_databaseaccounts_totalrequestunits. You do not set the prefix.

  • Validated at render time. A job missing jobName, metrics, interval, or aggregation — or customResourceTypes set without subscriptions — fails the Helm install with a specific error, rather than shipping broken config that crashloops the metrics agent.

  • Do not also name a custom type in resourceTypes, or its jobName collides with the catalog job.

Monitor Multiple Subscriptions

List every subscription you want to collect metrics from under subscriptions. The Cloud Exporter collects metrics from all listed subscriptions using the same Service Principal, provided it has the Monitoring Reader role assigned on each one.

kfuse-cloud-exporter:
  azure-metrics-exporter:
    enabled: true
    subscriptions:
      - xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx   # Production
      - yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy   # Staging
      - zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz  # Development
    resourceTypes:                             # applied to every listed subscription
      - Microsoft.ContainerService/managedClusters
      - Microsoft.Cache/Redis
yaml

Verify the Integration

Confirm the Cloud Exporter is Running

Check that the Cloud Exporter pod is healthy:

kubectl get pods -n <kloudfuse-namespace> -l app=azure-metrics-exporter
bash

The pod status must be Running. If it is CrashLoopBackOff, check the logs:

kubectl logs -n <kloudfuse-namespace> -l app=azure-metrics-exporter --tail=100
bash

Common error messages are listed in the Troubleshooting section.

Confirm Metrics are Arriving in Kloudfuse

  1. In the Kloudfuse UI, click the Metrics tab and select Explorer from the drop-down.

  2. In the metric search field, type azure_ to list all ingested Azure Monitor metrics. Metrics follow the naming convention <prefix>_<metric_name>, where <prefix> is derived from the resource type and the metric name is lowercased with non-alphanumeric characters replaced by _, for example:

    Azure Monitor metric Kloudfuse metric name

    Microsoft.Compute/virtualMachines — Percentage CPU

    azure_compute_virtualmachines_percentage_cpu

    Microsoft.Storage/storageAccounts — UsedCapacity

    azure_storage_storageaccounts_usedcapacity

    Microsoft.Cache/Redis — usedmemory

    azure_cache_redis_usedmemory

    Microsoft.ContainerService/managedClusters — node_cpu_usage_percentage

    azure_containerservice_managedclusters_node_cpu_usage_percentage

    Microsoft.DBforPostgreSQL/flexibleServers — cpu_percent

    azure_dbforpostgresql_flexibleservers_cpu_percent

  3. Select a metric and set the time range to the last 15 minutes. A non-empty chart confirms end-to-end delivery.

The first metrics appear approximately one scrape interval (one minute by default) after the Cloud Exporter pod starts.

Troubleshooting

Cloud Exporter Pod is in CrashLoopBackOff

kubectl logs -n <kloudfuse-namespace> -l app=azure-metrics-exporter --tail=50
bash

Common causes:

Error message Resolution

ClientAuthenticationError or AADSTS70011

The Client Secret is incorrect or has expired. Regenerate it: az ad app credential reset --id <AZURE_CLIENT_ID> and update the Kubernetes secret or Helm values.

AuthorizationFailed

The Service Principal lacks the Monitoring Reader role on the target subscription. Run the role assignment command in Step 2: Assign Monitoring Reader to Additional Subscriptions.

InvalidSubscriptionId

A subscription ID in the subscriptions list is malformed. Verify each value with az account list --output table.

Secret key not found

The Kubernetes secret name or key does not match the secretKeyRef in the Helm values. Run kubectl get secret azure-cloud-exporter-credentials -n <kloudfuse-namespace> -o yaml to verify.

No Azure Metrics Appear in Kloudfuse

  1. Confirm resourceTypes is non-empty. An empty or absent list collects nothing (see Select the Resource Types to Collect).

  2. Confirm the Cloud Exporter pod is Running (see Confirm the Cloud Exporter is Running).

  3. Wait at least one scrape interval (one minute by default) after the pod starts — the first scrape takes up to one interval to complete.

  4. Check the Cloud Exporter logs for scrape errors:

    kubectl logs -n <kloudfuse-namespace> -l app=azure-metrics-exporter | grep -i error
    bash
  5. Confirm the subscription IDs in custom-values.yaml match the subscriptions returned by:

    az account list --query "[].{Name:name,ID:id}" --output table
    bash

Metrics for a Listed Resource Type Never Appear

If one entry in resourceTypes produces no metrics while others work, the type is most likely not in the supported catalog and was silently skipped — there is no warning.

  1. Check the spelling and casing against Supported Azure Metrics. Entries must match exactly, for example Microsoft.Cache/Redis.

  2. If the type genuinely is not in the catalog, collect it with customResourceTypes (see Add a Resource Type That Isn’t Supported Yet).

Metrics from One Subscription are Missing

  1. Confirm the subscription ID is listed in the subscriptions field in custom-values.yaml.

  2. Confirm the Service Principal has the Monitoring Reader role on that subscription:

    az role assignment list \
      --assignee <AZURE_CLIENT_ID> \
      --scope /subscriptions/<subscription-id> \
      --query "[].{Role:roleDefinitionName,Scope:scope}" \
      --output table
    bash
  3. If the role is missing, add it with az role assignment create as shown in Step 2: Assign Monitoring Reader to Additional Subscriptions.

TLS Verification Errors on AKS

If the Cloud Exporter cannot verify TLS certificates when connecting to Azure Monitor, it may be a corporate proxy or custom CA issue. Add the CA certificate to the pod’s trust store via the Helm values, or temporarily disable TLS verification for diagnosis only:

kfuse-cloud-exporter:
  azure-metrics-exporter:
    extraEnv:
      - name: AZURE_SDK_DISABLE_CERT_VERIFICATION
        value: "true"
yaml
Disabling TLS verification exposes the connection to interception. Only use this for diagnostic purposes and re-enable it once the root cause is identified.

External References

Prometheus Exporter

Azure documentation