# Service Principal in Microsoft Fabric

In a recent [update](https://blog.fabric.microsoft.com/en-US/blog/announcing-service-principal-support-for-fabric-apis/) 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](http://azureguru.net/microsoft-fabric-rest-apis).

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**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727627396106/ffde1c02-a735-4c81-a5fb-45816985c0e8.png align="left")

you will have to enable `Service principals can use Fabric API’s` setting under **Tenant settings » Developer settings**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727627543172/4c3bb666-e633-4a84-a081-54700cb6ce01.png align="left")

After that’s done, you need to register the application at [**https://entra.microsoft.com**](https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/quickStartType~/null/sourceType/Microsoft_AAD_IAM/appId/ad53ea30-aa6b-4232-a596-b00f31920145/objectId/1d59f9c6-36a8-43f3-a862-32e7d087ddde/isMSAApp~/false/defaultBlade/Overview/appSignInAudience/AzureADMyOrg/servicePrincipalCreated~/true)

Once logged in, navigate to **Applications &gt;&gt;App registrations &gt;&gt; New registration**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1724690897503/35730069-9cfa-438d-b3b5-d15cd01735f7.png?auto=compress,format&format=webp align="left")

and register an application

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727630681488/c56fc73a-fa00-4137-9c4c-5c42d2fe0d3d.png align="left")

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727631146087/597be54c-cb7a-415c-acaa-a0c2c6bc63da.png align="left")

Also `Client Secrets` will be required.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727631411379/b46271e7-0dc2-4802-9ab7-c5a8f2595147.png align="center")

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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727632426801/297e1778-8c77-464a-8c5f-d12611b7d0bb.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727632491145/499fc3f4-5dfa-49a7-b44c-e1c7960a6bac.png align="left")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727633363875/22f0f344-6d99-49d3-ab6f-6b6750ec19d5.png align="left")

### 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.

```csharp
 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

```csharp
 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.

```csharp
  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` .

```csharp
  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 :

```csharp
 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

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727639675332/bb33553b-350d-4208-8153-298c55ded363.png align="left")

If the access for the service principal is removed from the workspace, the code errors out with an unauthorized error message.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727643821367/4d23fa7b-2828-4a07-bac1-4bcdfd213e0a.png align="left")

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`

```csharp
 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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1727642901305/c47434c5-2d7c-472e-9755-4a43cb482705.png align="left")

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

```csharp
 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 :

```csharp
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.
