Power BI Authentication with Service Principal
Service principal authentication relies on the Power BI Tenant ID and the Azure Active Directory application ID that you provide in the lineage harvester configuration file. The password you need to access Power BI is the client secret key of the Azure Active Directory application.
Requirement
Enable the Admin API Settings (access read-only admin APIs) for the service principal in the Admin Portal of Power BI Service : Enable service principal authentication for read-only admin APIs - Microsoft Fabric | Microsoft Learn
Make sur to add the latter in a security group.
...
Authentication & Authorization
We need to keep the following values to get the authorization from the Power BI Service : tenant id, client id and client secret associated with the service principal.
Code Block | ||
---|---|---|
| ||
public static IContextServicePowerBI GetContextServicesFromPowerBI(string tenantId, string clientId, string clientSecret) { var credential = new ClientSecretCredential(tenantId, clientId, clientSecret); // A service principal var accessToken = credential.GetToken(new Azure.Core.TokenRequestContext(new[] { "https://analysis.windows.net/powerbi/api/.default" })); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Token); return new ContextServicePowerBI("", "", accessToken.Token, "Url", client, client.DefaultRequestHeaders.Authorization, "", "", ""); } |
Info |
---|
The service principal is not an administrator of PBI Platform and then, it will not be able to use all the REST APIs. It will access only to the components deployed in a workspace because it has access to this workspace (group) and not to all the external components of the platform (for example, handling and accessing the log generated by the platform…). |
Power BI REST APIs
Creation of a repository layer for the APIs
Code Block | ||
---|---|---|
| ||
public interface IRepositoryAPI<T> where T : class { T? GetById(string id); T? GetById(string id, string id2); IEnumerable<T>? GetAll(); IEnumerable<T>? GetAll(string id); IEnumerable<T>? GetAll(string id, string id2); } |
Creation of the PBI objects for the T object
...
PBI service return a returns data in format JSON. So, we will have to deserialize the data.
Code Block | ||
---|---|---|
| ||
public class RootGroup { [JsonPropertyName("@odata.context")] public string? OdataContext { get; set; } public List<Group>? value { get; set; } } public class Group { public bool isReadOnly { get; set; } public bool isOnDedicatedCapacity { get; set; } public Guid capacityId { get; set; } public string? defaultDatasetStorageFormat { get; set; } public string? type { get; set; } public Guid id { get; set; } public string? name { get; set; } } |
Creation of the abstract class for the repository layer
Code Block | ||
---|---|---|
| ||
public abstract class PowerBiRepository<T> : IRepositoryAPI<T> where T : class { protected IContextServicePowerBI PowerBiService { get; set; } protected PowerBiRepository(IContextServicePowerBI powerBiService) { PowerBiService = powerBiService; } public virtual IEnumerable<T>? GetAll() { throw new NotImplementedException(); } //etc... } |
Creation of the Proxies FormProxy : Once we got the values of the Table “systemform” related to the form in which the PBI report is embedded. The data of [SystemForm] type will be mapped into an object [FormProxy[]] of type array.repositories layer
Here’s the example for the group or workspace repository (workspace contains the report).
Code Block | ||
---|---|---|
| ||
varpublic systemFormclass = serviceProvider.GetRequiredService<DataverseRepository<DataverseModel.SystemForm>>().FindAll(qe); GroupRepository : PowerBiRepository<Group> { public GroupRepository(IContextServicePowerBI context) : base(context) var formProxies = systemForm{ } /// <summary> /// Get all groups / workspaces in PBI service .Select<Entity, FormProxy>((Func<Entity, FormProxy>)(f => new FormProxy(f))) to which the service principal has access. /// </summary> /// <returns></returns> public override .OrderBy<FormProxy, string>((Func<FormProxy, string>)(f => f.ToString()))IEnumerable<Group>? GetAll() { var content .ToArray<FormProxy>();//convert entities into array of proxy |
TabProxy : Tab is contained in a form in the dataverse. This object will contain the form Id.
Code Block | ||
---|---|---|
| ||
public static List<TabProxy> LoadTabs(string formXML, string formid) { if (formXML.Equals(string.Empty)) = PowerBiService.Client.GetAsync($"{PowerBiService.PowerBiApiUrl}v1.0/myorg/groups").Result.Content.ReadAsStringAsync(); return null; FormModel formModel; var tabProxiescontenu = new List<TabProxy>content.Result.ToString(); using (StringReaderRootGroup stringReadergroups = new StringReaderRootGroup(formXML)) ; { formModel = (FormModel)new XmlSerializer(typeof(FormModel)).Deserialize((TextReader)stringReader); groups.value = new List<PowerBiModel.Group>(); } if (formModelcontenu != null && formModel.Tabs.Count > 0) { foreach (FormTab tab in formModel.Tabs) { tabProxies.Add(new TabProxy() string.Empty) { groups = JsonSerializer.Deserialize<RootGroup>(contenu); } { Text = tab.Labels.FirstOrDefault<FormTabLabel>()?.Description, Value = (object)tab.Id, Name = tab.Name != null ? tab.Name : string.Empty, FormId = formid }return groups?.value?.ToList(); } } return tabProxies; } |
...