I have created an application following the steps in Microsoft's documentation:
FabricAPIApp
)APP_ID
.CLIENT_SECRET
) securely.1- In Microsoft Fabric, go to the workspace and give to the user who going to use the API the role contributor.
%pip install msal
import msal
CLIENT_ID = ""
CLIENT_SECRET= ""
AUTHORITY = "https://login.microsoftonline.com/<TENANT_ID>"
SCOPES = ["https://api.fabric.microsoft.com/.default"]
app = msal.ConfidentialClientApplication(
CLIENT_ID,
authority=AUTHORITY,
client_credential=CLIENT_SECRET
)
result = app.acquire_token_for_client(scopes=SCOPES)
if "access_token" in result:
access_token = result["access_token"]
print("[+] Token obtained.")
else:
print("[-] Error getting token:", result.get("error_description"))
exit()
The last step I'm trying is making a GET request to view my workspaces, but this comes up:
[+] Token obtained. [+] Response: {'value': []}
If I try other requests with different endpoints, I get a 401 error. I’ve been investigating, and this documentation https://learn.microsoft.com/en-us/rest/api/fabric/core/workspaces/get-workspace?tabs=HTTP says that my app needs to have this:
Required Delegated Scopes Workspace.Read.All or Workspace.ReadWrite.All
But I don’t know where to grant it. I tried from the Azure portal, in API permissions, but I can’t find anything related to Fabric. Does anyone have an idea?
I tried to search fabric service on https://aad.portal.azure.com/ > dashboard > my api > api permissions
The GET request:
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
url = "https://api.fabric.microsoft.com/v1/workspaces"
response = requests.get(url, headers=headers)
if response.status_code == 200:
print("[+] Response:", response.json())
else:
print(f"[-] Error: {response.status_code} - {response.text}")
This is how the permissions panel looks, and I don't see "fabric" here --> https://www.youtube.com/watch?v=4NByBBdy11I
Registered Single Tenant Microsoft Entra ID application:
NOTE: As mentioned in this MS-Document, To list the workspaces, Requires Delegated Scopes only Workspace.Read.All
or Workspace.ReadWrite.All
delegated type permissions works with user-interaction flow and if you want to use client credentials flow use application type permissions.
For listing the workspaces from your Microsoft Fabric, Added delegated type Workspace.Read.All
API permission from Power BI Services and granted admin Consent like below:
Using delegated type, authorization_code flow which requires user-interaction. To get code
, I ran below authorization request in browser:
https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/authorize?
client_id=<client_id>
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=https://analysis.windows.net/powerbi/api/.default
&state=12345
Use below Modified Python Script for using authorization_code flow:
import msal
import requests
import json
# Configuration
CLIENT_ID = "<application-id>"
CLIENT_SECRET = "<client-secret>"
AUTHORITY = "https://login.microsoftonline.com/<your-tenant-id>"
REDIRECT_URI = "https://jwt.ms" # Use redirect URI you configured in App Registration
SCOPE = ["https://api.fabric.microsoft.com/.default"] # Scopes for Microsoft Fabric API
# Initialize the MSAL ConfidentialClientApplication
app = msal.ConfidentialClientApplication(
CLIENT_ID,
authority=AUTHORITY,
client_credential=CLIENT_SECRET
)
authorization_code = input("Enter the authorization code you received after consent: ")
# Step 2: Exchange Authorization Code for Access Token
result = app.acquire_token_by_authorization_code(
authorization_code,
SCOPE,
redirect_uri=REDIRECT_URI
)
if "access_token" in result:
print("Access token obtained successfully.")
access_token = result["access_token"]
# Step 3: Make an API request to list all workspaces
api_url = "https://api.fabric.microsoft.com/v1/workspaces" # Adjust endpoint to list all workspaces
headers = {
"Authorization": f"Bearer {access_token}"
}
response = requests.get(api_url, headers=headers)
if response.status_code == 200:
# Parse the JSON response
workspaces = response.json()
print("List of Workspaces:")
print(json.dumps(workspaces, indent=2)) # Pretty print the list of workspaces
else:
print(f"Failed to retrieve workspaces. Status code: {response.status_code}")
print("Response:", response.text)
else:
print("Error acquiring token.")
print(result.get("error_description"))
After running this code, you will get prompt like Enter the authorization code you received after consent:
Paste the authorization_code which you generated using above parameters from browser.
Response:
If you want to use application type permission needs to use client credentials flow which doesnot requires user-interaction.
Ensure to add your Microsoft Entra ID app registration to Power BI workspace with at least Reader access.
Go to Power BI Portal -> Your Workspace -> Click on ... -> Manage access -> +Add people or groups -> Search for Service Principal with name -> Reader
I added Admin access to my app registration:
Also check, have you allows access for service principals to call Power BI API by enabling below option in Tenant Settings of Admin Portal:
Use below modified Python code for listing the Workspace using Client Credential flow:
import requests
import msal
TENANT_ID = "<tenant-id>"
CLIENT_ID = "<application-id>"
CLIENT_SECRET = "<client-secret>"
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
SCOPES = ["https://analysis.windows.net/powerbi/api/.default"]
app = msal.ConfidentialClientApplication(
CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET
)
token_response = app.acquire_token_for_client(scopes=SCOPES)
if "access_token" in token_response:
access_token = token_response["access_token"]
print(" Token obtained.")
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
# Power BI API for listing workspaces
url = "https://api.fabric.microsoft.com/v1/workspaces"
response = requests.get(url, headers=headers)
if response.status_code == 200:
print(" Workspaces:", response.json())
else:
print(f" Error: {response.status_code} - {response.text}")
else:
print(" Error getting token:", token_response.get("error_description"))
Response:
Reference: