Service Principal in Microsoft Fabric
In a recent update the Microsoft team introduced service principal support for Fabric APIs.
This means is that now its possible to access Fabric resources using service principals. In one of my previous blog posts, I discussed how to access Fabric resources through REST APIs which you can check out here.
The key advantage of using service principals is that unlike user accounts they are not linked to any individual account reducing the risk of credentials being compromised.
In this article, we'll walk through how to set up a service principal in your Fabric tenant and use it to authenticate access to Fabric resources.
The Setup
To get started ensure you are the Admin of your fabric tenant. Next, under the tenant Settings» Admin Portal
you will have to enable Service principals can use Fabric API’s
setting under Tenant settings » Developer settings
After that’s done, you need to register the application at https://entra.microsoft.com
Once logged in, navigate to Applications >>App registrations >> New registration
and register an application
I have registered the application under the name Fabric API Service Principle
. We would required Client ID
and Tenant ID
values to reference in the code from the registered app.
Also Client Secrets
will be required.
Now that the feature is enabled in the next step we would grant access to the registered application to connect to the Workspaces. In this case we would assign contributor access to the workspace.
In your Fabric tenant, under the workspace, select Manage access
and assign Contributor
access to the registered application that was created in the previous step.
The Code
Lets make a simple call to fetch details of a given workspace.
Create a new Console application and declare a bunch of variables.
private static string responsename = "";
private static string clientId = "Client Id of the Registered App";
private static string tenantId = "Tenant Id of the Registered App";
private static string clientSecret = "Client Secret of the Registered App";
private static string workspaceName = "My_Workspace"; ## To retrieve details of the workspace
private static string lakeHouse = "LakeHouse_1"; ## To retrieve the table list of the lakehouse
private static MyService service = new MyService();
private static ClientSecretCredential credential;
Create a class MyService
that invokes Get method
public class MyService
{
private static readonly HttpClient client = new HttpClient();
public HttpClient Client => client;
public async Task<string> GetAsync(string url, AccessToken Token)
{
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.Token);
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
Create a method that returns the WorkSpaceId
based on the value set in the WorkspaceName
variable.
static string GetWorkSpaceId(string responsename)
{
JObject jsonObject = JObject.Parse(responsename);
int i = 0; string workspaceId = "";
for (i = 0; i < jsonObject["value"].Count(); i++)
{
if (jsonObject["value"][i]["displayName"].ToString() == workspaceName)
{
workspaceId = jsonObject["value"][0]["id"].ToString();
}
}
return workspaceId;
}
Create a method to return a Response
and set its AccessToken
.
static async Task ReturnResponse(string baseUrl)
{
credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
AccessToken token = await credential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes));
responsename = await service.GetAsync(baseUrl, token);
}
Call to the above method :
ReturnResponse("https://api.fabric.microsoft.com/v1/workspaces").GetAwaiter().GetResult();
ReturnResponse($"https://api.fabric.microsoft.com/v1/workspaces/{GetWorkSpaceId(responsename)}").GetAwaiter().GetResult();
var jsonObject = JsonSerializer.Deserialize<dynamic>(responsename);
string prettyJson = JsonSerializer.Serialize(jsonObject, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(prettyJson);
this will produce the following output
If the access for the service principal is removed from the workspace, the code errors out with an unauthorized error message.
Now that we saw on ways to leverage service principal to make REST API calls, lets check how to get a list of all the tables from a Lakehouse through managed identity.
The following namespace would be required to reference the lakehouse.
using Azure.Storage.Files.DataLake
string accountUrl = "https://onelake.dfs.fabric.microsoft.com";
var fileSystemClient = new DataLakeFileSystemClient(new Uri($"{accountUrl}/{workspaceName}"), credential);
string path = $"{lakeHouse}.Lakehouse/Tables/";
var paths = fileSystemClient.GetPaths(path, recursive: false);
foreach (var p in paths)
{
Console.WriteLine(p.Name);
}
The above code print outs the physical paths of the underlying table.
It print outs lk_table_1
as that’s the only table in the lakehouse.
To access OneLake data use the following paths in this piece of code
string path = $"{lakeHouse}.Lakehouse/Tables/";
/<lakehouse name>.Lakehouse/Tables/
for the tables in your Lakehouse/<lakehouse name>.Lakehouse/Files/
for the files in your Lakehouse/<lakehouse name>.Warehouse/Tables/
for the tables in your Warehouse
The complete code :
using Azure.Core;
using Azure.Identity;
using Azure.Storage.Files.DataLake;
using Microsoft.Identity.Client;
using Newtonsoft.Json.Linq;
using System.Net.Http.Headers;
using System.Text.Json;
namespace Fabric_Service_Principal
{
internal class Program
{
private static string responsename = "";
private static string clientId = "Client Id of the Registered App";
private static string tenantId = "Tenant Id of the Registered App";
private static string clientSecret = "Client Secret of the Registered App";
private static string workspaceName = "Workspace Name"; ## To retrieve details of the workspace
private static string lakeHouse = "LakeHouse Name"; ## To retrieve the table list of the lakehouse
private static MyService service = new MyService();
private static ClientSecretCredential credential;
static async Task Main(string[] args)
{
ReturnResponse("https://api.fabric.microsoft.com/v1/workspaces").GetAwaiter().GetResult();
ReturnResponse($"https://api.fabric.microsoft.com/v1/workspaces/{GetWorkSpaceId(responsename)}").GetAwaiter().GetResult();
var jsonObject = JsonSerializer.Deserialize<dynamic>(responsename);
string prettyJson = JsonSerializer.Serialize(jsonObject, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(prettyJson);
string accountUrl = "https://onelake.dfs.fabric.microsoft.com";
var fileSystemClient = new DataLakeFileSystemClient(new Uri($"{accountUrl}/{workspaceName}"), credential);
string path = $"{lakeHouse}.Lakehouse/Tables/";
var paths = fileSystemClient.GetPaths(path, recursive: false);
foreach (var p in paths)
{
Console.WriteLine(p.Name );
}
}
static async Task ReturnResponse(string baseUrl)
{
credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
AccessToken token = await credential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes));
responsename = await service.GetAsync(baseUrl, token);
}
static string GetWorkSpaceId(string responsename)
{
JObject jsonObject = JObject.Parse(responsename);
int i = 0; string workspaceId = "";
for (i = 0; i < jsonObject["value"].Count(); i++)
{
if (jsonObject["value"][i]["displayName"].ToString() == workspaceName)
{
workspaceId = jsonObject["value"][0]["id"].ToString();
}
}
return workspaceId;
}
public class MyService
{
private static readonly HttpClient client = new HttpClient();
public HttpClient Client => client;
public async Task<string> GetAsync(string url, AccessToken Token)
{
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.Token);
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
}
Conclusion :
To sum up, with the introduction of service principal support for Fabric APIs, it provides a safer and more effective means of gaining access to Fabric resources. Service principals improve overall security and lower the danger of unwanted access by doing away with the requirement for user-specific passwords. You can now quickly set up and verify your service principal in Fabric by following the instructions in this article, which guarantees secure access to Fabric resources.
Subscribe to my newsletter
Read articles from Sachin Nandanwar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by