Forum Discussion
POC Service Principal Export and Import a DataFlow
- Anonymous4 years ago
Ok this took lots of GoogleFU. Basically I had to read and understand all of the materials related to service principals, application registrations, API scopes, and then do a test once I thought I had it figured out. If I did not have my own Visual Studio Pro account setting this up would not be possible.
So in my POC I wanted to export/import dataflows using a service principal.
- Is this possible? No not with just a service principal. It MIGHT be possible with a manged identity but not something I need to test at the moment.
- Is this possible using a the GitHub - PowerBiDevCamp/App-Owns-Data-Starter-Kit: App-Owns-Data Starter Kit is a developer sample demonstrating common design techniques used in App-Owns-Data embedding. YES because that application forces you to login to AAD.
- Follow the normal setup instructions and ensure you have put the Power BI API permissions on the application registration that you need. I put all of them on for now.
- You have to get an access token for the logged in user see the HomeController.cs file.
- You have to pass that access token to the PowerBIClient CTOR
- Then your export and import calls will work.
I guess figuring out if this will work with a managed identity is next.
I included code for two files you can play with if you want to try this to see that it works
HomeController.cs
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using AppOwnsDataShared.Models; using AppOwnsDataShared.Services; using AppOwnsDataAdmin.Models; using AppOwnsDataAdmin.Services; using Microsoft.Identity.Web; namespace AppOwnsDataAdmin.Controllers { [Authorize] public class HomeController : Controller { private PowerBiServiceApi powerBiServiceApi; private AppOwnsDataDBService AppOwnsDataDBService; ITokenAcquisition _tokenAcquisition; public HomeController(PowerBiServiceApi powerBiServiceApi, AppOwnsDataDBService AppOwnsDataDBService, ITokenAcquisition tokenAcquisition) { this.powerBiServiceApi = powerBiServiceApi; this.AppOwnsDataDBService = AppOwnsDataDBService; this._tokenAcquisition = tokenAcquisition; } [AllowAnonymous] public IActionResult Index() { return View(); } public IActionResult Tenants() { var model = AppOwnsDataDBService.GetTenants(); return View(model); } public IActionResult Tenant(string Name) { var model = AppOwnsDataDBService.GetTenant(Name); var modelWithDetails = powerBiServiceApi.GetTenantDetails(model); return View(modelWithDetails); } public class OnboardTenantModel { public string TenantName { get; set; } public string SuggestedDatabase { get; set; } public List<SelectListItem> DatabaseOptions { get; set; } public string SuggestedAppIdentity { get; set; } public List<SelectListItem> AppIdentityOptions { get; set; } } public IActionResult OnboardTenant() { var model = new OnboardTenantModel { TenantName = this.AppOwnsDataDBService.GetNextTenantName(), SuggestedDatabase = "WingtipSales", DatabaseOptions = new List<SelectListItem> { new SelectListItem{ Text="AcmeCorpSales", Value="AcmeCorpSales" }, new SelectListItem{ Text="ContosoSales", Value="ContosoSales" }, new SelectListItem{ Text="MegaCorpSales", Value="MegaCorpSales" } } }; return View(model); } [HttpPost] [AuthorizeForScopes(Scopes = new[] { "https://analysis.windows.net/powerbi/api/.default" })] public IActionResult OnboardTenant(string TenantName, string DatabaseServer, string DatabaseName, string DatabaseUserName, string DatabaseUserPassword) { var accessToken = _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "https://analysis.windows.net/powerbi/api/.default" }).Result; var tenant = new PowerBiTenant { Name = TenantName, DatabaseServer = DatabaseServer, DatabaseName = DatabaseName, DatabaseUserName = DatabaseUserName, DatabaseUserPassword = DatabaseUserPassword, }; tenant = this.powerBiServiceApi.OnboardNewTenant(tenant, accessToken); this.AppOwnsDataDBService.OnboardNewTenant(tenant); return RedirectToAction("Tenants"); } public IActionResult DeleteTenant(string TenantName) { var tenant = this.AppOwnsDataDBService.GetTenant(TenantName); this.powerBiServiceApi.DeleteWorkspace(tenant); this.AppOwnsDataDBService.DeleteTenant(tenant); return RedirectToAction("Tenants"); } public IActionResult Embed(string AppIdentity, string Tenant) { var viewModel = this.powerBiServiceApi.GetReportEmbeddingData(AppIdentity, Tenant).Result; return View(viewModel); } public IActionResult Users() { var model = AppOwnsDataDBService.GetUsers(); return View(model); } public IActionResult GetUser(string LoginId) { var model = AppOwnsDataDBService.GetUser(LoginId); return View(model); } public class EditUserModel { public User User { get; set; } public List<SelectListItem> TenantOptions { get; set; } } public IActionResult EditUser(string LoginId) { var model = new EditUserModel{ User = AppOwnsDataDBService.GetUser(LoginId), TenantOptions = this.AppOwnsDataDBService.GetTenants().Select(tenant => new SelectListItem { Text = tenant.Name, Value = tenant.Name }).ToList(), }; return View(model); } [HttpPost] public IActionResult EditUser(User user) { var model = AppOwnsDataDBService.UpdateUser(user); return RedirectToAction("Users"); } public class CreateUserModel { public List<SelectListItem> TenantOptions { get; set; } } public IActionResult CreateUser() { var model = new CreateUserModel { TenantOptions = this.AppOwnsDataDBService.GetTenants().Select(tenant => new SelectListItem { Text = tenant.Name, Value = tenant.Name }).ToList(), }; return View(model); } [HttpPost] public IActionResult CreateUser(User user) { var model = AppOwnsDataDBService.CreateUser(user); return RedirectToAction("Users"); } public IActionResult DeleteUser(string LoginId) { var user = this.AppOwnsDataDBService.GetUser(LoginId); this.AppOwnsDataDBService.DeleteUser(user); return RedirectToAction("Users"); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }PowerBiServiceApi.cs
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using Newtonsoft.Json; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.PowerBI.Api; using Microsoft.PowerBI.Api.Models; using Microsoft.PowerBI.Api.Models.Credentials; using Microsoft.Rest; using AppOwnsDataShared.Models; using Microsoft.Identity.Web; using AppOwnsDataShared.Services; using Newtonsoft.Json.Linq; namespace AppOwnsDataAdmin.Services { public class EmbeddedReportViewModel { public string ReportId; public string Name; public string EmbedUrl; public string Token; public string TenantName; } public class PowerBiTenantDetails : PowerBiTenant { public IList<Report> Reports { get; set; } public IList<Dataset> Datasets { get; set; } public IList<GroupUser> Members { get; set; } } public class PowerBiServiceApi { private readonly AppOwnsDataDBService AppOwnsDataDBService; private readonly IConfiguration Configuration; private readonly IWebHostEnvironment Env; private ITokenAcquisition tokenAcquisition { get; } private string urlPowerBiServiceApiRoot { get; } public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition, AppOwnsDataDBService AppOwnsDataDBService, IWebHostEnvironment env) { this.Configuration = configuration; this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"]; this.tokenAcquisition = tokenAcquisition; this.AppOwnsDataDBService = AppOwnsDataDBService; this.Env = env; } public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default"; public string GetAccessToken() { return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result; } public PowerBIClient GetPowerBiClient() { var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer"); return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); } public async Task<EmbeddedReportViewModel> GetReport(Guid WorkspaceId, Guid ReportId) { PowerBIClient pbiClient = GetPowerBiClient(); // call to Power BI Service API to get embedding data var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId); // generate read-only embed token for the report var datasetId = report.DatasetId; var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId); var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest); var embedToken = embedTokenResponse.Token; // return report embedding data to caller return new EmbeddedReportViewModel { ReportId = report.Id.ToString(), EmbedUrl = report.EmbedUrl, Name = report.Name, Token = embedToken }; } public Dataset GetDataset(PowerBIClient pbiClient, Guid WorkspaceId, string DatasetName) { var datasets = pbiClient.Datasets.GetDatasetsInGroup(WorkspaceId).Value; foreach (var dataset in datasets) { if (dataset.Name.Equals(DatasetName)) { return dataset; } } return null; } public async Task<IList<Group>> GetTenantWorkspaces(PowerBIClient pbiClient) { var workspaces = (await pbiClient.Groups.GetGroupsAsync()).Value; return workspaces; } public PowerBiTenant OnboardNewTenant(PowerBiTenant tenant,string accessToken) { PowerBIClient pbiClient = this.GetPowerBiClient(); // create new app workspace GroupCreationRequest request = new GroupCreationRequest(tenant.Name); Group workspace = pbiClient.Groups.CreateGroup(request); tenant.WorkspaceId = workspace.Id.ToString(); tenant.WorkspaceUrl = "https://app.powerbi.com/groups/" + workspace.Id.ToString() + "/"; // add user as new workspace admin to make demoing easier string adminUser = Configuration["DemoSettings:AdminUser"]; if (!string.IsNullOrEmpty(adminUser)) { pbiClient.Groups.AddGroupUser(workspace.Id, new GroupUser { EmailAddress = adminUser, GroupUserAccessRight = "Admin" }); } // upload sample PBIX file #1 string pbixPath = this.Env.WebRootPath + @"/PBIX/SalesReportTemplate.pbix"; string importName = "Sales"; PublishPBIX(pbiClient, workspace.Id, pbixPath, importName); Dataset dataset = GetDataset(pbiClient, workspace.Id, importName); UpdateMashupParametersRequest req = new UpdateMashupParametersRequest(new List<UpdateMashupParameterDetails>() { new UpdateMashupParameterDetails { Name = "DatabaseServer", NewValue = tenant.DatabaseServer }, new UpdateMashupParameterDetails { Name = "DatabaseName", NewValue = tenant.DatabaseName } }); pbiClient.Datasets.UpdateParametersInGroup(workspace.Id, dataset.Id, req); PatchSqlDatasourceCredentials(pbiClient, workspace.Id, dataset.Id, tenant.DatabaseUserName, tenant.DatabaseUserPassword); pbiClient.Datasets.RefreshDatasetInGroup(workspace.Id, dataset.Id); try { string dataflowId = "67ff3929-25f7-434f-95f1-0cb4abe12d92"; ImportExportDataFlow(new Guid(dataflowId), workspace.Name, workspace.Id,accessToken); } catch (Exception ex) { System.Diagnostics.Debugger.Break(); } return tenant; } void ImportExportDataFlow(Guid dataFlowId, string name,Guid workflowId, string accessToken) { //string[] scopes = new string[] { "https://analysis.windows.net/powerbi/api/.default" }; //string accessToken = tokenAcquisition.GetAccessTokenForUserAsync(scopes).Result; var tokenCredentials = new TokenCredentials(accessToken, "Bearer"); PowerBIClient pbiClient = new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); using Stream stream = pbiClient.Dataflows.ExportDataflowAsAdmin(dataFlowId); using StreamReader reader = new StreamReader(stream); JObject data = JObject.Parse(reader.ReadToEnd()); data["name"] = name + "DF"; MemoryStream memoryStream = new MemoryStream(); StreamWriter writer = new StreamWriter(memoryStream); JsonTextWriter jsonWriter = new JsonTextWriter(writer); data.WriteTo(jsonWriter); jsonWriter.Flush(); memoryStream.Seek(0, SeekOrigin.Begin); pbiClient.Imports.PostImportWithFileInGroup(workflowId, memoryStream, "model.json"); } public PowerBiTenantDetails GetTenantDetails(PowerBiTenant tenant) { PowerBIClient pbiClient = this.GetPowerBiClient(); return new PowerBiTenantDetails { Name = tenant.Name, DatabaseName = tenant.DatabaseName, DatabaseServer = tenant.DatabaseServer, DatabaseUserName = tenant.DatabaseUserName, DatabaseUserPassword = tenant.DatabaseUserPassword, WorkspaceId = tenant.WorkspaceId, WorkspaceUrl = tenant.WorkspaceUrl, Members = pbiClient.Groups.GetGroupUsers(new Guid(tenant.WorkspaceId)).Value, Datasets = pbiClient.Datasets.GetDatasetsInGroup(new Guid(tenant.WorkspaceId)).Value, Reports = pbiClient.Reports.GetReportsInGroup(new Guid(tenant.WorkspaceId)).Value }; } public PowerBiTenant CreateAppWorkspace(PowerBIClient pbiClient, PowerBiTenant tenant) { // create new app workspace GroupCreationRequest request = new GroupCreationRequest(tenant.Name); Group workspace = pbiClient.Groups.CreateGroup(request); // add user as new workspace admin to make demoing easier string adminUser = Configuration["DemoSettings:AdminUser"]; if (!string.IsNullOrEmpty(adminUser)) { pbiClient.Groups.AddGroupUser(workspace.Id, new GroupUser { EmailAddress = adminUser, GroupUserAccessRight = "Admin" }); } tenant.WorkspaceId = workspace.Id.ToString(); return tenant; } public void DeleteWorkspace(PowerBiTenant tenant) { PowerBIClient pbiClient = this.GetPowerBiClient(); Guid workspaceIdGuid = new Guid(tenant.WorkspaceId); pbiClient.Groups.DeleteGroup(workspaceIdGuid); } public void PublishPBIX(PowerBIClient pbiClient, Guid WorkspaceId, string PbixFilePath, string ImportName) { FileStream stream = new FileStream(PbixFilePath, FileMode.Open, FileAccess.Read); var import = pbiClient.Imports.PostImportWithFileInGroup(WorkspaceId, stream, ImportName); while (import.ImportState != "Succeeded") { import = pbiClient.Imports.GetImportInGroup(WorkspaceId, import.Id); } } public void PatchSqlDatasourceCredentials(PowerBIClient pbiClient, Guid WorkspaceId, string DatasetId, string SqlUserName, string SqlUserPassword) { var datasources = (pbiClient.Datasets.GetDatasourcesInGroup(WorkspaceId, DatasetId)).Value; // find the target SQL datasource foreach (var datasource in datasources) { if (datasource.DatasourceType.ToLower() == "sql") { // get the datasourceId and the gatewayId var datasourceId = datasource.DatasourceId; var gatewayId = datasource.GatewayId; // Create UpdateDatasourceRequest to update Azure SQL datasource credentials UpdateDatasourceRequest req = new UpdateDatasourceRequest { CredentialDetails = new CredentialDetails( new BasicCredentials(SqlUserName, SqlUserPassword), PrivacyLevel.None, EncryptedConnection.NotEncrypted) }; // Execute Patch command to update Azure SQL datasource credentials pbiClient.Gateways.UpdateDatasource((Guid)gatewayId, (Guid)datasourceId, req); } }; } public async Task<EmbeddedReportViewModel> GetReportEmbeddingData(string AppIdentity, string Tenant) { PowerBIClient pbiClient = GetPowerBiClient(); var tenant = this.AppOwnsDataDBService.GetTenant(Tenant); Guid workspaceId = new Guid(tenant.WorkspaceId); var reports = (await pbiClient.Reports.GetReportsInGroupAsync(workspaceId)).Value; var report = reports.Where(report => report.Name.Equals("Sales")).First(); GenerateTokenRequest generateTokenRequestParameters = new GenerateTokenRequest(accessLevel: "View"); // call to Power BI Service API and pass GenerateTokenRequest object to generate embed token string embedToken = pbiClient.Reports.GenerateTokenInGroup(workspaceId, report.Id, generateTokenRequestParameters).Token; return new EmbeddedReportViewModel { ReportId = report.Id.ToString(), Name = report.Name, EmbedUrl = report.EmbedUrl, Token = embedToken, TenantName = Tenant }; } } }
Wonder if the Service Principal Limitations listed here Embed Power BI content in an embedded analytics application with service principal and an application secret - Power BI | Microsoft Docs are the issue. Could it be that importing a DataFrame counts as DataFlow management? If that was the case why let me export it.
- Anonymous4 years agoNot applicable
Ok this took lots of GoogleFU. Basically I had to read and understand all of the materials related to service principals, application registrations, API scopes, and then do a test once I thought I had it figured out. If I did not have my own Visual Studio Pro account setting this up would not be possible.
So in my POC I wanted to export/import dataflows using a service principal.
- Is this possible? No not with just a service principal. It MIGHT be possible with a manged identity but not something I need to test at the moment.
- Is this possible using a the GitHub - PowerBiDevCamp/App-Owns-Data-Starter-Kit: App-Owns-Data Starter Kit is a developer sample demonstrating common design techniques used in App-Owns-Data embedding. YES because that application forces you to login to AAD.
- Follow the normal setup instructions and ensure you have put the Power BI API permissions on the application registration that you need. I put all of them on for now.
- You have to get an access token for the logged in user see the HomeController.cs file.
- You have to pass that access token to the PowerBIClient CTOR
- Then your export and import calls will work.
I guess figuring out if this will work with a managed identity is next.
I included code for two files you can play with if you want to try this to see that it works
HomeController.cs
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using AppOwnsDataShared.Models; using AppOwnsDataShared.Services; using AppOwnsDataAdmin.Models; using AppOwnsDataAdmin.Services; using Microsoft.Identity.Web; namespace AppOwnsDataAdmin.Controllers { [Authorize] public class HomeController : Controller { private PowerBiServiceApi powerBiServiceApi; private AppOwnsDataDBService AppOwnsDataDBService; ITokenAcquisition _tokenAcquisition; public HomeController(PowerBiServiceApi powerBiServiceApi, AppOwnsDataDBService AppOwnsDataDBService, ITokenAcquisition tokenAcquisition) { this.powerBiServiceApi = powerBiServiceApi; this.AppOwnsDataDBService = AppOwnsDataDBService; this._tokenAcquisition = tokenAcquisition; } [AllowAnonymous] public IActionResult Index() { return View(); } public IActionResult Tenants() { var model = AppOwnsDataDBService.GetTenants(); return View(model); } public IActionResult Tenant(string Name) { var model = AppOwnsDataDBService.GetTenant(Name); var modelWithDetails = powerBiServiceApi.GetTenantDetails(model); return View(modelWithDetails); } public class OnboardTenantModel { public string TenantName { get; set; } public string SuggestedDatabase { get; set; } public List<SelectListItem> DatabaseOptions { get; set; } public string SuggestedAppIdentity { get; set; } public List<SelectListItem> AppIdentityOptions { get; set; } } public IActionResult OnboardTenant() { var model = new OnboardTenantModel { TenantName = this.AppOwnsDataDBService.GetNextTenantName(), SuggestedDatabase = "WingtipSales", DatabaseOptions = new List<SelectListItem> { new SelectListItem{ Text="AcmeCorpSales", Value="AcmeCorpSales" }, new SelectListItem{ Text="ContosoSales", Value="ContosoSales" }, new SelectListItem{ Text="MegaCorpSales", Value="MegaCorpSales" } } }; return View(model); } [HttpPost] [AuthorizeForScopes(Scopes = new[] { "https://analysis.windows.net/powerbi/api/.default" })] public IActionResult OnboardTenant(string TenantName, string DatabaseServer, string DatabaseName, string DatabaseUserName, string DatabaseUserPassword) { var accessToken = _tokenAcquisition.GetAccessTokenForUserAsync(new string[] { "https://analysis.windows.net/powerbi/api/.default" }).Result; var tenant = new PowerBiTenant { Name = TenantName, DatabaseServer = DatabaseServer, DatabaseName = DatabaseName, DatabaseUserName = DatabaseUserName, DatabaseUserPassword = DatabaseUserPassword, }; tenant = this.powerBiServiceApi.OnboardNewTenant(tenant, accessToken); this.AppOwnsDataDBService.OnboardNewTenant(tenant); return RedirectToAction("Tenants"); } public IActionResult DeleteTenant(string TenantName) { var tenant = this.AppOwnsDataDBService.GetTenant(TenantName); this.powerBiServiceApi.DeleteWorkspace(tenant); this.AppOwnsDataDBService.DeleteTenant(tenant); return RedirectToAction("Tenants"); } public IActionResult Embed(string AppIdentity, string Tenant) { var viewModel = this.powerBiServiceApi.GetReportEmbeddingData(AppIdentity, Tenant).Result; return View(viewModel); } public IActionResult Users() { var model = AppOwnsDataDBService.GetUsers(); return View(model); } public IActionResult GetUser(string LoginId) { var model = AppOwnsDataDBService.GetUser(LoginId); return View(model); } public class EditUserModel { public User User { get; set; } public List<SelectListItem> TenantOptions { get; set; } } public IActionResult EditUser(string LoginId) { var model = new EditUserModel{ User = AppOwnsDataDBService.GetUser(LoginId), TenantOptions = this.AppOwnsDataDBService.GetTenants().Select(tenant => new SelectListItem { Text = tenant.Name, Value = tenant.Name }).ToList(), }; return View(model); } [HttpPost] public IActionResult EditUser(User user) { var model = AppOwnsDataDBService.UpdateUser(user); return RedirectToAction("Users"); } public class CreateUserModel { public List<SelectListItem> TenantOptions { get; set; } } public IActionResult CreateUser() { var model = new CreateUserModel { TenantOptions = this.AppOwnsDataDBService.GetTenants().Select(tenant => new SelectListItem { Text = tenant.Name, Value = tenant.Name }).ToList(), }; return View(model); } [HttpPost] public IActionResult CreateUser(User user) { var model = AppOwnsDataDBService.CreateUser(user); return RedirectToAction("Users"); } public IActionResult DeleteUser(string LoginId) { var user = this.AppOwnsDataDBService.GetUser(LoginId); this.AppOwnsDataDBService.DeleteUser(user); return RedirectToAction("Users"); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }PowerBiServiceApi.cs
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using Newtonsoft.Json; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.PowerBI.Api; using Microsoft.PowerBI.Api.Models; using Microsoft.PowerBI.Api.Models.Credentials; using Microsoft.Rest; using AppOwnsDataShared.Models; using Microsoft.Identity.Web; using AppOwnsDataShared.Services; using Newtonsoft.Json.Linq; namespace AppOwnsDataAdmin.Services { public class EmbeddedReportViewModel { public string ReportId; public string Name; public string EmbedUrl; public string Token; public string TenantName; } public class PowerBiTenantDetails : PowerBiTenant { public IList<Report> Reports { get; set; } public IList<Dataset> Datasets { get; set; } public IList<GroupUser> Members { get; set; } } public class PowerBiServiceApi { private readonly AppOwnsDataDBService AppOwnsDataDBService; private readonly IConfiguration Configuration; private readonly IWebHostEnvironment Env; private ITokenAcquisition tokenAcquisition { get; } private string urlPowerBiServiceApiRoot { get; } public PowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition, AppOwnsDataDBService AppOwnsDataDBService, IWebHostEnvironment env) { this.Configuration = configuration; this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"]; this.tokenAcquisition = tokenAcquisition; this.AppOwnsDataDBService = AppOwnsDataDBService; this.Env = env; } public const string powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default"; public string GetAccessToken() { return this.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result; } public PowerBIClient GetPowerBiClient() { var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer"); return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); } public async Task<EmbeddedReportViewModel> GetReport(Guid WorkspaceId, Guid ReportId) { PowerBIClient pbiClient = GetPowerBiClient(); // call to Power BI Service API to get embedding data var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId); // generate read-only embed token for the report var datasetId = report.DatasetId; var tokenRequest = new GenerateTokenRequest(TokenAccessLevel.View, datasetId); var embedTokenResponse = await pbiClient.Reports.GenerateTokenAsync(WorkspaceId, ReportId, tokenRequest); var embedToken = embedTokenResponse.Token; // return report embedding data to caller return new EmbeddedReportViewModel { ReportId = report.Id.ToString(), EmbedUrl = report.EmbedUrl, Name = report.Name, Token = embedToken }; } public Dataset GetDataset(PowerBIClient pbiClient, Guid WorkspaceId, string DatasetName) { var datasets = pbiClient.Datasets.GetDatasetsInGroup(WorkspaceId).Value; foreach (var dataset in datasets) { if (dataset.Name.Equals(DatasetName)) { return dataset; } } return null; } public async Task<IList<Group>> GetTenantWorkspaces(PowerBIClient pbiClient) { var workspaces = (await pbiClient.Groups.GetGroupsAsync()).Value; return workspaces; } public PowerBiTenant OnboardNewTenant(PowerBiTenant tenant,string accessToken) { PowerBIClient pbiClient = this.GetPowerBiClient(); // create new app workspace GroupCreationRequest request = new GroupCreationRequest(tenant.Name); Group workspace = pbiClient.Groups.CreateGroup(request); tenant.WorkspaceId = workspace.Id.ToString(); tenant.WorkspaceUrl = "https://app.powerbi.com/groups/" + workspace.Id.ToString() + "/"; // add user as new workspace admin to make demoing easier string adminUser = Configuration["DemoSettings:AdminUser"]; if (!string.IsNullOrEmpty(adminUser)) { pbiClient.Groups.AddGroupUser(workspace.Id, new GroupUser { EmailAddress = adminUser, GroupUserAccessRight = "Admin" }); } // upload sample PBIX file #1 string pbixPath = this.Env.WebRootPath + @"/PBIX/SalesReportTemplate.pbix"; string importName = "Sales"; PublishPBIX(pbiClient, workspace.Id, pbixPath, importName); Dataset dataset = GetDataset(pbiClient, workspace.Id, importName); UpdateMashupParametersRequest req = new UpdateMashupParametersRequest(new List<UpdateMashupParameterDetails>() { new UpdateMashupParameterDetails { Name = "DatabaseServer", NewValue = tenant.DatabaseServer }, new UpdateMashupParameterDetails { Name = "DatabaseName", NewValue = tenant.DatabaseName } }); pbiClient.Datasets.UpdateParametersInGroup(workspace.Id, dataset.Id, req); PatchSqlDatasourceCredentials(pbiClient, workspace.Id, dataset.Id, tenant.DatabaseUserName, tenant.DatabaseUserPassword); pbiClient.Datasets.RefreshDatasetInGroup(workspace.Id, dataset.Id); try { string dataflowId = "67ff3929-25f7-434f-95f1-0cb4abe12d92"; ImportExportDataFlow(new Guid(dataflowId), workspace.Name, workspace.Id,accessToken); } catch (Exception ex) { System.Diagnostics.Debugger.Break(); } return tenant; } void ImportExportDataFlow(Guid dataFlowId, string name,Guid workflowId, string accessToken) { //string[] scopes = new string[] { "https://analysis.windows.net/powerbi/api/.default" }; //string accessToken = tokenAcquisition.GetAccessTokenForUserAsync(scopes).Result; var tokenCredentials = new TokenCredentials(accessToken, "Bearer"); PowerBIClient pbiClient = new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); using Stream stream = pbiClient.Dataflows.ExportDataflowAsAdmin(dataFlowId); using StreamReader reader = new StreamReader(stream); JObject data = JObject.Parse(reader.ReadToEnd()); data["name"] = name + "DF"; MemoryStream memoryStream = new MemoryStream(); StreamWriter writer = new StreamWriter(memoryStream); JsonTextWriter jsonWriter = new JsonTextWriter(writer); data.WriteTo(jsonWriter); jsonWriter.Flush(); memoryStream.Seek(0, SeekOrigin.Begin); pbiClient.Imports.PostImportWithFileInGroup(workflowId, memoryStream, "model.json"); } public PowerBiTenantDetails GetTenantDetails(PowerBiTenant tenant) { PowerBIClient pbiClient = this.GetPowerBiClient(); return new PowerBiTenantDetails { Name = tenant.Name, DatabaseName = tenant.DatabaseName, DatabaseServer = tenant.DatabaseServer, DatabaseUserName = tenant.DatabaseUserName, DatabaseUserPassword = tenant.DatabaseUserPassword, WorkspaceId = tenant.WorkspaceId, WorkspaceUrl = tenant.WorkspaceUrl, Members = pbiClient.Groups.GetGroupUsers(new Guid(tenant.WorkspaceId)).Value, Datasets = pbiClient.Datasets.GetDatasetsInGroup(new Guid(tenant.WorkspaceId)).Value, Reports = pbiClient.Reports.GetReportsInGroup(new Guid(tenant.WorkspaceId)).Value }; } public PowerBiTenant CreateAppWorkspace(PowerBIClient pbiClient, PowerBiTenant tenant) { // create new app workspace GroupCreationRequest request = new GroupCreationRequest(tenant.Name); Group workspace = pbiClient.Groups.CreateGroup(request); // add user as new workspace admin to make demoing easier string adminUser = Configuration["DemoSettings:AdminUser"]; if (!string.IsNullOrEmpty(adminUser)) { pbiClient.Groups.AddGroupUser(workspace.Id, new GroupUser { EmailAddress = adminUser, GroupUserAccessRight = "Admin" }); } tenant.WorkspaceId = workspace.Id.ToString(); return tenant; } public void DeleteWorkspace(PowerBiTenant tenant) { PowerBIClient pbiClient = this.GetPowerBiClient(); Guid workspaceIdGuid = new Guid(tenant.WorkspaceId); pbiClient.Groups.DeleteGroup(workspaceIdGuid); } public void PublishPBIX(PowerBIClient pbiClient, Guid WorkspaceId, string PbixFilePath, string ImportName) { FileStream stream = new FileStream(PbixFilePath, FileMode.Open, FileAccess.Read); var import = pbiClient.Imports.PostImportWithFileInGroup(WorkspaceId, stream, ImportName); while (import.ImportState != "Succeeded") { import = pbiClient.Imports.GetImportInGroup(WorkspaceId, import.Id); } } public void PatchSqlDatasourceCredentials(PowerBIClient pbiClient, Guid WorkspaceId, string DatasetId, string SqlUserName, string SqlUserPassword) { var datasources = (pbiClient.Datasets.GetDatasourcesInGroup(WorkspaceId, DatasetId)).Value; // find the target SQL datasource foreach (var datasource in datasources) { if (datasource.DatasourceType.ToLower() == "sql") { // get the datasourceId and the gatewayId var datasourceId = datasource.DatasourceId; var gatewayId = datasource.GatewayId; // Create UpdateDatasourceRequest to update Azure SQL datasource credentials UpdateDatasourceRequest req = new UpdateDatasourceRequest { CredentialDetails = new CredentialDetails( new BasicCredentials(SqlUserName, SqlUserPassword), PrivacyLevel.None, EncryptedConnection.NotEncrypted) }; // Execute Patch command to update Azure SQL datasource credentials pbiClient.Gateways.UpdateDatasource((Guid)gatewayId, (Guid)datasourceId, req); } }; } public async Task<EmbeddedReportViewModel> GetReportEmbeddingData(string AppIdentity, string Tenant) { PowerBIClient pbiClient = GetPowerBiClient(); var tenant = this.AppOwnsDataDBService.GetTenant(Tenant); Guid workspaceId = new Guid(tenant.WorkspaceId); var reports = (await pbiClient.Reports.GetReportsInGroupAsync(workspaceId)).Value; var report = reports.Where(report => report.Name.Equals("Sales")).First(); GenerateTokenRequest generateTokenRequestParameters = new GenerateTokenRequest(accessLevel: "View"); // call to Power BI Service API and pass GenerateTokenRequest object to generate embed token string embedToken = pbiClient.Reports.GenerateTokenInGroup(workspaceId, report.Id, generateTokenRequestParameters).Token; return new EmbeddedReportViewModel { ReportId = report.Id.ToString(), Name = report.Name, EmbedUrl = report.EmbedUrl, Token = embedToken, TenantName = Tenant }; } } }