Maneja los precios actualizados de todo Azure en tu aplicación a través de la RateCard API

¿Has necesitado alguna vez hacer una automatización o aplicación que realice acciones en base a los precios de lista de Azure? ¿Cómo los obtenemos? ¿Hay alguna API a la que llamar? Respuestas en este artículo

4 min de lectura
Maneja los precios actualizados de todo Azure en tu aplicación a través de la RateCard API

La calculadora de precios de Microsoft Azure es una estupenda forma, rápida e intuitiva, de obtener una cotización de costes de los servicios de Azure que necesitamos para nuestra aplicación, migración, proyecto...

Sin embargo, si manejamos grándes volúmenes de elementos a cotizar, como puede ser el inventario de máquinas virtuales de una gran organización, la situación se puede volver muy tediosa.

Para ayudarnos en estas situaciones existen excelentes herramientas de código abierto como VMChooser, que no solo nos puede cotizar un listado completo de máquinas virtuales, sino que además selecciona la familia y tamaño de máquina más adecuados en Azure.

Sin embargo, puede ocurrirnos lo siguiente:

  • Por motivos de privacidad y protección de información no podemos cargar datos en un sitio web externo.
  • No tenemos infraestructura para implementar la solución de código abierto.
  • No sólo tenemos que cotizar máquinas virtuales, sino también otros recursos de Azure.

Quizá la solución sea hacernos un pequeño script o una aplicación que haga rápidamente lo que queremos. Para conseguirlo, necesitamos algún lugar oficial al que consultarle los costes de Azure, es decir, ¡necesitamos una API!

APIs disponibles para consultar costes y precios en Azure

El primer lugar donde se nos puede ocurrir consultar es la Azure Consumption API, que tiene un subset más que interesante: la Pricesheet API. Sin embargo esta API sólo está disponible para clientes Enterprise y si queremos hacer pruebas con una suscripción de myVisualStudio seguramente no tengamos mucha fortuna.

En segundo lugar, como parte de la Microsoft Partner Center REST API, tenemos una API que nos permite retirar una lista de precios de Azure. Por si fuera poco, tenemos un módulo de PowerShell listo para usar. Lamentablemente tampoco está disponible al público general y normalmente sólo podrán acceder a ella los proveedores de licenciamiento Microsoft.

En tercer lugar, la calculadora de precios de Microsoft Azure tiene una API públicamente accesible, pero no documentada, que es la que utiliza en sus cálculos. Esta es la alternativa que VMChooser utiliza, con la ventaja de no necesitar siquiera suscripción de Azure. El principal problema es que no está oficialmente soportada ni documentada por Microsoft, y aunque es bastante sencilla de utilizar, mejor ir de momento por aproximaciones más oficiales.

Llegado este punto, ¿tenemos alguna API que de algún soporte más universal? ¡Afortunadamente SÍ! La Azure RateCard API.

Azure RateCard API

Esta API nos permite obtener un listado completo del precio de todos los recursos de Azure en el momento de la consulta, con el único requisito de tener una suscripción de Azure de cualquier tipo; eliminando así las restriciones que teníamos en las alternativas anteriores.

La documentación de la API se encuentra aquí y aunque parece tener un estado de descatalogada y lleva años sin actualizarse, todavía funciona correctamente en el momento de escribir estas líneas. A falta de una alternativa mejor, esta me ha parecido la mejor opción, ¡pero si conoces alguna adicional deja un comentario indicándolo!

Entrando en materia, ¿qué es lo que nos devuelve la Azure RateCard API al consultarla? Algo parecido a esto:

EffectiveDate    : 2020-05-01T00:00:00Z
IncludedQuantity : 0,0
MeterCategory    : Virtual Machines
MeterId          : cd2d7ca5-2d4c-5f93-94d0-8cee0662c71c
MeterName        : E20 v4
MeterRates       : @{0=1,281816}
MeterRegion      : AP Southeast
MeterStatus      : Active
MeterSubCategory : Ev4 Series
MeterTags        : {}
Unit             : 1 Hour

EffectiveDate    : 2019-10-28T00:00:00Z
IncludedQuantity : 0,0
MeterCategory    : Azure App Service
MeterId          : dee311f4-b516-4e64-9158-df610306a632
MeterName        : S1
MeterRates       : @{0=0,0861}
MeterRegion      : NO East
MeterStatus      : Active
MeterSubCategory : Standard Plan
MeterTags        : {}
Unit             : 1 Hour

Información más que suficiente ¿verdad?

Llamando a la Azure RateCard API

