0
votes

I have a Python function using the preview option of sending custom metrics to Azure using the REST API https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-store-custom-rest-api, previously this was a C# function where authorisation and getting a bearer token was handled automagically by:

var azureServiceTokenProvider = new AzureServiceTokenProvider();
string bearerToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://monitoring.azure.com/").ConfigureAwait(false);

This worked in VS Code using the logged in user and in Azure when a Managed Identity was assigned to the Function.

I needed to convert this to Python but so far the best (working) I've been able to come up with is:

import logging, requests, os, adal
import azure.functions as func

def main(req: func.HttpRequest) -> func.HttpResponse:
    regional_monitoring_url = "https://eastus.monitoring.azure.com"
    monitored_resource_id = os.environ['RESOURCE_ID']
    full_endpoint = f"{regional_monitoring_url}{monitored_resource_id}/metrics"

    tenant_id = os.environ['AZURE_TENANT_ID']
    context = adal.AuthenticationContext(f'https://login.microsoftonline.com/{tenant_id}')
    token = context.acquire_token_with_client_credentials("https://monitoring.azure.com/", os.environ['AZURE_CLIENT_ID'], os.environ['AZURE_CLIENT_SECRET']    )
    bearer_token = token['accessToken']

    json = req.get_json()
    headers = {"Authorization": 'Bearer ' + bearer_token}
    result = requests.post(url = full_endpoint, headers = headers, json = json)

    return func.HttpResponse(f"Done - {result.status_code} {result.text}", status_code=200)

This obviously relies on me creating a Service Principal with the relevant permissions. I'm trying to work out how to use the automatic Managed Identity authorisation that the C# libraries have.

I know ADAL should be replaced by MSAL but I can't work out how/if that automagically handles Managed Identities so I tried azure-identity:

from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
token = credential.get_token("https://monitoring.azure.com/.default")
bearer_token = token.token

This gets me a token but because it requires a scope rather than a resource, which means adding .default to the resource URL, when I send the bearer token to the monitoring endpoint it complains the resource doesn't match and must be exactly "https://monitoring.azure.com/"

Is this just not currently possible or am I missing something with either azure-identity or the MSAL Python modules?

1

1 Answers

1
votes

According to my research, when werequest an Azure AD token to emit custom metrics, ensure that the audience the token is requested for is https://monitoring.azure.com/. For more details, please refer to here. So we should update scope as https://monitoring.azure.com//.default enter image description here

For example

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    credential = DefaultAzureCredential()
    token = credential.get_token("https://monitoring.azure.com//.default")
    bearer_token = token.token
    #full_endpoint=""
    json = req.get_json()
    headers = {"Authorization": 'Bearer ' + bearer_token}
    #result = requests.post(url = full_endpoint, headers = headers, json = json)
    return func.HttpResponse(f"Done - {bearer_token}", status_code=200)

enter image description here enter image description here