Embed Power BI in your product for your customers (.NET Framework Embed Report)

Initialize

  1. Install nuget Microsoft.PowerBI.Api.
  2. Install nuget Microsoft.PowerBI.JavaScript.


Models

  • PBISetting.cs
public class PBISetting
{
  public string ApplicationId { get; set; }
  public List<Guid> WorkspaceId { get; set; }
  public List<List<Guid>> ReportId { get; set; }
  public string AuthenticationType { get; set; }
  public string Username { get; set; }
  public string Password { get; set; }
  public string ApplicationSecret { get; set; }
  public string Tenant { get; set; }
}
  • EmbedReport.cs
public class EmbedReport
{
  // Id of Power BI report to be embedded
  public Guid ReportId { get; set; }

  // Name of the report
  public string ReportName { get; set; }

  // Embed URL for the Power BI report
  public string EmbedUrl { get; set; }
}
  • ReportEmbedConfig.cs
public class ReportEmbedConfig
{
  // Report to be embedded
  public List<EmbedReport> EmbedReports { get; set; }

  // Embed Token for the Power BI report
  public EmbedToken EmbedToken { get; set; }
}
  • ErrorModel.cs
public class ErrorModel
{
  public string ErrorMessage { get; internal set; }
}


Services

  • AadService.cs
  public class AadService
  {
    private static readonly string m_authorityUrl = "https://login.microsoftonline.com/organizations";
    private static readonly string[] m_scope = "https://analysis.windows.net/powerbi/api/.default".Split(';');

    /// <summary>
    /// Get Access token
    /// </summary>
    /// <returns>Access token</returns>
    [Obsolete]
    public static async Task<string> GetAccessToken(PBISetting setting)
    {
      AuthenticationResult authenticationResult = null;
      if (setting.AuthenticationType.Equals("masteruser", StringComparison.InvariantCultureIgnoreCase))
      {
        IPublicClientApplication clientApp = PublicClientApplicationBuilder
                                  .Create(setting.ApplicationId)
                                  .WithAuthority(m_authorityUrl)
                                  .Build();
        var userAccounts = await clientApp.GetAccountsAsync();

        try
        {
          authenticationResult = await clientApp.AcquireTokenSilent(m_scope, userAccounts.FirstOrDefault()).ExecuteAsync();
        }
        catch (MsalUiRequiredException)
        {
          SecureString secureStringPassword = new SecureString();
          foreach (var key in setting.Password)
          {
            secureStringPassword.AppendChar(key);
          }
          authenticationResult = await clientApp.AcquireTokenByUsernamePassword(m_scope, setting.Username, secureStringPassword).ExecuteAsync();
        }
      }

      // Service Principal auth is recommended by Microsoft to achieve App Owns Data Power BI embedding
      else if (setting.AuthenticationType.Equals("serviceprincipal", StringComparison.InvariantCultureIgnoreCase))
      {
        // For app only authentication, we need the specific tenant id in the authority url
        var tenantSpecificURL = m_authorityUrl.Replace("organizations", setting.Tenant);

        IConfidentialClientApplication clientApp = ConfidentialClientApplicationBuilder
                                        .Create(setting.ApplicationId)
                                        .WithClientSecret(setting.ApplicationSecret)
                                        .WithAuthority(tenantSpecificURL)
                                        .Build();

        authenticationResult = await clientApp.AcquireTokenForClient(m_scope).ExecuteAsync();
      }

      return authenticationResult.AccessToken;
    }
  }
  • PBIService.cs
  public class PBIService
  {
    public static string CheckSettingError(PBISetting setting)
    {
      string message = null;
      Guid result;

      // Application Id must have a value.
      if (string.IsNullOrWhiteSpace(setting.ApplicationId))
      {
        message = "ApplicationId is empty. please register your application as Native app in https://dev.powerbi.com/apps and fill client Id in setting.";
      }
      // Application Id must be a Guid object.
      else if (!Guid.TryParse(setting.ApplicationId, out result))
      {
        message = "ApplicationId must be a Guid object. please register your application as Native app in https://dev.powerbi.com/apps and fill application Id in setting.";
      }
      // Workspace Id must have a value.
      else if (setting.WorkspaceId.Where(x => x == Guid.Empty).Count() > 0)
      {
        message = "WorkspaceId is empty or not a valid Guid. Please fill its Id correctly in setting";
      }
      // Report Id must have a value.
      else if (setting.ReportId.Where(x => x.Where(y => y == Guid.Empty).Count() > 0).Count() > 0)
      {
        message = "ReportId is empty or not a valid Guid. Please fill its Id correctly in setting";
      }
      else if (setting.AuthenticationType.Equals("masteruser", StringComparison.InvariantCultureIgnoreCase))
      {
        // Username must have a value.
        if (string.IsNullOrWhiteSpace(setting.Username))
        {
          message = "Username is empty. Please fill Power BI username in setting";
        }

        // Password must have a value.
        if (string.IsNullOrWhiteSpace(setting.Password))
        {
          message = "Password is empty. Please fill password of Power BI username in setting";
        }
      }
      else if (setting.AuthenticationType.Equals("serviceprincipal", StringComparison.InvariantCultureIgnoreCase))
      {
        if (string.IsNullOrWhiteSpace(setting.ApplicationSecret))
        {
          message = "ApplicationSecret is empty. please register your application as Web app and fill appSecret in setting.";
        }
        // Must fill tenant Id
        else if (string.IsNullOrWhiteSpace(setting.Tenant))
        {
          message = "Invalid Tenant. Please fill Tenant ID in Tenant under setting";
        }
      }
      else
      {
        message = "Invalid authentication type";
      }

      return message;
    }

    private static Guid GetParamGuid(string param)
    {
      Guid paramGuid = Guid.Empty;
      Guid.TryParse(param, out paramGuid);
      return paramGuid;
    }
  }
  • EmbedService.cs
  public static class EmbedService
  {
    private static readonly string urlPowerBiServiceApiRoot = "https://api.powerbi.com";

    public static async Task<PowerBIClient> GetPowerBiClient(PBISetting setting)
    {
      var tokenCredentials = new TokenCredentials(await AadService.GetAccessToken(setting), "Bearer");
      return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials);
    }

    /// <summary>
    /// Get embed params for a report
    /// </summary>
    /// <returns>Wrapper object containing Embed token, Embed URL, Report Id, and Report name for single report</returns>
    public static async Task<ReportEmbedConfig> GetEmbedParams(PBISetting setting, Guid workspaceId, Guid reportId, [Optional] Guid additionalDatasetId)
    {
      using (var pbiClient = await GetPowerBiClient(setting))
      {
        // Get report info
        var pbiReport = pbiClient.Reports.GetReportInGroup(workspaceId, reportId);

        /*
        Check if dataset is present for the corresponding report
        If no dataset is present then it is a RDL report 
        */
        bool isRDLReport = String.IsNullOrEmpty(pbiReport.DatasetId);

        EmbedToken embedToken;

        if (isRDLReport)
        {
          // Get Embed token for RDL Report
          embedToken = await GetEmbedTokenForRDLReport(setting, workspaceId, reportId);
        }
        else
        {
          // Create list of dataset
          var datasetIds = new List<Guid>();

          // Add dataset associated to the report
          datasetIds.Add(Guid.Parse(pbiReport.DatasetId));

          // Append additional dataset to the list to achieve dynamic binding later
          if (additionalDatasetId != Guid.Empty)
          {
            datasetIds.Add(additionalDatasetId);
          }

          // Get Embed token multiple resources
          embedToken = await GetEmbedToken(setting, reportId, datasetIds, workspaceId);
        }

        // Add report data for embedding
        var embedReports = new List<EmbedReport>() {
          new EmbedReport
          {
            ReportId = pbiReport.Id, ReportName = pbiReport.Name, EmbedUrl = pbiReport.EmbedUrl
          }
        };

        // Capture embed params
        var embedParams = new ReportEmbedConfig
        {
          EmbedReports = embedReports,
          EmbedToken = embedToken
        };

        return embedParams;
      }
    }

    /// <summary>
    /// Get embed params for multiple reports for a single workspace
    /// </summary>
    /// <returns>Wrapper object containing Embed token, Embed URL, Report Id, and Report name for multiple reports</returns>
    /// <remarks>This function is not supported for RDL Report</remakrs>
    public static async Task<ReportEmbedConfig> GetEmbedParams(PBISetting setting, Guid workspaceId, IList<Guid> reportIds, [Optional] IList<Guid> additionalDatasetIds)
    {
      // Note: This method is an example and is not consumed in this sample app

      using (var pbiClient = await GetPowerBiClient(setting))
      {
        // Create mapping for reports and Embed URLs
        var embedReports = new List<EmbedReport>();

        // Create list of datasets
        var datasetIds = new List<Guid>();

        // Get datasets and Embed URLs for all the reports
        foreach (var reportId in reportIds)
        {
          // Get report info
          var pbiReport = pbiClient.Reports.GetReportInGroup(workspaceId, reportId);

          // Append to existing list of datasets to achieve dynamic binding later
          datasetIds.Add(Guid.Parse(pbiReport.DatasetId));

          // Add report data for embedding
          embedReports.Add(new EmbedReport { ReportId = pbiReport.Id, ReportName = pbiReport.Name, EmbedUrl = pbiReport.EmbedUrl });
        }

        // Append to existing list of datasets to achieve dynamic binding later
        if (additionalDatasetIds != null)
        {
          datasetIds.AddRange(additionalDatasetIds);
        }

        // Get Embed token multiple resources
        var embedToken = await GetEmbedToken(setting, reportIds, datasetIds, workspaceId);

        // Capture embed params
        var embedParams = new ReportEmbedConfig
        {
          EmbedReports = embedReports,
          EmbedToken = embedToken
        };

        return embedParams;
      }
    }

    /// <summary>
    /// Get Embed token for single report, multiple datasets, and an optional target workspace
    /// </summary>
    /// <returns>Embed token</returns>
    /// <remarks>This function is not supported for RDL Report</remakrs>
    public static async Task<EmbedToken> GetEmbedToken(PBISetting setting, Guid reportId, IList<Guid> datasetIds, [Optional] Guid targetWorkspaceId)
    {
      using (var pbiClient = await GetPowerBiClient(setting))
      {
        // Create a request for getting Embed token 
        // This method works only with new Power BI V2 workspace experience
        var tokenRequest = new GenerateTokenRequestV2(

        reports: new List<GenerateTokenRequestV2Report>() { new GenerateTokenRequestV2Report(reportId) },

        datasets: datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList(),

        targetWorkspaces: targetWorkspaceId != Guid.Empty ? new List<GenerateTokenRequestV2TargetWorkspace>() { new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId) } : null
        );

        // Generate Embed token
        var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest);

        return embedToken;
      }
    }

    /// <summary>
    /// Get Embed token for multiple reports, datasets, and an optional target workspace
    /// </summary>
    /// <returns>Embed token</returns>
    /// <remarks>This function is not supported for RDL Report</remakrs>
    public static async Task<EmbedToken> GetEmbedToken(PBISetting setting, IList<Guid> reportIds, IList<Guid> datasetIds, [Optional] Guid targetWorkspaceId)
    {
      // Note: This method is an example and is not consumed in this sample app

      using (var pbiClient = await GetPowerBiClient(setting))
      {
        // Convert reports to required types
        var reports = reportIds.Select(reportId => new GenerateTokenRequestV2Report(reportId)).ToList();

        // Convert datasets to required types
        var datasets = datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList();

        // Create a request for getting Embed token 
        // This method works only with new Power BI V2 workspace experience
        var tokenRequest = new GenerateTokenRequestV2(

          datasets: datasets,

          reports: reports,

          targetWorkspaces: targetWorkspaceId != Guid.Empty ? new List<GenerateTokenRequestV2TargetWorkspace>() { new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId) } : null
        );

        // Generate Embed token
        var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest);

        return embedToken;
      }
    }

    /// <summary>
    /// Get Embed token for multiple reports, datasets, and optional target workspaces
    /// </summary>
    /// <returns>Embed token</returns>
    /// <remarks>This function is not supported for RDL Report</remakrs>
    public static async Task<EmbedToken> GetEmbedToken(PBISetting setting, IList<Guid> reportIds, IList<Guid> datasetIds, [Optional] IList<Guid> targetWorkspaceIds)
    {
      // Note: This method is an example and is not consumed in this sample app

      using (var pbiClient = await GetPowerBiClient(setting))
      {
        // Convert report Ids to required types
        var reports = reportIds.Select(reportId => new GenerateTokenRequestV2Report(reportId)).ToList();

        // Convert dataset Ids to required types
        var datasets = datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList();

        // Convert target workspace Ids to required types
        IList<GenerateTokenRequestV2TargetWorkspace> targetWorkspaces = null;
        if (targetWorkspaceIds != null)
        {
          targetWorkspaces = targetWorkspaceIds.Select(targetWorkspaceId => new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId)).ToList();
        }

        // Create a request for getting Embed token 
        // This method works only with new Power BI V2 workspace experience
        var tokenRequest = new GenerateTokenRequestV2(

          datasets: datasets,

          reports: reports,

          targetWorkspaces: targetWorkspaceIds != null ? targetWorkspaces : null
        );

        // Generate Embed token
        var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest);

        return embedToken;
      }
    }

    /// <summary>
    /// Get Embed token for RDL Report
    /// </summary>
    /// <returns>Embed token</returns>
    public static async Task<EmbedToken> GetEmbedTokenForRDLReport(PBISetting setting, Guid targetWorkspaceId, Guid reportId, string accessLevel = "view")
    {
      using (var pbiClient = await GetPowerBiClient(setting))
      {

        // Generate token request for RDL Report
        var generateTokenRequestParameters = new GenerateTokenRequest(
          accessLevel: accessLevel
        );

        // Generate Embed token
        var embedToken = pbiClient.Reports.GenerateTokenInGroup(targetWorkspaceId, reportId, generateTokenRequestParameters);

        return embedToken;
      }
    }
  }


