Azure RateCard API

My name is Satish Kumar Jaiswal. I work for Hitachi Ltd., R&D Group, Services Computing Research Department. In this article, I will explain how to use the Azure RateCard API. This API is used to fetch the pricing information of a resource in a subscription. Eg. You can use this API to query the hourly rate of a VM or monthly rate of a bulk storage on Azure.

The official documentation of this API is very descriptive. However, there are very few good documents that detail the initial setups required before you can start using this API. The main focus of this article is on the initial setups.  

# 1. Introduction

Azure Resource RateCard API is a REST API provided by Microsoft Azure. It is used to fetch the pricing information for all the available resources in an Azure subscription under consideration. 

The following diagram shows the steps involved in making an API call to the RateCard API. A **client application** is an application that is registered by the user either using Azure Portal or Azure CLI. Refer to 2.1 on how to register a client application using Azure CLI. 

<img width="727" alt="Screen Shot 2019-12-17 at 11.42.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546248/205ab049-888e-68aa-4aca-983859c3a844.png">


An HTTP request to this API has the following format.

```
GET https://management.azure.com/subscriptions
  /{SUBSCRIPTION_ID}/providers
  /Microsoft.Commerce/RateCard
  ?api-version={API_VERSION}
  &$filter=OfferDurableId eq '{OFFER_DURABLE_ID}' and Currency eq '{CURRENCY}' and Locale eq '{LOCALE}' and RegionInfo eq '{REGION_INFO}'
Accept: application/json
Authorization: Bearer {ACCESS_TOKEN}
```

