pythonazureazure-ad-msalazure-rbac

Cannot get Azure subscriptions using `msal` and requests in python through REST API


I can get subsriptions using token captured from browser: REST API result

Then I switch to Python and msal. I use the following code:

import msal
import requests
import sys
import json

data = json.load(open("parameters.json"))

config = {
    "authority": f"https://login.microsoftonline.com/{data['tenant']}",
    "client_id": data["client_id"],
    "client_secret": data["client_secret"],
    "scopes": [
        "https://management.azure.com/.default",
    ]
}

app = msal.ConfidentialClientApplication(
    data["client_id"],
    authority=f"https://login.microsoftonline.com/{data['tenant']}",
    client_credential=data["client_secret"],
)

result = app.acquire_token_for_client(scopes=[
        "https://management.azure.com/.default",
    ])


if "access_token" in result:
    print("success")
else:
    print(result.get("error"))
    print(result.get("error_description"))
    print(result.get("correlation_id"))
    sys.exit(-1)

headers = {
    "Authorization": f"Bearer {result['access_token']}"
}

response = requests.get(
    headers=headers, url="https://management.azure.com/subscriptions?api-version=2019-08-01")

if response.status_code == 200:
    print(response.content)
else:
    print(response.status_code)

However, I cannot get desired result:

success
b'{"value":[],"count":{"type":"Total","value":0}}'

The permissions are already granted: Permissions: user_impersonation

I tried to add "https://management.azure.com//user_impersonation" to scope, but it fails with error:

AADSTS1002012: The provided value for scope https://management.azure.com//user_impersonation is not valid. Client credential flows must have a scope value with /.default suffixed to the resource identifier (application ID URI).

I reads about the "OBO" flow, but my app isn't supposed to need user interactions.

Ask: Does I miss something in the auth flow or permissions?


Solution

  • You need to assign at least Reader role to the service principal before generating access token to call Azure Management API.

    In my case, I registered one Microsoft Entra ID application and created one secret in it as below:

    enter image description here

    Now, I assigned Reader role to the service principal under the management group as below:

    enter image description here

    When I ran your code after assigning role to service principal, I got the response successfully like this:

    import msal
    import requests
    import sys
    import json
    
    data = json.load(open("parameters.json"))
    
    config = {
        "authority": f"https://login.microsoftonline.com/{data['tenant']}",
        "client_id": data["client_id"],
        "client_secret": data["client_secret"],
        "scopes": [
            "https://management.azure.com/.default",
        ]
    }
    
    app = msal.ConfidentialClientApplication(
        data["client_id"],
        authority=f"https://login.microsoftonline.com/{data['tenant']}",
        client_credential=data["client_secret"],
    )
    
    result = app.acquire_token_for_client(scopes=[
            "https://management.azure.com/.default",
        ])
    
    
    if "access_token" in result:
        print("success")
    else:
        print(result.get("error"))
        print(result.get("error_description"))
        print(result.get("correlation_id"))
        sys.exit(-1)
    
    headers = {
        "Authorization": f"Bearer {result['access_token']}"
    }
    
    response = requests.get(
        headers=headers, url="https://management.azure.com/subscriptions?api-version=2019-08-01")
    
    if response.status_code == 200:
        print(response.content)
    else:
        print(response.status_code)
    

    Response:

    enter image description here