Controllers

public class HomeController : Controller
{
  private string m_errorMessage;
  
  [Obsolete]
  public async Task<ActionResult> Index()
  {
    //Get User
    UserModel user = new UserModel();
    var users = crm.User.Where(x => x.IsActive == true).ToList();
    user = users.Where(x => x.ID.Equals(SessionHelper.UserID)).FirstOrDefault();
  
    if (user == null)
    {
      return RedirectToAction("Login", "Account");
    }
  
    //Get ClientProfile
    ClientProfileModel client = new ClientProfileModel();
    client = crm.ClientProfile.Where(x => x.ID.Equals(SessionHelper.ClientProfileID)).FirstOrDefault();
  
    if (client == null)
    {
      return RedirectToAction("Login", "Account");
    }
  
    //Get Workspaces
    List<PowerBIWorkspaceModel> workspace = new List<PowerBIWorkspaceModel>();
    var workspaces = crm.PowerBIWorkspace.Where(x => x.IsActive == true).ToList();
  
    if (user.PowerBIWorkspaceID != null)
    {
      workspace = workspaces.Where(x => user.PowerBIWorkspaceID.Split(',').ToList().Contains(x.ID.ToString())).ToList();
    }
  
    //Get Embeded
    List<PowerBIEmbedModel> embeds = new List<PowerBIEmbedModel>();
    List<List<Guid>> EmbedId = new List<List<Guid>>();
    List<List<string>> EmbedType = new List<List<string>>();
    List<List<string>> EmbedTitle = new List<List<string>>();
    embeds = crm.PowerBIEmbed.Where(x => x.IsActive == true).ToList();
    foreach (var w in workspace)
    {
      var e = embeds.Where(x => x.WorkspaceID.ToString() == w.ID.ToString());
      EmbedId.Add(e.Select(x => new Guid(x.EmbedID)).ToList());
    }
  
    string ApplicationId = client.PowerBIApplicationID;
    List<Guid> WorkspaceId = workspace.Select(x => new Guid(x.WorkspaceID)).ToList();
    string AuthenticationType = "masteruser";
    string ApplicationSecret = "";
    string Tenant = "";
    string Username = workspace.Select(x => x.PBIUsername).ToList().FirstOrDefault();
    string Password = workspace.Select(x => x.PBIPassword).ToList().FirstOrDefault();
  
    PBISetting setting = new PBISetting();
    setting.ApplicationId = ApplicationId;
    setting.WorkspaceId = WorkspaceId;
    setting.EmbedId = EmbedId;
    setting.EmbedType = EmbedType;
    setting.EmbedTitle = EmbedTitle;
    setting.AuthenticationType = AuthenticationType;
    setting.Username = Username;
    setting.Password = Password;
    setting.ApplicationSecret = ApplicationSecret;
    setting.Tenant = Tenant;
  
    m_errorMessage = PBIService.CheckSettingError(setting);
  
    if (!m_errorMessage.IsNullOrWhiteSpace())
    {
      return View("Error", BuildErrorModel(m_errorMessage));
    }
  
    try
    {
      EmbedConfig results = new EmbedConfig();
      List<ReportEmbedConfig> reportList = new List<ReportEmbedConfig>();
      List<DashboardEmbedConfig> dashboardList = new List<DashboardEmbedConfig>();
      List<TileEmbedConfig> tileList = new List<TileEmbedConfig>();
  
      for (int w = 0; w < setting.WorkspaceId.Count; w++)
      {
        var embedReport = await EmbedService.GetEmbedParams(setting, setting.WorkspaceId[w], setting.EmbedId[w]);
        reportList.Add(embedReport);
      }
  
      results.Reports = reportList;
      results.Dashboards = dashboardList;
      results.Tiles = tileList;
  
      return View(results);
    }
    catch (Exception ex)
    {
      //m_errorMessage = "Operation failed: system error. please contact your system administrator.";
      m_errorMessage = ex.Message;
      return View("Error", BuildErrorModel(m_errorMessage));
    }
  }
  