where,

 - SUBSCRIPTION_ID: It is associated with your Azure account, and can be confirmed from your Azure Portal under Cost Management + Billing.
 - [API_VERSION](https://docs.microsoft.com/en-us/previous-versions/azure/reference/mt219005%28v%3dazure.100%29): There are two versions for the RateCard API as of now. They are "2015-06-01-preview" and "2016-08-31-preview".
 - The followings are the filter options.
  - [OFFER_DURABLE_ID](https://azure.microsoft.com/en-us/support/legal/offer-details/): Eg. "MS-AZR-0003P" for "Pay-As-You-Go", and "0044P" for "Free Trial", etc.
  - CURRENCY: Eg. JPY, USD, etc.
  - LOCALE: Eg. ja-JP, en-US, etc.
  - REGION_INFO: Eg. JP, US, etc.
 - **ACCESS_TOKEN: It is an Azure Active Directory token of 1-hour life-span. The details of how to acquire this token are explained below.**


# 2. Pre-requisite

Apart from having an Azure account, a subscription in your Azure account and [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) installed on your system, you need to 

 - register an application in Azure Active Directory with all the necessary permissions
 - acquire an access token for the newly registered application so that you are able to make requests to the RateCard API.

## 2.1 Register a client application with permissions

To make a REST API call to the RateCard API, first of all we need a client application with necessary permissions (refer to the figure above). In this section, I will describe how to register a client application and assign a role to it.

Before we register an application, we need to activate a few APIs that are relevant to the RateCard API.

```
$ az provider register --namespace Microsoft.Commerce
$ az provider register --namespace Microsoft.Compute
$ az provider register --namespace Microsoft.Resources
$ az provider register --namespace Microsoft.ContainerService
```

Next, we need to create a role with all the necessary permissions. The role definition file below contains the details of the permissions set for this role. Make sure to replace **SUBSCRIPTION_ID** with your own. You can confirm the subscription ID from your Azure Portal under Cost Management + Billing.

```myrole.json
{
 “Name”: “MyRateCardAPIRole”,
 “IsCustom”: true,
 “Description”: “Role for RateCardAPI”,
 “Actions”: [
 “Microsoft.Compute/virtualMachines/vmSizes/read”,
 “Microsoft.Resources/subscriptions/locations/read”,
 “Microsoft.Resources/providers/read”,
 “Microsoft.ContainerService/containerServices/read”,
 “Microsoft.Commerce/RateCard/read”
 ],
 “AssignableScopes”: [
 “/subscriptions/{SUBSCRIPTION_ID}”
 ]
}
```

```
$ az role definition create --verbose --role-definition @myrole.json

{
  "assignableScopes": [
    "/subscriptions/{SUBSCRIPTION_ID}"
  ],
  "description": "Role for RateCardAPI",
  "id": "/subscriptions/{SUBSCRIPTION_ID}/providers/Microsoft.Authorization/roleDefinitions/{ID}",
  "name": "{ID}",
  "permissions": [
    {
      "actions": [
        "Microsoft.Compute/virtualMachines/vmSizes/read",
        "Microsoft.Resources/subscriptions/locations/read",
        "Microsoft.Resources/providers/read",
        "Microsoft.ContainerService/containerServices/read",
        "Microsoft.Commerce/RateCard/read"
      ],
      "dataActions": [],
      "notActions": [],
      "notDataActions": []
    }
  ],
  "roleName": "MyRateCardAPIRole",
  "roleType": "CustomRole",
  "type": "Microsoft.Authorization/roleDefinitions"
}
```
 


Finally, to register the application execute the following command. It registers an application called MyRateCardAPIApp and associates the role called MyRateCardAPIRole. 

```
$ az ad sp create-for-rbac --name "MyRateCardAPIApp" --role "MyRateCardAPIRole" --sdk-auth true > my_credentials.json

Changing "MyRateCardAPIApp" to a valid URI of "http://MyRateCardAPIApp", which is the required format used for service principal names
Creating a role assignment under the scope of "/subscriptions/95f893dc-95db-4fe2-a150-e5d535723cdc"
  Retrying role assignment creation: 1/36
  Retrying role assignment creation: 2/36
```


```my_credentials.json
{
  "clientId": "{CLIENT_ID}",
  "clientSecret": "{CLIENT_SECRET}",
  "subscriptionId": "{SUBSCRIPTION_ID}",
  "tenantId": "{TENANT_ID}",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}
```
## 2.2 Acquire an access token (Step 1. and 2. in the fig.)

As described in the figure above, we will make a **client_credential call** to the Azure Active Directory. The following command makes a client_credential call to the Azure Active Directory and fetches the access token.

```
$ curl https://login.microsoftonline.com/{TENANT_ID}/oauth2/token \
  -F grant_type=client_credentials \
  -F resource=https://management.core.windows.net/ \
  -F client_id={CLIENT_ID} \
  -F client_secret={CLIENT_SECRET}

{
  "token_type": "Bearer",
  "expires_in": "3600",
  "ext_expires_in": "3600",
  "expires_on": "1576495607",
  "not_before": "1576491707",
  "resource": "https://management.core.windows.net/",
  "access_token": "{ACCESS_TOKEN}"
}
```

where, 

 - {TENANT_ID}: You can confirm it from **Azure Active Directory / Properties / Directory ID** in your Azure Portal.
 - {CLIENT_ID}: You can confirm it in the my_credentials.json file generated above.
 - {CLIENT_SECRET}: You can confirm it in the my_credentials.json file generated above.

# 3. Make a REST API call to the RateCard API (Step 3. and 4. in the fig.)

The following command calls the RateCardAPI using the access token acquired above and stores the output in a JSON file called output.json. Make sure to replace SUBSCRIPTION_ID and ACCESS_TOKEN with whatever is applicable to you.

```
$ curl -L
  "https://management.azure.com/subscriptions
  /{SUBSCRIPTION_ID}/providers
  /Microsoft.Commerce/RateCard
  ?api-version=2016-08-31-preview
  &$filter=OfferDurableId+eq+'MS-AZR-0003P'+and+Currency+eq+'USD'+and+Locale+eq+'en-US'+and+RegionInfo+eq+'US'" 
  -H 'Authorization: Bearer {ACCESS_TOKEN}' > outout.json
```

The output.json file is as big as 16MB! It contains information about the rate in an array called **Meters**. Each element of this array is a JSON object. I have listed below some of the important fields which might be of interest to filtering.
 
 - MeterCategory: "Virtual Machines", "Storage", etc.
 - MeterSubCategory
 - MeterRegion

```output.json
{
  "OfferTerms": [],
  "Meters": [
    .
    .
    .    
    {
      "EffectiveDate": "2018-06-01T00:00:00Z",
      "IncludedQuantity": 0,
      "MeterCategory": "Virtual Machines",
      "MeterId": "85373a0c-6ece-41bd-8d07-34477114df1b",
      "MeterName": "D12/DS12",
      "MeterRates": {
        "0": 0.442
      },
      "MeterRegion": "EU West",
      "MeterStatus": "Active",
      "MeterSubCategory": "D/DS Series",
      "MeterTags": [],
      "Unit": "1 Hour"
    },
    .
    .
    . 
    {
      "EffectiveDate": "2019-07-12T00:00:00Z",
      "IncludedQuantity": 0,
      "MeterCategory": "Storage",
      "MeterId": "616ae87f-477b-4554-ba7d-f88be91d2e43",
      "MeterName": "Hot ZRS Write Operations",
      "MeterRates": {
        "0": 0.137
      },
      "MeterRegion": "CH West",
      "MeterStatus": "Active",
      "MeterSubCategory": "General Block Blob v2 Hierarchical Namespace",
      "MeterTags": [],
      "Unit": "10K"
    }
  ],
  "Currency": "USD",
  "Locale": "en-US",
  "IsTaxIncluded": false
}
``` 

# 4. Filtering the relevant information 

The output obtained from the RateCard API is assumed to be stored and then processed for the relevant information. There are many options available to process a JSON file. In this section, I will show how we can use jq to filter the required information. 

The following command lists the rate of all the Av2 Series VMs in the Japan East region and runs a Windows operating system.

```
$ cat rate-card-api-output.json | jq '.Meters[] | select(.MeterCategory=="Virtual Machines" and .MeterRegion=="JA East" and .MeterSubCategory=="Av2 Series Windows")' > output_filter.json
```

There are 14 records that match the above filtering criteria. You can narrow down your search by other fields as well. 

```outut_filter.json
{
  "EffectiveDate": "2016-11-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "9995cfdb-2107-4bb8-8045-9bddd1773493",
  "MeterName": "A8 v2",
  "MeterRates": {
    "0": 0.75
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2016-11-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "562a0a36-b282-4c60-b636-c137ff56e83f",
  "MeterName": "A2 v2",
  "MeterRates": {
    "0": 0.17
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2016-11-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "f45bb57d-d696-4148-9727-02bdf14ee397",
  "MeterName": "A8m v2",
  "MeterRates": {
    "0": 1.025
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2017-04-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "a5e1e506-3706-4372-8c4f-9a49543ba2f7",
  "MeterName": "A1 v2 Low Priority",
  "MeterRates": {
    "0": 0.032
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2016-11-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "817948c5-969f-4529-a58e-55ba7f06813f",
  "MeterName": "A4m v2",
  "MeterRates": {
    "0": 0.488
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2017-04-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "00502626-d537-496c-8afd-ca6683cd0976",
  "MeterName": "A8 v2 Low Priority",
  "MeterRates": {
    "0": 0.3
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2016-11-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "0377be69-4b0d-40a5-b3b7-4a67f0ef62e4",
  "MeterName": "A2m v2",
  "MeterRates": {
    "0": 0.233
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2017-04-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "e8cf2cf6-30e8-4e84-a6a7-52d15ac1167d",
  "MeterName": "A4 v2 Low Priority",
  "MeterRates": {
    "0": 0.143
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2017-04-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "2d0d64b1-339a-4c5a-b5e6-77c6601b7008",
  "MeterName": "A8m v2 Low Priority",
  "MeterRates": {
    "0": 0.41
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2017-04-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "9ba0c2c5-b8bb-4e41-9f58-197e4a8abdc8",
  "MeterName": "A4m v2 Low Priority",
  "MeterRates": {
    "0": 0.195
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2017-04-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "c7cb29f6-d7a9-4ff3-8401-2625d7f122c7",
  "MeterName": "A2m v2 Low Priority",
  "MeterRates": {
    "0": 0.093
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2016-11-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "3ce83be4-1d7f-4f30-afc4-b35d3797db8e",
  "MeterName": "A1 v2",
  "MeterRates": {
    "0": 0.081
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2017-04-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "2fdf56c5-7ced-410a-8eb0-11f184734e7f",
  "MeterName": "A2 v2 Low Priority",
  "MeterRates": {
    "0": 0.068
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
{
  "EffectiveDate": "2016-11-01T00:00:00Z",
  "IncludedQuantity": 0,
  "MeterCategory": "Virtual Machines",
  "MeterId": "33ccb5ca-8fa8-4278-8aa2-006dc33d4c4f",
  "MeterName": "A4 v2",
  "MeterRates": {
    "0": 0.357
  },
  "MeterRegion": "JA East",
  "MeterStatus": "Active",
  "MeterSubCategory": "Av2 Series Windows",
  "MeterTags": [],
  "Unit": "1 Hour"
}
```

# 5. Summary

We made a REST API request to the RateCard API using an access token. The response of the API call was a humongous JSON object which needs further processing. While there are many options, I demonstrated a quick use-case of jq command. More interesting applications can be developed by combining the price information obtained from RateCard API with resource information obtained from [Resource SKUs - List](https://docs.microsoft.com/en-us/rest/api/compute/resourceskus/list).


# 6. References

 1. [Use Azure Billing APIs to programmatically get insight into your Azure usage](https://docs.microsoft.com/en-us/azure/billing/billing-usage-rate-card-overview)
 2. [Get price and metadata information for resources used in an Azure subscription](https://docs.microsoft.com/en-us/previous-versions/azure/reference/mt219004(v=azure.100))
 3. [Microsoft Azure Offer Details](https://azure.microsoft.com/en-us/support/legal/offer-details/)
 4. [How to query the Azure Rate Card API for cloud pricing](https://medium.com/@dmaas/how-to-query-the-azure-rate-card-api-for-cloud-pricing-complete-step-by-step-guide-4498f8b75c2c)
 5. [Azure Billing API を使った料金計算 (Usage API / RateCard API)](https://tsmatz.wordpress.com/2015/10/05/azure-billing-api-usage-api-ratecard-api/)