Para llamar a esta API necesitamos una suscripción de Azure. En mi caso me ha servido una suscripción de My VisualStudio. Como herramienta voy a utilizar Azure CLI.

En primer lugar necesitamos registrar en Azure el proveedor Microsoft.Commerce:

$ az provider register --namespace Microsoft.Commerce

Segundo, tendremos que crear un rol personalizado que utilizaremos para la cuenta del servicio que crearemos posteriormente. Crearemos un nuevo archivo que por ejemplo se llame RateCardRole.json y que contenga lo siguiente:

{
    "Name": "RateCard Reader",
    "IsCustom": true,
    "Description": "RateCard API read role",
    "Actions": [
        "Microsoft.Commerce/RateCard/read"
    ],
    "AssignableScopes": [
        "/subscriptions/TU_SUBSCRIPTION_ID"
    ]
}

Evidentemente, cambia TU_SUBSCRIPTION_ID por el que corresponda a tu suscripción. Guarda el archivo y crea el rol mediante:

$ az role definition create --verbose --role-definition "@RateCardRole.json"

Acto seguido creamos el Azure AD Service Principal:

$ az ad sp create-for-rbac --name "RateCard API" --role "RateCard Reader" --scopes /subscriptions/TU_SUBSCRIPTION_ID

Finalmente necesitamos utilizar el Azure AD Service Principal creado para obtener el token que nos permite hacer llamadas a la API REST de Azure. Voy a utilizar PowerShell para el ejemplo:

$loginUri = [string]::Format('https://login.microsoftonline.com/{0}/oauth2/token?api-version=1.0', $tenantId)

$body = @{
    grant_type    = "client_credentials"
    resource      = "https://management.core.windows.net/"
    client_id     = $clientId
    client_secret = $clientPassword
}

$loginResponse = Invoke-RestMethod $loginUri -Method Post -Body $body
$authorization = [string]::Format('{0} {1}', $loginResponse.token_type, $loginResponse.access_token)

La variable $authorization conforma la cabecera HTTP que tenemos que adjuntar para hacer llamadas.

Talk is cheap, show me the code

Así que dicho todo esto... ¿Cómo quedaría finalmente el código de llamada API para obtener la preciada RateCard? Este es un ejemplo en PowerShell, pero que podría desarrollarse en cualquier otro lenguaje:

$tenantId = "MINOMBREDETENANT.onmicrosoft.com"
$clientId = "CLIENT-ID-DEL-AZURE-AD-SP"
$clientPassword = "SECRET-DEL-AZURE-AD-SP"
$subscriptionId = "MI-SUBSCRIPTION-ID"
$offerDurableId = "MS-AZR-0003p" # Pay As You Go
$currency = "EUR"
$locale = "en-US"
$regionInfo = "ES"
$rateCardFileName = 'ratecard.xml'
$loginUri = [string]::Format('https://login.microsoftonline.com/{0}/oauth2/token?api-version=1.0', $tenantId)

$body = @{
    grant_type    = "client_credentials"
    resource      = "https://management.core.windows.net/"
    client_id     = $clientId
    client_secret = $clientPassword
}

Write-Host "Authenticating..." 
$loginResponse = Invoke-RestMethod $loginUri -Method Post -Body $body
$authorization = [string]::Format('{0} {1}', $loginResponse.token_type, $loginResponse.access_token)

$headers = @{
    authorization = $authorization
}

$rateCardFilter = [string]::Format("OfferDurableId eq '{0}' and Currency eq '{1}' and Locale eq '{2}' and RegionInfo eq '{3}'", `
            $offerDurableId, $currency, $locale, $regionInfo)
$rateCardUri = [string]::Format("https://management.azure.com/subscriptions/{0}/providers/Microsoft.Commerce/RateCard?api-version=2016-08-31-preview&`$filter={1}", `
            $subscriptionId, $rateCardFilter)

Write-Host "Querying Rate Card API..."
$rateCardResponse = Invoke-RestMethod $rateCardUri -Headers $headers -ContentType 'application/json'
Export-Clixml -InputObject $rateCardResponse -Path $rateCardFileName -Encoding utf8 -Force

Toda la información de pricing de Azure se almacena en la variable $rateCardResponse, que podemos guardar en un archivo mediante Export-Clixml para que en ejecuciones sucesivas no necesitemos llamar de nuevo a la API, que aunque responde rápido, tarda unos 10 segundos en entregarnos el listado de precios completo.

Con estos datos en memoria, ya podéis hacer todo lo que se os ocurra con ellos.

Happy pricing!