  private ErrorModel BuildErrorModel(string errorMessage)
  {
    return new ErrorModel
    {
      ErrorMessage = errorMessage
    };
  }
}


View

@if (Model.Count > 0)
{
    for (int i = 0; i < Model.Count; i++)
    {
        for (int j = 0; j < Model[i].EmbedReports.Count; j++)
        {
            string id = "embedContainer_" + i + "_" + j;


            <div id="@id" class="mb-3" style="height:@height"></div>
        }
    }
}

<script src="~/Scripts/powerbi-client/dist/powerbi.min.js"></script>
@if (Model.Count > 0)
{
    <script>
        // Get a reference to the embedded report HTML element
        const reportContainer = [];


        @for (int i = 0; i < Model.Count; i++)
        {
            for (int j = 0; j < Model[i].EmbedReports.Count; j++)
            {
                @:reportContainer.push($(`#embedContainer_@(i)_@(j)`)[0]);
            }
        }


        // Read embed application token from Model
        const accessToken = [];


        @for (int i = 0; i < Model.Count; i++)
        {
            for (int j = 0; j < Model[i].EmbedReports.Count; j++)
            {
                @:accessToken.push(`@(Model[i].EmbedToken.Token)`);
            }
        }


        // You can embed different reports as per your need by changing the index
        // Read embed URL from Model
        const embedUrl = [];


        @for (int i = 0; i < Model.Count; i++)
        {
            for (int j = 0; j < Model[i].EmbedReports.Count; j++)
            {
                @:embedUrl.push(`@(Model[i].EmbedReports[j].EmbedUrl)`);
            }
        }


        // Read report Id from Model
        const embedReportId = [];


        @for (int i = 0; i < Model.Count; i++)
        {
            for (int j = 0; j < Model[i].EmbedReports.Count; j++)
            {
                @:embedReportId.push(`@`(Model[i].EmbedReports[j].ReportId)`);
            }
        }


        // Use the token expiry to regenerate Embed token for seamless end user experience
        // Refer https://aka.ms/RefreshEmbedToken
        const tokenExpiry = [];


        @for (int i = 0; i < Model.Count; i++)
        {
            for (int j = 0; j < Model[i].EmbedReports.Count; j++)
            {
                @:embedReportId.push(`@(Html.Raw(Model[i].EmbedToken.Expiration))`);
            }
        }


        // Get models. models contains enums that can be used.
        const models = window['powerbi-client'].models;

        // Embed configuration used to describe the what and how to embed.
        // This object is used when calling powerbi.embed.
        // This also includes settings and options such as filters.
        // You can find more information at https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-Configuration-Details.
        for (let i = 0; i < reportContainer.length; i++) {
            const config = {
                type: 'report',
                tokenType: models.TokenType.Embed,
                accessToken: accessToken[i],
                embedUrl: embedUrl[i],
                id: embedReportId[i],
                permissions: models.Permissions.All,
                settings: {
                    // Enable this setting to remove gray shoulders from embedded report
                    // background: models.BackgroundType.Transparent,
                    filterPaneEnabled: true,
                    navContentPaneEnabled: true
                }
            };


            // Embed the report and display it within the div container.
            const report = powerbi.embed(reportContainer[i], config);
        }
    </script>
}