Initialize
- Install nuget Microsoft.PowerBI.Api.
- Install nuget Microsoft.PowerBI.JavaScript.
Models
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; }
}
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; }
}
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; }
}
public class ErrorModel
{
public string ErrorMessage { get; internal set; }
}
Services
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;
}
}
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;
}
}
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>
}