Embed Power BI Reports On Your Website or Portal – ASP.NET Core Back End & React Front End

The purpose of this document is to demonstrate how to embed a Power BI Report on to your website or portal when your website or portal is using an architecture that has an ASP.NET Core back end, and a REACT front end.

Introduction

Power BI is Microsoft’s premier data analytics tool. The possibilities of sharing the reports generated in that tool are virtually endless. Today we will cover one use case where you would like to embed the reports on to a website or portal using the Power BI API. There are many other options and architectures that can be used for these embedding options including no / low code options where report developers can supply an “embed code” to you or your portal from the Power BI service. However, that workflow might not meet the use case of your portal, and this demonstration will show you how to use the power of the API to embed objects on to your website or portal.

Inspiration

When you go through the “Set up your Power BI Embedding environment” through this page you get a .NET Framework Web Application https://app.powerbi.com/embedsetup. Furthermore , if you look through this GitHub repo you will see many things that will help you get started server side, and even has Server Side rendered pages for those frameworks to demonstrate what is necessary on the front end; furthermore, there is a REACT example, however it only covers the “for your organization” use case and not the “for your customer” use case and doesn’t really cover what the server needs to do in order to get the embed configuration and provide it to the front end application https://github.com/microsoft/PowerBI-Developer-Samples.

Today we will be combining communications between the server and REACT client.

This tutorial will feel allot like reading https://learn.microsoft.com/en-us/power-bi/developer/embedded/embed-sample-for-customers?tabs=net-core however the twist for us is we will be using ASP.NET Core to provide API responses instead of server side rendered pages, and REACT to display the report.

Prerequisites

This tutorial will assume that you already have followed the first 7 steps on this page https://learn.microsoft.com/en-us/power-bi/developer/embedded/embed-sample-for-customers and have an account that can authenticate to Power BI, an App Registration in the Azure AD tenant, a Power BI Workspace, a sample Power BI report, and the configuration parameters necessary such as the Client ID, Workspace ID, Report ID, Client Secret, Tenant ID, and if necessary the Power BI Username and Power BI Password.

Method

To make things easier for the purposes of this tutorial we will be creating an ASP.NET Core hosted REACT App. Your situation might actually have ASP.NET Core as one code base, and the REACT app as a second code base which is ok too, and still possible to work with. Again, I am only choosing this method for ease of demonstration. I will also be using the command line and Visual Studio code in this demonstration. Visual Studio Professional is still a viable option as well.

Create an ASP.NET Core Hosted REACT App

Change directories to a place where you would like to host your code (for me I will be using C:\users\me\source\repos\powerbi-netcore-react-demo) and execute the following command:

dotnet new sln
dotnet new react
dotnet add package Microsoft.Identity.Client
dotnet add package Microsoft.PowerBI.Api
dotnet sln add .
code .

When prompted say yes you would like to add the files necessary to debug the application. If you miss it don’t worry you can click on the Run and Debug tab to create the files.

To begin let’s put our configuration information in to the appsettings.json file

This should be the minimum necessary information to fill out:

