OpenCost on Microsoft Azure
Use this guide to deploy OpenCost components for Kubernetes Billing exports to Azure Blob Storage.
Prerequisites
Before you begin, make sure you have:
- Infrastructure & permissions
- An AKS cluster
- Cluster administrator access
- Required cloud permissions
Contributorrole at the subscription or resource group levelUser Access Administratorrole (required to manage service principals)
- Required tools
- Azure CLI (
az) kubectl- Helm (v3.x or later)
- Azure CLI (
Set Up a Service Principal
Create a Service Principal
- In the Azure portal, navigate to Microsoft Entra ID.
- Under Manage, select App registrations and click + New registration.
- Set:
- Name:
opencost-sp - Supported account types: Single tenant
- Name:
- After creating the application, note:
- Application (client) ID
- Directory (tenant) ID
Create a Client Secret
- Open the newly created application.
- Under Manage, select Certificates & secrets and click + New client secret.
- Set a description and select an expiry period.
- Copy and save the generated secret value. This value cannot be retrieved later.
Assign Permissions
- Navigate to the subscription that contains the AKS cluster.
- Open Access control (IAM).
- Click + Add → Add role assignment.
- Assign the following roles to the service principal:
Reader(required for cost metrics).Storage Blob Data Contributor(required for Parquet exports).
Set Up the Storage Account
- In the Azure portal, navigate to Storage accounts.
- Click Create and configure:
- Name: for example,
opencost-reports(must be globally unique, only lowercase letters and numbers). - Performance: leave the default value.
- Redundancy: leave the default value.
- Name: for example,
- Create the storage account.
Use the storage account name consistently in the later steps.
Configure Access for Cloudaware
- Open the newly created storage account.
- Go to Access control (IAM).
- Assign the
Storage Blob Data Readerrole to the service principal used for Cloudaware access:- Assign access to: User, group, or service principal.
- Members: click + Select members and select the Cloudaware application (for example,
cloudaware-api-access).
Create a Container for Exports
- Navigate to Containers and click + Container.
- Set:
- Name: for example,
opencost-exports. - Public access level: Private.
- Name: for example,
Connect to the AKS Cluster
- In the Azure portal, go to Kubernetes services.
- Select the target cluster.
- Click Connect and copy the credentials command.
- Run the
az aks get-credentialscommand locally to merge cluster credentials into your kubeconfig.
Deploy Prometheus
If you use an external Prometheus instance, skip this step and review the Configuration notes (1) for external Prometheus below.
-
Add the Prometheus Helm repository:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts -
Create the Prometheus namespace:
kubectl create namespace prometheus-system -
Install Prometheus:
helm install prometheus prometheus-community/prometheus \--namespace prometheus-system \--set prometheus-pushgateway.enabled=false \--set alertmanager.enabled=false
Deploy OpenCost
-
Add the OpenCost Helm repository:
helm repo add opencost https://opencost.github.io/opencost-helm-chart -
Create the OpenCost namespace:
kubectl create namespace opencost -
Create a Kubernetes secret for the service principal credentials. Replace
{YOUR_CLIENT_SECRET}with the value created in Step 1:kubectl create secret generic azure-secret \--from-literal=password={YOUR_CLIENT_SECRET} \--namespace opencost -
Install OpenCost:
helm --namespace opencost upgrade --install opencost opencost/opencost -f - <<EOFserviceAccount:create: truename: opencost-saopencost:prometheus:namespaceName: prometheus-systempodAnnotations:prometheus.io/path: /metricsprometheus.io/port: "9003"prometheus.io/scrape: "true"EOF
The podAnnotations ensure that Prometheus scrapes metrics from the OpenCost pods, enabling label‑based cost attribution in reports.
Configuration Notes
Configure the following Helm values depending on your setup:
-
If your cluster already has an external Prometheus, configure OpenCost to use the existing Prometheus instance. Replace
{YOUR_PROMETHEUS_URL}with the correct endpoint:opencost:prometheus:external:enabled: trueurl: {YOUR_PROMETHEUS_URL} -
If your cluster uses
prometheus-operator, enableServiceMonitor; otherwise, OpenCost metrics will not be scraped automatically:opencost:metrics:serviceMonitor:enabled: true
Deploy the Parquet Exporter
Install the OpenCost Parquet exporter.
-
To store reports in subfolders or use a naming prefix, use the command below, replacing
{YOUR_CLUSTER_PREFIX},{YOUR_AKS_CLUSTER_ID},{YOUR_OPENCOST_STORAGE},{YOUR_OPENCOST_PARQUET_AZURE_CONTAINER__NAME},{YOUR_TENANT_ID}, and{YOUR_CLIENT_ID}with the appropriate values:helm install parquet-exporter opencost/opencost-parquet-exporter \--namespace opencost \--set schedule="0 */12 * * *" \--set existingServiceAccount=opencost-sa \--values - <<EOFresources:limits:cpu: "1"memory: 1Girequests:cpu: 100mmemory: 100MisecurityContext: nullenv:- name: OPENCOST_PARQUET_SVC_HOSTNAMEvalue: "opencost.opencost.svc.cluster.local"- name: OPENCOST_PARQUET_STORAGE_BACKENDvalue: "azure"- name: OPENCOST_PARQUET_FILE_KEY_PREFIXvalue: "{YOUR_CLUSTER_PREFIX}/{YOUR_AKS_CLUSTER_ID}/"- name: OPENCOST_PARQUET_JSON_SEPARATORvalue: "_"- name: OPENCOST_PARQUET_AZURE_STORAGE_ACCOUNT_NAMEvalue: "{YOUR_OPENCOST_STORAGE}"- name: OPENCOST_PARQUET_AZURE_CONTAINER_NAMEvalue: "{YOUR_OPENCOST_PARQUET_AZURE_CONTAINER__NAME}"- name: OPENCOST_PARQUET_AZURE_TENANTvalue: "{YOUR_TENANT_ID}"- name: OPENCOST_PARQUET_AZURE_APPLICATION_IDvalue: "{YOUR_CLIENT_ID}"- name: OPENCOST_PARQUET_AZURE_APPLICATION_SECRETvalueFrom:secretKeyRef:name: azure-secretkey: passwordEOFnoteUse the AKS cluster ID without the leading forward slash (
/) inOPENCOST_PARQUET_FILE_KEY_PREFIX. Example:- name: OPENCOST_PARQUET_FILE_KEY_PREFIXvalue: prodk8s/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/opencost/providers/Microsoft.ContainerService/managedClusters/opencost-aks/ -
To store reports in the root folder, use the command below, replacing
{YOUR_AKS_CLUSTER_ID},{YOUR_OPENCOST_STORAGE},{YOUR_TENANT_ID}, and{YOUR_CLIENT_ID}:helm install parquet-exporter opencost/opencost-parquet-exporter \--namespace opencost \--set schedule="0 */12 * * *" \--set existingServiceAccount=opencost-sa \--values - <<EOFresources:limits:cpu: "1"memory: 1Girequests:cpu: 100mmemory: 100MisecurityContext: nullenv:- name: OPENCOST_PARQUET_SVC_HOSTNAMEvalue: "opencost.opencost.svc.cluster.local"- name: OPENCOST_PARQUET_STORAGE_BACKENDvalue: "azure"- name: OPENCOST_PARQUET_FILE_KEY_PREFIXvalue: "{YOUR_AKS_CLUSTER_ID}/"- name: OPENCOST_PARQUET_JSON_SEPARATORvalue: "_"- name: OPENCOST_PARQUET_AZURE_STORAGE_ACCOUNT_NAMEvalue: "{YOUR_OPENCOST_STORAGE}"- name: OPENCOST_PARQUET_AZURE_CONTAINER_NAMEvalue: "{YOUR_OPENCOST_PARQUET_AZURE_CONTAINER__NAME}"- name: OPENCOST_PARQUET_AZURE_TENANTvalue: "{YOUR_TENANT_ID}"- name: OPENCOST_PARQUET_AZURE_APPLICATION_IDvalue: "{YOUR_CLIENT_ID}"- name: OPENCOST_PARQUET_AZURE_APPLICATION_SECRETvalueFrom:secretKeyRef:name: azure-secretkey: passwordEOFnoteUse the AKS cluster ID without the leading forward slash (
/) inOPENCOST_PARQUET_FILE_KEY_PREFIX.Example:
- name: OPENCOST_PARQUET_FILE_KEY_PREFIXvalue: subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/opencost/providers/Microsoft.ContainerService/managedClusters/opencost-aks/
For multi‑cluster environments, deploy the Parquet exporter separately on each cluster.
Verify the Deployment
-
Check that all pods are running:
kubectl get pods -n prometheus-systemkubectl get pods -n opencost -
Access the OpenCost UI:
kubectl port-forward -n opencost service/opencost 9090:9090
Then open http://localhost:9090 in your browser.
The first Parquet export may take up to 24 hours. To verify the export, check your Azure Storage container for newly created files.
Next Steps
Return to the parent guide Kubernetes Billing (OpenCost) to proceed with the Kubernetes Billing setup in Cloudaware.