Tutorial: Embed a Power BI report in an application for your customers
Article
In this tutorial, you learn how to embed a Power BI report in a .NET 5.0 application, as part of the embed-for-your-customers (also known as an app-owns-data) solution. In an embed-for-your-customers solution, your app users don't need to sign in to Power BI or have a Power BI license.
In this tutorial, you learn how to embed:
A Power BI report.
In an embed-for-your-customers app.
By using a service principal.
By using .NET 5.0.
With the Microsoft.Identity.Web library (this library is also supported in .NET Core).
Step 1 - Configure your Microsoft Entra app and service principal
In this tutorial, you use a service principal to authenticate your web app against Microsoft Entra ID. You also need a Microsoft Entra app, which makes it possible to generate an Microsoft Entra token. By using the Microsoft Entra token, your web app can call Power BI REST APIs and embed Power BI items, such as reports, dashboards, and tiles.
Follow the service principal instructions to create a Microsoft Entra app and enable the app's service principal to work with your Power BI content.
Step 2 - Get the embedding parameter values
To embed your report, you need the following values:
Search for App registrations and select the App registrations link.
Select the Microsoft Entra app you're using for embedding your Power BI content.
Under Manage, select Certificates & secrets.
Under Client secrets, select New client secret.
In the Add a client secret pop-up window, provide a description for your application secret, select when the application secret expires, and select Add.
From the Client secrets section, copy the string in the Value column of the newly created application secret. The client secret value is your client ID.
Note
Make sure you copy the client secret value when it first appears. After navigating away from this page, the client secret will be hidden and you'll not be able to retrieve its value.
Workspace ID
To get the workspace ID GUID, follow these steps:
Sign in to Power BI service.
Open the report you want to embed.
Copy the GUID from the URL. The GUID is the number between /groups/ and /reports/.
Note
To get the workspace ID programmatically, use the Get Groups API.
Report ID
To get the report ID GUID, follow these steps:
Sign in to Power BI service.
Open the report you want to embed.
Copy the GUID from the URL. The GUID is the number between /reports/ and /ReportSection.
Turn on server-side authentication in your app by creating or modifying the files in the following table.
File
Use
Startup.cs
Initialize the Microsoft.Identity.Web authentication service
appsettings.json
Configure authentication details
PowerBiServiceApi.cs
Get the Microsoft Entra token and embedding metadata
HomeController.cs
Pass embedding data as a model to the view
Configure your startup file to support Microsoft.Identity.Web
Modify the code in Startup.cs to properly initialize the authentication service provided by Microsoft.Identity.Web.
Add the following code to your app's Startup.cs file.
Note
The code in ConfigureServices accomplishes several important things:
The call to AddMicrosoftWebAppCallsWebApi configures the Microsoft Authentication Library to acquire access tokens (Microsoft Entra tokens).
The call to AddInMemoryTokenCaches configures a token cache that the Microsoft Authentication Library uses to cache access tokens and refresh tokens behind the scenes.
The call to services.AddScoped(typeof(PowerBiServiceApi)) configures the PowerBiServiceApi class as a service class that can be added to other classes by using dependency injection.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AppOwnsData.Services;
namespaceAppOwnsData
{
publicclassStartup
{
publicStartup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.publicvoidConfigureServices(IServiceCollection services) {
services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
services.AddScoped(typeof(PowerBiServiceApi));
services.AddControllersWithViews(options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.publicvoidConfigure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You might want to change this for production scenarios. See https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
}
Create an authentication details file
In this tutorial, the appsettings.json file contains sensitive information, such as client ID and client secret. For security reasons, we don't recommend that you keep this information in the settings file. When embedding in your application, consider a more secure tool, such as Azure Key Vault, to secure sensitive information.
In your project, create a new file and name it appsettings.json.
In the preceding code, the PowerBi:ServiceRootUrl parameter is added as a custom configuration value to track the base URL to the Power BI service. When you program against the Power BI service in the Microsoft public cloud, the URL is https://api.powerbi.com/. However, the root URL for the Power BI service is different in other clouds, such as the government cloud. Therefore, the custom configuration value is stored as a project configuration value, so you can change it as needed.
Get the Microsoft Entra access token and call the Power BI service
The code in this section uses the .NET Core dependency injection pattern. When your class needs to use a service, you can add a constructor parameter for that service. The .NET Core runtime takes care of passing the service instance at run time. In this case, the constructor injects an instance of the .NET Core configuration service by using the IConfiguration parameter, which is used to retrieve the PowerBi:ServiceRootUrl configuration value from appsettings.json. The ITokenAcquisition parameter, which is named tokenAcquisition, holds a reference to the Microsoft authentication service provided by the Microsoft.Identity.Web library. The ITokenAcquisition parameter is used to acquire access tokens from Microsoft Entra ID.
The RequiredScopes field holds a string array that contains a set of delegated permissions supported by the Power BI service API. When your application calls across the network to acquire a Microsoft Entra token, it passes this set of delegated permissions so that Microsoft Entra ID can include them in the access token it returns.
In your app's project, create a new folder titled Services.
In the Services folder, create a new file titled PowerBiServiceApi.cs.
Add the following code to PowerBiServiceApi.cs.
C#
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Identity.Web;
using Microsoft.Rest;
using Microsoft.PowerBI.Api;
using Microsoft.PowerBI.Api.Models;
using Newtonsoft.Json;
namespaceAppOwnsData.Services {
// A view model class to pass the data needed to embed a single reportpublicclassEmbeddedReportViewModel {
publicstring Id;
publicstring Name;
publicstring EmbedUrl;
publicstring Token;
}
publicclassPowerBiServiceApi {
private ITokenAcquisition tokenAcquisition { get; }
privatestring urlPowerBiServiceApiRoot { get; }
publicPowerBiServiceApi(IConfiguration configuration, ITokenAcquisition tokenAcquisition) {
this.urlPowerBiServiceApiRoot = configuration["PowerBi:ServiceRootUrl"];
this.tokenAcquisition = tokenAcquisition;
}
publicconststring powerbiApiDefaultScope = "https://analysis.windows.net/powerbi/api/.default";
// A method to get the Azure AD token (also known as 'access token')publicstringGetAccessToken() {
returnthis.tokenAcquisition.GetAccessTokenForAppAsync(powerbiApiDefaultScope).Result;
}
public PowerBIClient GetPowerBiClient() {
var tokenCredentials = new TokenCredentials(GetAccessToken(), "Bearer");
returnnew PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials);
}
publicasync Task<EmbeddedReportViewModel> GetReport(Guid WorkspaceId, Guid ReportId) {
PowerBIClient pbiClient = GetPowerBiClient();
// Call the Power BI service API to get the embedding data.var report = await pbiClient.Reports.GetReportInGroupAsync(WorkspaceId, ReportId);
// Generate a 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 the report embedded data to caller.returnnew EmbeddedReportViewModel {
Id = report.Id.ToString(),
EmbedUrl = report.EmbedUrl,
Name = report.Name,
Token = embedToken
};
}
}
}
Modify the HomeController.cs file
In this code example, you use dependency injection to modify the HomeController.cs file. By following a previous step, you configured the PowerBiServiceApi class as a service by calling services.AddScoped in the ConfigureServices method. With this code, you add a PowerBiServiceApi parameter to the constructor, and the .NET Core runtime creates a PowerBiServiceApi instance and pass it to the constructor.
From the Controllers folder, open the HomeController.cs file and add the following code to it:
C#
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.Extensions.Logging;
using AppOwnsData.Models;
using AppOwnsData.Services;
namespaceAppOwnsData.Controllers
{
[Authorize]
publicclassHomeController : Controller
{
private PowerBiServiceApi powerBiServiceApi;
publicHomeController(PowerBiServiceApi powerBiServiceApi)
{
this.powerBiServiceApi = powerBiServiceApi;
}
[AllowAnonymous]
public IActionResult Index()
{
return View();
}
publicasync Task<IActionResult> Embed() {
// Replace these two GUIDs with the workspace ID and report ID you recorded earlier.
Guid workspaceId = new Guid("11111111-1111-1111-1111-111111111111");
Guid reportId = new Guid("22222222-2222-2222-2222-222222222222");
var viewModel = await powerBiServiceApi.GetReport(workspaceId, reportId);
return View(viewModel);
}
[AllowAnonymous]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Step 5 - Build your app's client side
For client-side implementation, you need to create or modify the files that are listed in the following table:
File
Use
embed.js
Contains the client-side JavaScript code
Embed.cshtml
Contains your app's document object model (DOM) and a DIV for embedding the report
Create a container for your embedded report
In this tutorial, you create the Embed.cshtml file, which has a div element that's a container for your embedded report, and three scripts.
In the View/Home folder, create a file called Embed.cshtml.
Add the following code to the Embed.cshtml file.
HTML
@model AppOwnsData.Services.EmbeddedReportViewModel;
<divid="embed-container"style="height:800px;"></div>
@section Scripts {
<!-- powerbi.min.js is the JavaScript file that loads the client-side Power BI JavaScript API library.
Make sure that you're working with the latest library version.
You can check the latest library available in https://cdnjs.com/libraries/powerbi-client --><scriptsrc="https://cdn.jsdelivr.net/npm/powerbi-client@2.18.0/dist/powerbi.min.js"></script><!-- This script creates a JavaScript object named viewModel which is accessible to the JavaScript code in embed.js. --><script>var viewModel = {
reportId: "@Model.Id",
embedUrl: "@Model.EmbedUrl",
token: "@Model.Token"
};
</script><!-- This script specifies the location of the embed.js file --><scriptsrc="~/js/embed.js"></script>
}
Add client-side JavaScript to embed your report
To embed Power BI content, you need to create a configuration object. To learn more about creating the configuration object, see Embed a report.
In this tutorial, you create a JavaScript file named embed.js with a configuration object for embedding your report that uses the variable models.
You can initialize models by using a call to window['powerbi-client'].models. The models variable is used to set configuration values such as models.Permissions.All, models.TokenType.Aad, and models.ViewMode.View.
The powerbi.embed function uses the models configuration object to embed your report.
In the wwwroot/js folder, create a file called embed.js.
Add the following code to the embed.js file.
JavaScript
$(function () {
// 1 - Get DOM object for the div that's the report container.var reportContainer = document.getElementById("embed-container");
// 2 - Get the report embedding data from the view model.var reportId = window.viewModel.reportId;
var embedUrl = window.viewModel.embedUrl;
var token = window.viewModel.token
// 3 - Embed the report by using the Power BI JavaScript API.var models = window['powerbi-client'].models;
var config = {
type: 'report',
id: reportId,
embedUrl: embedUrl,
accessToken: token,
permissions: models.Permissions.All,
tokenType: models.TokenType.Embed,
viewMode: models.ViewMode.View,
settings: {
panes: {
filters: { expanded: false, visible: true },
pageNavigation: { visible: false }
}
}
};
// Embed the report and display it within the div container.var report = powerbi.embed(reportContainer, config);
// 4 - Add logic to resize the embed container on a window resize event.var heightBuffer = 12;
var newHeight = $(window).height() - ($("header").height() + heightBuffer);
$("#embed-container").height(newHeight);
$(window).resize(function () {
var newHeight = $(window).height() - ($("header").height() + heightBuffer);
$("#embed-container").height(newHeight);
});
});
Step 6 - Run your application
After you've followed all previous steps, you're ready to run your application. Try running your application, and experiment with the way your Power BI report is embedded. You can use the Power BI embedded analytics Client APIs to enhance your app by using client-side APIs.
Important
If you used free embed trial tokens for development, you must buy a capacity for production. Until a capacity is purchased, the Free trial version banner continues to appear at the top of the embedded report.