1"use strict";(globalThis.webpackChunkweb_opencost=globalThis.webpackChunkweb_opencost||[]).push([[2964],{67(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={code:"code",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"Prometheus is a prerequisite for OpenCost installation. OpenCost requires Prometheus for scraping metrics and data storage. For the installation of Prometheus please use the following command:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"helm install prometheus --repo https://prometheus-community.github.io/helm-charts prometheus \\\n --namespace prometheus-system --create-namespace \\\n --set prometheus-pushgateway.enabled=false \\\n --set alertmanager.enabled=false \\\n -f https://raw.githubusercontent.com/opencost/opencost/develop/kubernetes/prometheus/extraScrapeConfigs.yaml\n"})}),"\n",(0,t.jsxs)(n.p,{children:["This will install Prometheus in the ",(0,t.jsx)(n.code,{children:"prometheus-system"})," namespace with default settings for use with OpenCost."]})]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},550(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={code:"code",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["Create the ",(0,t.jsx)(n.code,{children:"opencost"})," namespace for your installation:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"kubectl create namespace opencost\n"})}),"\n",(0,t.jsx)(n.p,{children:"Alternate namespaces may be used if necessary."})]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},1227(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={a:"a",p:"p",...(0,o.R)(),...e.components};return(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.a,{href:"https://helm.sh/",children:"Helm"})," is the preferred installation method for OpenCost."]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},3160(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={a:"a",code:"code",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["You may check out the source for the ",(0,t.jsx)(n.a,{href:"https://github.com/opencost/opencost-helm-chart/",children:"OpenCost Helm Chart"})," or you may install the Helm chart directly to your Kubernetes cluster. Review the ",(0,t.jsx)(n.a,{href:"https://github.com/opencost/opencost-helm-chart/blob/main/charts/opencost/values.yaml",children:"values.yaml"})," for the settings available for customization. With your custom settings in the Helm values file ",(0,t.jsx)(n.code,{children:"local.yaml"}),", install OpenCost:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"helm install opencost --repo https://opencost.github.io/opencost-helm-chart opencost \\\n --namespace opencost -f local.yaml\n"})})]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},4244(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={p:"p",...(0,o.R)(),...e.components};return(0,t.jsx)(n.p,{children:"You will need to install Prometheus, create your OpenCost namespace, configure your cluster pricing and cloud costs, and then install OpenCost."})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},5072(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={code:"code",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"OpenCost standalone Kubernetes manifest installation is no longer supported. Use the OpenCost Helm chart for installation and upgrades:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"helm repo add opencost https://opencost.github.io/opencost-helm-chart\nhelm repo update\nhelm install opencost opencost/opencost --namespace opencost --create-namespace\n"})})]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},6455(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={a:"a",p:"p",...(0,o.R)(),...e.components};return(0,t.jsxs)(n.p,{children:["You may refer to the ",(0,t.jsx)(n.a,{href:"../configuration/#cloud-costs",children:"Cloud Costs"})," documentation for configuring Cloud Costs for multiple accounts and cloud service providers."]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},6940(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={admonition:"admonition",p:"p",...(0,o.R)(),...e.components};return(0,t.jsx)(n.admonition,{type:"info",children:(0,t.jsx)(n.p,{children:"The Cloud Costs feature is included in the stable releases as of 1.108.0. Please ensure you have the latest release to access this new feature."})})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},8323(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={a:"a",p:"p",...(0,o.R)(),...e.components};return(0,t.jsxs)(n.p,{children:["If you wish to use a different Prometheus installation or work with another compatible technology, please refer to the ",(0,t.jsx)(n.a,{href:"../installation/prometheus",children:"Prometheus"})," installation page."]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},8410(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={code:"code",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.p,{children:"Upgrading the Helm chart version or updating settings may be done with the following:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"helm upgrade opencost --repo https://opencost.github.io/opencost-helm-chart opencost \\\n --namespace opencost -f local.yaml\n"})})]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},8453(e,n,s){s.d(n,{R:()=>i,x:()=>c});var t=s(6540);const o={},r=t.createContext(o);function i(e){const n=t.useContext(r);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),t.createElement(r.Provider,{value:n},e.children)}},9283(e,n,s){s.d(n,{Ay:()=>c,RM:()=>r});var t=s(4848),o=s(8453);const r=[];function i(e){const n={code:"code",p:"p",pre:"pre",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)(n.p,{children:["Load the ",(0,t.jsx)(n.code,{children:"cloud-integration.json"})," into a Kubernetes secret in your ",(0,t.jsx)(n.code,{children:"opencost"})," namespace."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-sh",children:"kubectl create secret generic cloud-costs --from-file=./cloud-integration.json --namespace opencost\n"})}),"\n",(0,t.jsx)(n.p,{children:"Update your local OpenCost Helm values file to match the name of the secret and enable Cloud Costs:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-yaml",children:"opencost:\n cloudIntegrationSecret: cloud-costs\n cloudCost:\n enabled: true\n"})})]})}function c(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(i,{...e})}):i(e)}},9599(e,n,s){s.r(n),s.d(n,{assets:()=>j,contentTitle:()=>x,default:()=>b,frontMatter:()=>f,metadata:()=>t,toc:()=>v});const t=JSON.parse('{"id":"configuration/azure","title":"Microsoft Azure","description":"OpenCost may be installed on Kubernetes clusters running on Azure Compute or on the Azure Kubernetes Service (AKS).","source":"@site/docs/configuration/azure.mdx","sourceDirName":"configuration","slug":"/configuration/azure","permalink":"/docs/configuration/azure","draft":false,"unlisted":false,"editUrl":"https://github.com/opencost/opencost-website/tree/main/docs/configuration/azure.mdx","tags":[],"version":"current","sidebarPosition":2,"frontMatter":{"sidebar_position":2,"title":"Microsoft Azure"},"sidebar":"docsSidebar","previous":{"title":"Amazon Web Services","permalink":"/docs/configuration/aws"},"next":{"title":"Google Cloud Platform","permalink":"/docs/configuration/gcp"}}');var o=s(4848),r=s(8453),i=s(6455),c=s(6940),a=s(8323),l=s(1227),d=s(9283),h=(s(5072),s(3160)),u=s(67),p=s(4244),m=s(550),g=s(8410);const f={sidebar_position:2,title:"Microsoft Azure"},x="Installing on Microsoft Azure",j={},v=[...p.RM,{value:"Install Prometheus",id:"install-prometheus",level:2},...u.RM,...a.RM,{value:"Create the OpenCost Namespace",id:"create-the-opencost-namespace",level:2},...m.RM,{value:"Azure Configuration",id:"azure-configuration",level:2},{value:"Azure Pricing Configuration",id:"azure-pricing-configuration",level:3},{value:"Create a Custom Azure role",id:"create-a-custom-azure-role",level:4},{value:"Create an Azure Service Principal",id:"create-an-azure-service-principal",level:4},{value:"Supply Azure Service Principal details to OpenCost",id:"supply-azure-service-principal-details-to-opencost",level:4},{value:"Installed from YAML",id:"installed-from-yaml",level:4},{value:"Installed with Helm",id:"installed-with-helm",level:4},{value:"Customer-specific pricing",id:"customer-specific-pricing",level:3},{value:"Find your billing account ID",id:"find-your-billing-account-id",level:4},{value:"Grant billing access to your Service Principal",id:"grant-billing-access-to-your-service-principal",level:4},{value:"Find the offer ID for your subscription",id:"find-the-offer-id-for-your-subscription",level:4},{value:"Configure OpenCost to use the Price Sheet API",id:"configure-opencost-to-use-the-price-sheet-api",level:4},{value:"Installed from YAML",id:"installed-from-yaml-1",level:4},{value:"Installed with Helm",id:"installed-with-helm-1",level:4},{value:"Script to assign billing role",id:"script-to-assign-billing-role",level:4},{value:"Azure Cloud Costs",id:"azure-cloud-costs",level:2},...c.RM,...d.RM,...i.RM,{value:"Install OpenCost",id:"install-opencost",level:2},...l.RM,{value:"Using the OpenCost Helm Chart",id:"using-the-opencost-helm-chart",level:3},...h.RM,{value:"Updating OpenCost via Helm",id:"updating-opencost-via-helm",level:3},...g.RM];function y(e){const n={a:"a",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"installing-on-microsoft-azure",children:"Installing on Microsoft Azure"})}),"\n",(0,o.jsx)(n.p,{children:"OpenCost may be installed on Kubernetes clusters running on Azure Compute or on the Azure Kubernetes Service (AKS)."}),"\n",(0,o.jsx)(p.Ay,{}),"\n",(0,o.jsx)(n.h2,{id:"install-prometheus",children:"Install Prometheus"}),"\n",(0,o.jsx)(u.Ay,{}),"\n",(0,o.jsx)(a.Ay,{}),"\n",(0,o.jsx)(n.h2,{id:"create-the-opencost-namespace",children:"Create the OpenCost Namespace"}),"\n",(0,o.jsx)(m.Ay,{}),"\n",(0,o.jsx)(n.h2,{id:"azure-configuration",children:"Azure Configuration"}),"\n",(0,o.jsx)(n.p,{children:"The following Microsoft documents are a helpful reference for pricing and billing questions:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"https://azure.microsoft.com/en-us
1/support/legal/offer-details/",children:"Microsoft Azure Offer Details"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"https://azure.microsoft.com/en-us/pricing/faq/",children:"Azure Pricing FAQ"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/azure/marketplace/marketplace-geo-availability-currencies",children:"Geographic availability and currency support for the commercial marketplace"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"https://portal.azure.com/#view/Microsoft_Azure_GTM/ModernBillingMenuBlade/~/Properties",children:"Azure Portal > Cost Management + Billing > Billing Account Properties"})}),"\n",(0,o.jsx)(n.li,{children:(0,o.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/azure/cost-management-billing/costs/understand-cost-mgt-data",children:"Understand Cost Management data"})}),"\n"]}),"\n",(0,o.jsx)(n.h3,{id:"azure-pricing-configuration",children:"Azure Pricing Configuration"}),"\n",(0,o.jsx)(n.p,{children:"OpenCost needs access to the Microsoft Azure Billing Rate Card API to access accurate pricing data for your Kubernetes resources."}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Note:"})," The prices used will be accurate at the time of access, but they can still change over time."]}),"\n"]}),"\n",(0,o.jsx)(n.h4,{id:"create-a-custom-azure-role",children:"Create a Custom Azure role"}),"\n",(0,o.jsxs)(n.p,{children:["Start by creating an Azure role definition. Below is an example definition, replace ",(0,o.jsx)(n.code,{children:"YOUR_SUBSCRIPTION_ID"})," with the ID of the subscription containing your Kubernetes cluster.\n(",(0,o.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription",children:"How to find your subscription ID."}),")"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-json",children:'{\n "Name": "OpenCostRole",\n "IsCustom": true,\n "Description": "Rate Card query role",\n "Actions": [\n "Microsoft.Compute/virtualMachines/vmSizes/read",\n "Microsoft.Resources/subscriptions/locations/read",\n "Microsoft.Resources/providers/read",\n "Microsoft.ContainerService/containerServices/read",\n "Microsoft.Commerce/RateCard/read"\n ],\n "AssignableScopes": [\n "/subscriptions/YOUR_SUBSCRIPTION_ID"\n ]\n}\n'})}),"\n",(0,o.jsxs)(n.p,{children:["Save this into a file called ",(0,o.jsx)(n.code,{children:"myrole.json"})]}),"\n",(0,o.jsx)(n.p,{children:"Next, you'll want to register that role with Azure:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-shell",children:"az role definition create --verbose --role-definition @myrole.json\n"})}),"\n",(0,o.jsx)(n.h4,{id:"create-an-azure-service-principal",children:"Create an Azure Service Principal"}),"\n",(0,o.jsx)(n.p,{children:"Next, create an Azure Service Principal."}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-shell",children:'az ad sp create-for-rbac --name "OpenCostAccess" --role "OpenCostRole" --scope "/subscriptions/YOUR_SUBSCRIPTION_ID" --output json\n'})}),"\n",(0,o.jsx)(n.p,{children:"Keep this information which is used in the service-key.json below."}),"\n",(0,o.jsx)(n.h4,{id:"supply-azure-service-principal-details-to-opencost",children:"Supply Azure Service Principal details to OpenCost"}),"\n",(0,o.jsxs)(n.p,{children:["Create a file called ",(0,o.jsx)(n.a,{href:"https://github.com/kubecost/poc-common-configurations/blob/main/azure/service-key.json",children:(0,o.jsx)(n.code,{children:"service-key.json"})})," and update it with the Service Principal details from the above steps:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-json",children:'{\n "subscriptionId": "<Azure Subscription ID>",\n "serviceKey": {\n "appId": "<Azure AD App ID>",\n "displayName": "OpenCostAccess",\n "password": "<Azure AD Client Secret>",\n "tenant": "<Azure AD Tenant ID>"\n }\n}\n'})}),"\n",(0,o.jsx)(n.p,{children:"Next, create a secret for the Azure Service Principal"}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Note"}),": When managing the service account key as a Kubernetes secret, the secret must reference the service account key JSON file, and that file must be named ",(0,o.jsx)(n.code,{children:"service-key.json"}),"."]}),"\n"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-shell",children:"kubectl create secret generic azure-service-key -n opencost --from-file=service-key.json\n"})}),"\n",(0,o.jsx)(n.p,{children:"Now the OpenCost deployment can be configured to mount that secret as a volume. The exact method will depend on how OpenCost was installed."}),"\n",(0,o.jsx)(n.h4,{id:"installed-from-yaml",children:"Installed from YAML"}),"\n",(0,o.jsxs)(n.p,{children:["If you installed OpenCost using the ",(0,o.jsx)(n.a,{href:"../installation/install#
1install-opencost",children:"provided YAML"}),", save that YAML and edit the ",(0,o.jsx)(n.code,{children:"opencost"})," deployment to add:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["A ",(0,o.jsxs)(n.a,{href:"https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes",children:[(0,o.jsx)(n.code,{children:"volumes"})," object"]})," under ",(0,o.jsx)(n.code,{children:"spec.template.spec"}),", with a ",(0,o.jsx)(n.code,{children:"service-key-secret"})," volume referring to the ",(0,o.jsx)(n.code,{children:"azure-service-key"})," secret:"]}),"\n"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:" volumes:\n - name: service-key-secret\n secret:\n secretName: azure-service-key\n"})}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["A ",(0,o.jsxs)(n.a,{href:"https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#volumes-1",children:[(0,o.jsx)(n.code,{children:"volumeMounts"})," object"]})," under the ",(0,o.jsx)(n.code,{children:"opencost"})," container (at ",(0,o.jsx)(n.code,{children:"spec.template.spec.containers[0]"}),") that mounts the ",(0,o.jsx)(n.code,{children:"service-key-secret"})," volume:"]}),"\n"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:" volumeMounts:\n - mountPath: /var/secrets\n name: service-key-secret\n"})}),"\n",(0,o.jsx)(n.p,{children:"Then apply the updated YAML:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-sh",children:"$ kubectl apply -f opencost.yaml -n opencost\nnamespace/opencost unchanged\nserviceaccount/opencost unchanged\nclusterrole.rbac.authorization.k8s.io/opencost unchanged\nclusterrolebinding.rbac.authorization.k8s.io/opencost unchanged\ndeployment.apps/opencost configured\nservice/opencost unchanged\n"})}),"\n",(0,o.jsx)(n.h4,{id:"installed-with-helm",children:"Installed with Helm"}),"\n",(0,o.jsxs)(n.p,{children:["If you installed OpenCost using the ",(0,o.jsx)(n.a,{href:"https://github.com/opencost/opencost-helm-chart",children:"community-supported Helm chart"}),", you can update your ",(0,o.jsx)(n.code,{children:"values.yaml"})," file to add a volume for the secret and mount it into the exporter container:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"extraVolumes:\n - name: service-key-secret\n secret:\n secretName: azure-service-key\nopencost:\n exporter:\n extraVolumeMounts:\n - mountPath: /var/secrets\n name: service-key-secret\n"})}),"\n",(0,o.jsx)(n.p,{children:"Apply those changes with:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-sh",children:"helm upgrade opencost . --namespace opencost -f values.yaml\n"})}),"\n",(0,o.jsx)(n.h3,{id:"customer-specific-pricing",children:"Customer-specific pricing"}),"\n",(0,o.jsxs)(n.p,{children:["The Rate Card prices retrieved with the setup above are the standard prices for Azure resources offered to all customers.\nIf your organisation has an Enterprise Agreement or Partner Agreement with Azure you may have discounts for some of the resources used by your clusters.\nIn that case you can configure OpenCost to use the ",(0,o.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/rest/api/consumption/",children:"Consumption Price Sheet API"})," to request prices specifically for your billing account."]}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Note"}),": Calling the Price Sheet API uses the service principal secret created above - those steps are prerequisites for this section."]}),"\n"]}),"\n",(0,o.jsx)(n.h4,{id:"find-your-billing-account-id",children:"Find your billing account ID"}),"\n",(0,o.jsxs)(n.p,{children:["You can ",(0,o.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/azure/cost-management-billing/manage/view-all-accounts",children:"find your billing account ID in the Azure portal"}),",\nor using the ",(0,o.jsx)(n.code,{children:"az"})," CLI:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-sh",children:'az billing account list --query "[].{name:name, displayName:displayName}"\n'})}),"\n",(0,o.jsx)(n.h4,{id:"grant-billing-access-to-your-service-principal",children:"Grant billing access to your Service Principal"}),"\n",(0,o.jsxs)(n.p,{children:["To call the Price Sheet API the service principal you created above needs to be granted the EnrollmentReader billing role.\nYou can do this by following ",(0,o.jsx)(n.a,{href:"https://learn.microsoft.com/en-us
1/azure/cost-management-billing/manage/assign-roles-azure-service-principals#assign-enrollment-account-role-permission-to-the-spn",children:"this Azure guide"}),"\nand using the ",(0,o.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/rest/api/billing/2019-10-01-preview/role-assignments/put?tabs=HTTP",children:"Role Assignments API reference page"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Assigning a billing role isn't directly supported in the ",(0,o.jsx)(n.code,{children:"az"})," CLI yet, so the process is quite involved.\nTo simplify this you can use the ",(0,o.jsx)(n.code,{children:"bash"})," script ",(0,o.jsx)(n.a,{href:"#script-to-assign-billing-role",children:"below"})," to collect the details of your service principal, construct the PUT request and send it with ",(0,o.jsx)(n.code,{children:"curl"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Save the script to a file named ",(0,o.jsx)(n.code,{children:"assign-billing-role.bash"})," and run it:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-sh",children:"export SP_NAME=OpenCostAccess\nexport BILLING_ACCOUNT_ID=<your billing account ID>\nchmod u+x assign-billing-role.bash\n./assign-billing-role.bash\n"})}),"\n",(0,o.jsx)(n.h4,{id:"find-the-offer-id-for-your-subscription",children:"Find the offer ID for your subscription"}),"\n",(0,o.jsxs)(n.p,{children:["As well as the billing account ID, OpenCost also needs the offer ID for your subscription to query the price sheet.\nYou can find this on the ",(0,o.jsx)(n.a,{href:"https://learn.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription",children:"subscription page in the Azure portal"}),"."]}),"\n",(0,o.jsx)(n.h4,{id:"configure-opencost-to-use-the-price-sheet-api",children:"Configure OpenCost to use the Price Sheet API"}),"\n",(0,o.jsx)(n.p,{children:"The billing account and offer ID need to be passed to OpenCost in environment variables.\nHow you do this will depend on the method used to install OpenCost."}),"\n",(0,o.jsx)(n.h4,{id:"installed-from-yaml-1",children:"Installed from YAML"}),"\n",(0,o.jsxs)(n.p,{children:["If you installed OpenCost using the ",(0,o.jsx)(n.a,{href:"../installation/install#install-opencost",children:"provided YAML"}),", open that YAML and edit the ",(0,o.jsx)(n.code,{children:"opencost"})," deployment to add two more items to the ",(0,o.jsx)(n.code,{children:"opencost"})," container environment variables (under ",(0,o.jsx)(n.code,{children:".spec.template.spec.containers[0].env"}),"):"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:" - name: AZURE_BILLING_ACCOUNT\n value: <your billing account id>\n - name: AZURE_OFFER_ID\n value: <your offer id>\n"})}),"\n",(0,o.jsx)(n.p,{children:"Then apply the updated YAML:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-sh",children:"$ kubectl apply -f opencost.yaml -n opencost\nnamespace/opencost unchanged\nserviceaccount/opencost unchanged\nclusterrole.rbac.authorization.k8s.io/opencost unchanged\nclusterrolebinding.rbac.authorization.k8s.io/opencost unchanged\ndeployment.apps/opencost configured\nservice/opencost unchanged\n"})}),"\n",(0,o.jsx)(n.h4,{id:"installed-with-helm-1",children:"Installed with Helm"}),"\n",(0,o.jsxs)(n.p,{children:["If you installed OpenCost using the ",(0,o.jsx)(n.a,{href:"https://github.com/opencost/opencost-helm-chart",children:"community-supported Helm chart"}),", you can update your ",(0,o.jsx)(n.code,{children:"values.yaml"})," file to add the required variables:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-yaml",children:"opencost:\n exporter:\n extraEnv:\n AZURE_BILLING_ACCOUNT: <your billing account id>\n AZURE_OFFER_ID: <your offer id>\n"})}),"\n",(0,o.jsx)(n.p,{children:"Apply those changes with:"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-sh",children:"helm upgrade opencost . --namespace opencost -f values.yaml\n"})}),"\n",(0,o.jsx)(n.h4,{id:"script-to-assign-billing-role",children:"Script to assign billing role"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-bash",children:'#!/bin/bash\n\n# Helper to assign the billing EnrollmentReader role to a service principal\n# Needs SP name and billing account name variables set\n\nset -euo pipefail\n\nif [[ -z "${SP_NAME}" ]]; then\n echo "SP_NAME is not set"\n exit 1\nfi\n\nif [[ -z "${BILLING_ACCOUNT_ID}" ]]; then\n echo "BILLING_ACCOUNT_ID is not set"\n exit 1\nfi\n\n# Generate a unique name for the assignment.\nROLE_ASSIGNMENT_NAME="$(uuidgen)"\n\n# Work out the SP id and tenant id from the name.\nread -r SP_ID TENANT_ID < <(az ad sp list --display-name "${SP_NAME}" --query \'[0].{id:id,tenantId:appOwnerOrganizationId}\' -o tsv)\n\n# Get bearer token for talking to API.\nACCESS_TOKEN="$(az account get-access-token --query accessToken -o tsv)"\n\nURL="https://management.azure.com/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}/billingRoleAssignments/${ROLE_ASSIGNMENT_NAME}?api-version=2019-10-01-preview"\n\necho "Creating EnrollmentReader role assignment for SP ${SP_NAME} (${SP_ID}) in billing account ${BILLING_ACCOUNT_ID}"\necho "Role assignment name: ${ROLE_ASSIGNMENT_NAME}"\n\n# This is the role definition ID for EnrollmentReader\nENROLLMENT_READER_ROLE="24f8edb6-1668-4659-b5e2-40bb5f3a7d7e"\nRESPONSE="$(curl --silent --show-error -X PUT "${URL}" \\\n-H "Authorization: Bearer ${ACCESS_TOKEN}" \\\n-H "Content-type: application/json" \\\n-d "{\n \\"properties\\": {\n \\"principalId\\": \\"${SP_ID}\\",\n \\"principalTenantId\\": \\"${TENANT_ID}\\",\n \\"roleDefinitionId\\": \\"/providers/Microsoft.Billing/billingAccounts/${BILLING_ACCOUNT_ID}/billingRoleDefinitions/${ENROLLMENT_READER_ROLE}\\"\n }\n}")"\n\necho "Response: ${RESPONSE}"\n'})}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.strong,{children:"Note:"})," For Microsoft Customer Agreement (MCA) accounts, users will see an error when attempting to download the default price sheet. On-demand node pricing for MCA accounts is instead provided through the Azure Retail Prices API. If this limitation affects you, please open or comment on an issue so we can evaluate adding support in a future release."]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"azure-cloud-costs",children:"Azure Cloud Costs"}),"\n",(0,o.jsx)(c.Ay,{}),"\n",(0,o.jsxs)(n.p,{children:["The following values can be found in the Azure Portal under ",(0,o.jsx)(n.em,{children:"Cost Management > Exports"}),", or ",(0,o.jsx)(n.em,{children:"Storage"})," accounts:"]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"<SUBSCRIPTION_ID>"})," is the Subscription ID belonging to the Storage account which stores your exported Azure cost report data."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"<STORAGE_ACCOUNT>"})," is the name of the Storage account where the exported Azure cost report data is being stored."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"<STORAGE_ACCESS_KEY>"})," can be found by selecting ",(0,o.jsx)(n.em,{children:"Access Keys"})," from the navigation sidebar then selecting Show keys. Using either of the two keys will work."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"<STORAGE_CONTAINER>"})," is the name that you chose for the exported cost report when you set it up. This is the name of the container where the CSV cost reports are saved in your Storage account."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"<CONTAINER_PATH>"})," should be used if there is more than one billing report that is exported to the configured container. The path provided should have only one billing export because OpenCost will retrieve the most recent billing report for a given month found within the path. If this configuration is not used, it should be set to an empty string ",(0,o.jsx)(n.code,{children:'""'}),"."]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.code,{children:"<CLOUD>"})," is the value which denotes the cloud where the storage account exists. Possible values are ",(0,o.jsx)(n.code,{children:"public"})," and ",(0,o.jsx)(n.code,{children:"gov"}),". The default is ",(0,o.jsx)(n.code,{children:"public"})," if an empty string is provided."]}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["Set these values to the Azure array in the ",(0,o.jsx)(n.code,{children:"cloud-integration.json"})," file:"]}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-json",children:'{\n "azure": {\n "storage": [\n {\n "subscriptionID": "<SUBSCRIPTON_ID>",\n "account": "<STORAGE_ACCOUNT>",\n "container": "<STORAGE_CONTAINER>",\n "path": "<CONTAINER_PATH>",\n "cloud": "<CLOUD>",\n "authorizer": {\n "accessKey": "<STORAGE_ACCESS_KEY>",\n "account": "<STORAGE_ACCOUNT>",\n "authorizerType": "AzureAccessKey"\n }\n },\n {\n "subscriptionID": "<SUBSCRIPTION_ID>",\n "account": "<STORAGE_ACCOUNT>",\n "container": "<EXPORT_CONTAINER>",\n "path": "",\n "cloud": "<CLOUD>",\n "authorizer": {\n "accessKey": "<ACCOUNT_ACCESS_KEY>",\n "account": "<STORAGE_ACCOUNT>",\n "authorizerType": "AzureAccessKey"\n }\n }\n ]\n }\n}\n'})}),"\n",(0,o.jsx)(d.Ay,{}),"\n",(0,o.jsx)(i.Ay,{}),"\n",(0,o.jsx)(n.h2,{id:"install-opencost",children:"Install OpenCost"}),"\n",(0,o.jsx)(l.Ay,{}),"\n",(0,o.jsx)(n.h3,{id:"using-the-opencost-helm-chart",children:"Using the OpenCost Helm Chart"}),"\n",(0,o.jsx)(h.Ay,{}),"\n",(0,o.jsx)(n.h3,{id:"updating-opencost-via-helm",children:"Updating OpenCost via Helm"}),"\n",(0,o.jsx)(g.Ay,{})]})}function b(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(y,{...e})}):y(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.