I wrote a blog on using FabricRestClient from Semantic Link to call Power/Fabric REST API in Fabric notebooks. It makes it super easy. However, as I noted in the blog, it uses the calling user’s identify. It didn’t support SPN. Well, that was then and now you can use SPN to generate auth token.
All I had to do was see how Semantic Link Lab does it. I looked at the source code (Labs is open source) and copy it. So, all credit goes to Michael Kovalsky and Semantic Link Labs contributors, I am just copying what they did.
Create a service principal. Make sure you give it the permissions as required by whichever API you are calling and the API supports SPN (check the API documentation for that)
Create client secret
While optional, I very highly recommend using Azure Key Vault (hopefully one day this will be possible natively in Fabric) to store the SPN credentials. Use notebookutils to retrieve the creds from AKV.
Below, I create a class to get the token which is used in FabricRestClient .
from sempy.fabric import FabricRestClient
from azure.identity import ClientSecretCredential
key_vault = "https://sempykeyvaultsandeep.vault.azure.net/"
tenant_id = notebookutils.credentials.getSecret(key_vault , "tenantid")
client_id = notebookutils.credentials.getSecret(key_vault , "clientid")
client_secret = notebookutils.credentials.getSecret(key_vault , "secret")
class ServicePrincipalTokenProvider:
def __init__(self, tenant_id, client_id, client_secret):
self.credential = ClientSecretCredential(
tenant_id=tenant_id,
client_id=client_id,
client_secret=client_secret
)
self.scope = "https://analysis.windows.net/powerbi/api/.default"
def __call__(self):
token = self.credential.get_token(self.scope)
return token.token
token_provider = ServicePrincipalTokenProvider(
tenant_id=tenant_id,
client_id=client_id,
client_secret=client_secret
)
client = FabricRestClient(token_provider=token_provider)
response = client.get("/v1/admin/tenantsettings")
response.json()
If you have used the .admin functions in Labs, you know that it uses the context managers to call the API, i.e. you use with to localize the call. You don’t have to but that’s the recommended way to ensure isolation, security and performance. With the context manager, you use the SPN token only during that particular call and use your own identify for all other calls. Let’s implement that too:
from sempy.fabric import FabricRestClient
from azure.identity import ClientSecretCredential
import contextlib
class ServicePrincipalAuth:
def __init__(self, tenant_id, client_id, client_secret):
self.tenant_id = tenant_id
self.client_id = client_id
self.client_secret = client_secret
self.client = None
def __enter__(self):
token_provider = self._create_token_provider()
self.client = FabricRestClient(token_provider=token_provider)
return self.client
def __exit__(self, exc_type, exc_val, exc_tb):
self.client = None
def _create_token_provider(self):
credential = ClientSecretCredential(
tenant_id=self.tenant_id,
client_id=self.client_id,
client_secret=self.client_secret
)
def token_provider():
token = credential.get_token("https://analysis.windows.net/powerbi/api/.default")
return token.token
return token_provider
with ServicePrincipalAuth(tenant_id, client_id, client_secret) as client:
response = client.get("/v1/admin/workspaces")
workspacess = response.json()
What are the use cases here? Well, when you call APIs directly or indirectly using Semantic Link and Semantic Link Labs, you use your own identify. But with SPN you can execute notebooks using your identify but call APIs using the SPN as required.
💡
I would highly encourage using the functions available in Semantic Link and Semantic Link Lab wherever possible because those are optimized, handle LRO requests, pagination etc. Above is a very basic implementation. Look at SLL’s source code for details.
Thanks again to Semantic Link Labs !
Resources