{
  "AzureAd": {
    "AuthenticationMode": "ServicePrincipal",
    "AuthorityUrl": "https://login.microsoftonline.com/organizations/",
    "ClientId": "",
    "TenantId": "",
    "ScopeBase": ["https://analysis.windows.net/powerbi/api/.default"],
    "PbiUsername": "",
    "PbiPassword": "",
    "ClientSecret": ""
  },
  "PowerBI": {
    "WorkspaceId": "",
    "ReportId": ""
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

If you are using a “masteraccount” instead of a “ServicePrincipal” be sure to update the AuthenticationMode property to the appropriate value.

Now let’s shamelessly plagiarize pieces of the Microsoft Sample Application (https://github.com/microsoft/PowerBI-Developer-Samples/tree/master/.NET%20Core/Embed%20for%20your%20customers) so that our application can at least run the Services that they are demonstrating.

Create a “Models” folder and add the 4 models that they have in their sample application (AzureAd.cs, EmbedReport.cs, EmbedParam.cs and PowerBI.cs).

Once that is in place add the following items to your Program.cs file to inject the services and load the configurations.
builder.Services.AddScoped(typeof(AadService));
builder.Services.AddScoped(typeof(PbiEmbedService));
builder.Services.Configure<AzureAd>(builder.Configuration.GetSection("AzureAd"));
builder.Services.Configure<PowerBI>(builder.Configuration.GetSection("PowerBI"));
Where our project will be different is with the Controllers, and what we are doing with the Client App (which doesn’t exist with the current Microsoft example).

Create a new file in the Controllers folder called: “EmbedInfoController.cs”.

Give it the following code:

// ----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// ----------------------------------------------------------------------------

namespace powerbi_netcore_react_demo.Controllers
{
    using powerbi_netcore_react_demo.Models;
    using powerbi_netcore_react_demo.Services;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Options;
    using System;
    using System.Text.Json;

    [ApiController]
    [Route("[controller]")]
    public class EmbedInfoController : ControllerBase
    {
        private readonly PbiEmbedService pbiEmbedService;
        private readonly IOptions<AzureAd> azureAd;
        private readonly IOptions<PowerBI> powerBI;

        public EmbedInfoController(PbiEmbedService pbiEmbedService, IOptions<AzureAd> azureAd, IOptions<PowerBI> powerBI)
        {
            this.pbiEmbedService = pbiEmbedService;
            this.azureAd = azureAd;
            this.powerBI = powerBI;
        }

        /// <summary>
        /// Returns Embed token, Embed URL, and Embed token expiry to the client
        /// </summary>
        /// <returns>JSON containing parameters for embedding</returns>
        [HttpGet]
        public string GetEmbedInfo()
        {
            try
            {
                // Validate whether all the required configurations are provided in appsettings.json
                string configValidationResult = ConfigValidatorService.ValidateConfig(azureAd, powerBI);
                if (configValidationResult != null)
                {
                    HttpContext.Response.StatusCode = 400;
                    return configValidationResult;
                }

                EmbedParams embedParams = pbiEmbedService.GetEmbedParams(new Guid(powerBI.Value.WorkspaceId), new Guid(powerBI.Value.ReportId));
                return JsonSerializer.Serialize<EmbedParams>(embedParams);
            }
            catch (Exception ex)
            {
                HttpContext.Response.StatusCode = 500;
                return ex.Message + "\n\n" + ex.StackTrace;
            }
        }
    }
}
Again, you may need to adjust the namespaces to suit your project.

In order for this endpoint to actually be exposed edit ClientApp\src\setupProxy.js and add “/embedinfo” to the existing context variable in that file:

const context = [
  "/weatherforecast",
  "/embedinfo",
];

Before we get too much further, let’s confirm that we can hit this endpoint and get the json response that has our embed code in it. Then we will move on to use that embed code in our application.

Startup the project and browse to your url/embedinfo, for example my URL was localhost:44416/embedinfo your https port might be different in your application. And when I went there I saw this:

Close your browser and stop your process and let’s continue to code.

Open a terminal and change directories so that your terminal is in the ClientApp folder of the project. Execute the following command to install the powerbi-client-react package.

npm i powerbi-client-react –save

Let’s create a component to be the Embedded Power BI Report page so in the ClientApp\src\components folder add a new file called: “PowerBIReport.js”

Give it the following code:

import React, { useEffect, useState } from 'react'
import { PowerBIEmbed, EmbedType } from 'powerbi-client-react';
import { models } from 'powerbi-client';

function PowerBIReport() {

    const [isLoadingEmbedConfig, setIsLoadingEmbedConfig] = useState(true);
    const [embedConfig, setEmbedConfig] = useState(null);
    const [loadingError, setLoadingError] = useState(null);

    useEffect(() => {
        const getEmbedConfig = async () => {
            try {
                const response = await fetch('embedinfo');
                const configReceived = await response.json();

                const configToUse = {
                    type: EmbedType.Report,
                    id: configReceived.EmbedReport[0].ReportId,
                    embedUrl: configReceived.EmbedReport[0].EmbedUrl,
                    accessToken: configReceived.EmbedToken.Token,
                    tokenType: models.TokenType.Embed,
                    settings: {
                        panes: {
                            filters: {
                                expanded: false,
                                visible: false
                            }
                        },
                        background: models.BackgroundType.Transparent
                    }
                }

                setEmbedConfig(configToUse);
                setIsLoadingEmbedConfig(false);
            }
            catch (error) {
                setLoadingError(error.message);
                setIsLoadingEmbedConfig(false);
            }
        }

        getEmbedConfig();        
    }, []);

    return (
        <>
            <h1>Power BI Report</h1>
            <p>This component demonstrates embedding a Power BI report.</p>
            {
                isLoadingEmbedConfig && <p>Loading...</p>
            }
            {
                !isLoadingEmbedConfig && loadingError && <p>{loadingError}</p>
            }
            {
                !isLoadingEmbedConfig && !loadingError && embedConfig &&
                <PowerBIEmbed
                    embedConfig={embedConfig}
                    cssClassName='apiembeddedreport'
                />
            }
        </>
    )
}

export default PowerBIReport
To the ClientApp\src\custom.css file add the following code to the end:
.apiembeddedreport {
  height: 541.25px;
  width: 1140px;
  margin-bottom: 1rem;
}
Add navigation to this page by editing ClientApp\src\components\NavMenu.js. Add the following nav item block after the fetch nav item block:
              <NavItem>
                <NavLink tag={Link} className="text-dark" to="/powerbi-report">Power BI Report</NavLink>
              </NavItem>
Add routing to this page by editing ClientApp\src\AppRoutes.js
import PowerBIReport from "./components/PowerBIReport";
And later in the file, within the AppRoutes array add the following object:
  {
    path: '/powerbi-report',
    element: <PowerBIReport />
  }
Browse to this page in your application and you should see something like this:

Discussion

We just walked through a lot of steps there. Let’s take a moment to review what we did there on the client side. We first installed the powerbi-client-react package. We then created a page that will use the component that is available in that package. When the page loads it queries the backend route that we created earlier. When that backend route is triggered it reaches out to AAD to get an access token, and uses that access token to gain access to the Power BI platform. One it has an access token to the Power BI platform it requests the embed code based on the provided workspace id and report id. Once that information is retrieved it gives it to the client. The client then converts the data to a useable configuration for the react component.

In this demonstration the storage medium for retrieving the WorkspaceId and ReportId was from the appconfiguration. As you might imagine in your use case you will probably want to provide a way for your users to select reports from the workspaces. The PowerBI client that is demonstrated within the EmbedService on the server is certainly capable of that. So you could theoretically create a new function that would retrieve that information from the PowerBI client and return that through your API and build a UI around that. On the other hand, maybe you want only report developers to curate which reports will be shared, and with who those reports will be shared. So in that case you would create the appropriate storage configuration and UI to allow report designers to give that information to the portal, and programming that will only show the users of the portal the reports that the designers designated to see.

Conclusion

The power of the PowerBI client API is very strong and can offer you many options for integration for your website or portal that you are developing. There may only be a few code samples from Microsoft. But it is certainly possible to read the documentation and learn from their code samples to apply them to the architecture of your website or portal.

GitHub repository: https://github.com/woodman231/powerbi-netcore-react-demo

About Intertech

Intertech is a Software Development Consulting Firm that provides single and multiple turnkey software development teams, available on your schedule and configured to achieve success as defined by your requirements independently or in co-development with your team. Intertech teams combine proven full-stack, DevOps, Agile-experienced lead consultants with Delivery Management, User Experience, Software Development, and QA experts in Business Process Automation (BPA), Microservices, Client- and Server-Side Web Frameworks of multiple technologies, Custom Portal and Dashboard development, Cloud Integration and Migration (Azure and AWS), and so much more. Each Intertech employee leads with the soft skills necessary to explain complex concepts to stakeholders and team members alike and makes your business more efficient, your data more valuable, and your team better. In addition, Intertech is a trusted partner of more than 4000 satisfied customers and has a 99.70% “would recommend” rating.