使用 Power BI 嵌入开发可缩放的多租户应用程序

本文介绍如何开发可嵌入 Power BI 内容的多租户应用程序,同时实现最高级别的可伸缩性、性能和安全性。 通过使用服务主体配置文件设计和实现应用程序,可以创建和管理由数万个客户租户组成的多租户解决方案,这些客户租户可向超过 100,000 名用户的受众提供报表。

通过服务主体配置文件功能,可以更轻松地管理 Power BI 中的组织内容,更有效地使用容量。 但是,使用服务主体配置文件会增加应用程序设计的复杂性。 因此,应该仅在需要实现大规模缩放时才使用它们。 如果有多个工作区和超过 1,000 名应用程序用户,我们建议使用服务主体配置文件。

注意

使用服务主体配置文件的价值会随着你对缩放需求以及实现最高级别安全性和租户隔离的需求的增加而增加。

可以使用两种不同的嵌入方案来实现 Power BI 嵌入:“为组织嵌入内容”和“为客户嵌入内容”。

当应用受众包括内部用户时,将应用“为组织嵌入内容”方案。 内部用户具有组织帐户,并且必须使用 Microsoft Entra ID 进行身份验证。 在此方案中,Power BI 是软件即服务 (SaaS)。 它有时称为“用户拥有数据”。

当应用受众包括外部用户时,将应用“为客户嵌入内容”方案。 应用程序负责对用户进行身份验证。 若要访问 Power BI 内容,应用程序依赖于嵌入标识(Microsoft Entra 服务主体或主用户帐户)通过 Microsoft Entra 进行身份验证。 在此方案中,Power BI 是平台即服务 (PaaS)。 它有时称为“应用拥有数据”。

注意

请务必了解服务主体配置文件功能旨在用于“为客户嵌入内容”方案。 这是因为,此方案为 ISV 和企业组织提供了向大量用户和大量客户租户进行较大规模嵌入的能力。

多租户应用程序开发

如果你熟悉 Microsoft Entra,租户这个词可能会让你想到 Microsoft Entra 租户。 但是,租户的概念在构建嵌入 Power BI 内容的多租户解决方案的上下文中有所不同。 在此上下文中,代表每个客户创建客户租户,应用程序通过使用“为客户嵌入内容”方案为其嵌入 Power BI 内容。 通常通过创建单个 Power BI 工作区来预配每个客户租户。

若要创建可缩放的多租户解决方案,必须能够自动创建新的客户租户。 预配新的客户租户通常涉及编写代码,该代码使用 Power BI REST API 创建新的 Power BI 工作区、通过导入 Power BI Desktop (.pbix) 文件、更新数据源参数、设置数据源凭据和设置计划的语义模型刷新来创建语义模型。 下图显示了如何将 Power BI 项(如报表和语义模型)添加到工作区中,以设置客户租户。

显示三个租户的设置的示意图。每个租户都有自己的数据源和工作区。

开发使用“为客户嵌入内容”方案的应用程序时,可以使用主用户帐户或服务主体形式的嵌入标识进行 Power BI REST API 调用。 建议使用生产应用程序的服务主体。 它提供最高的安全性,因此它是 Microsoft Entra 建议的方法。 此外,它还支持更好的自动化和缩放,管理开销更少。 但需要 Power BI 管理员权限进行设置和管理

通过使用服务主体,可以避免与主用户帐户关联的常见问题,例如,在要求用户使用多重身份验证 (MFA) 进行登录的环境中出现身份验证错误。 使用服务主体也与“为客户嵌入内容”方案基于使用 PaaS 思维而不是 SaaS 思维来嵌入 Power BI 内容的想法一致。

1,000 个工作区限制

设计实现“为客户嵌入内容”方案的多租户环境时,请务必考虑到无法向嵌入标识授予超过 1,000 个工作区的访问权限。 Power BI 服务施加此限制是为了确保在调用 REST API 时性能良好。 此限制的原因与 Power BI 如何为每个标识维护与安全相关的元数据有关。

Power BI 使用元数据跟踪标识可以访问的工作区和工作区项。 实际上,Power BI 必须为其授权子系统中的每个标识维护单独的访问控制列表 (ACL)。 当标识通过 REST API 调用访问工作区时,Power BI 必须针对标识的 ACL 执行安全检查,以确保其获得授权。 确定目标工作区是否在 ACL 内部所需的时间随着工作区数量的增加呈指数增长。

注意

Power BI 不会通过代码强制实施 1,000 个工作区限制。 如果要尝试,将嵌入标识添加到 1,000 多个工作区,REST API 调用仍将成功执行。 但是,应用程序将进入“不受支持”状态,如果你尝试请求 Microsoft 支持人员的帮助,这可能会产生影响。

考虑这样一个场景,其中两个多租户应用程序都被设置为使用单个服务主体。 现在假设第一个应用程序已创建 990 个工作区,而第二个应用程序已创建 1,010 个工作区。 从支持人员的角度来看,第一个应用程序在受支持的边界内,而第二个应用程序不在受支持的边界内。

现在从性能角度比较这两个应用程序。 没有太大的区别,因为两个服务主体的 ACL 都让其 ACL 的元数据增长到一定程度上降低性能的程度。

以下是关键的观察结果:服务主体创建的工作区的数量直接影响性能和可伸缩性。 作为 100 个工作区成员的服务主体执行 REST API 调用的速度要比作为 1,000 个工作区成员的服务主体更快。 同样,作为 10 个工作区成员的服务主体执行 REST API 调用的速度要比作为 100 个工作区成员的服务主体更快。

重要

从性能和可伸缩性的角度来看,服务主体是所属成员的工作区的最佳数量恰好是一个。

管理语义模型和数据源凭据的隔离

设计多租户应用程序时的另一个重要方面是隔离客户租户。 一个客户租户中的用户看不到属于另一个客户租户的数据,这一点至关重要。 因此,必须了解如何管理语义模型所有权和数据源凭据。

语义模型所有权

每个 Power BI 语义模型都有一个所有者,可以是用户帐户,也可以是服务主体。 需要语义模型所有权才能设置计划刷新和设置语义模型参数。

提示

在 Power BI 服务中,可以通过打开语义模型设置来确定语义模型所有者是谁。

如有必要,可以将语义模型的所有权转移到另一个用户帐户或服务主体。 可以在 Power BI 服务中或使用 REST API TakeOver 操作来实现。 导入 Power BI Desktop 文件以使用服务主体创建新语义模型时,服务主体将自动设置为语义模型所有者。

数据源凭据

若要将语义模型连接到其基础数据源,语义模型所有者必须设置数据源凭据。 数据源凭据由 Power BI 加密和缓存。 从那时起,当刷新数据(用于导入存储表)或执行传递查询(用于 DirectQuery 存储表)时,Power BI 使用这些凭据对基础数据源进行身份验证。

建议在预配新客户租户时应用通用设计模式。 可以使用服务主体的标识来执行一系列 REST API 调用:

  1. 创建新工作区。
  2. 将新工作区与专用容量相关联。
  3. 导入 Power BI Desktop 文件以创建语义模型。
  4. 设置该语义模型的语义模型源凭据。

完成这些 REST API 调用后,服务主体将是新工作区的管理员以及语义模型和数据源凭据的所有者。

重要

有一个常见的误解,即语义模型数据源凭据的范围是工作区级别的。 事实并非如此。 数据源凭据的范围限定为服务主体(或用户帐户),该范围扩展到 Microsoft Entra 租户中的所有 Power BI 工作区。

服务主体可以创建数据源凭据,这些凭据由跨客户租户的不同工作区中的语义模型共享,如下图所示。

显示两个租户的设置的示意图。每个租户共享相同的数据源凭据。

当数据源凭据由属于不同客户租户的语义模型共享时,客户租户并不会完全隔离。

在服务主体配置文件之前设计策略

在服务主体配置文件功能可用之前了解设计策略可以帮助你理解对该功能的需求。 在此之前,开发人员使用以下三种设计策略之一构建多租户应用程序:

  • 单个服务主体
  • 服务主体池
  • 每个工作区一个服务主体

每种设计策略都有各自的优点和缺点。

单个服务主体设计策略需要创建 Microsoft Entra 应用注册。 因此,它涉及的管理开销比另外两种设计策略少,因为无需创建更多的 Microsoft Entra 应用注册。 此策略也是最简单的设置,因为它不需要编写额外的代码,用于在进行 REST API 调用时在服务主体之间切换调用上下文。 但是,此设计策略的问题在于它无法缩放。 它仅支持可以扩展到1,000 个工作区的多租户环境。 此外,当服务主体被授予对较多工作区的访问权限时,性能肯定会下降。 还存在客户租户隔离问题,因为单个服务主体成为所有客户租户中每个语义模型和所有数据凭据的所有者。

服务主体池设计策略通常用于避免 1,000 个工作区限制。 它允许应用程序通过向池中添加正确数量的服务主体来扩展到任意数量的工作区。 例如,由五个服务主体组成的池可以扩展到 5,000 个工作区;由 80 个服务主体组成的池可以扩展到 80,000 个工作区,以此类推。 但是,虽然此策略可以扩展到大量的工作区,但它有几个缺点。 首先,它需要编写额外的代码并存储元数据,以便在进行 REST API 调用时允许在服务主体之间切换上下文。 其次,它涉及更多的管理工作,因为每当需要增加池中的服务主体数时,都必须创建 Microsoft Entra 应用注册。

而且,服务主体池策略不会针对性能进行优化,因为它允许服务主体成为数百个工作区的成员。 从客户租户隔离的角度来看,这也不理想,因为服务主体可以成为跨客户租户共享的语义模型和数据凭据的所有者。

每个工作区一个服务主体设计策略涉及为每个客户租户创建服务主体。 从理论角度来看,此策略提供最佳解决方案,因为它优化 REST API 调用的性能,同时在工作区级别为语义模型和数据源凭据提供真正的隔离。 然而,理论上最有效的方法在实践中并不总是最有效。 这是因为,为每个客户租户创建服务主体的要求对于许多组织来说是不切实际的。 这是因为某些组织有正式的审批流程,或者在创建 Microsoft Entra 应用注册时涉及过多的官僚主义。 由于这些原因,无法向自定义应用程序授予所需的权限,以解决方案所需的自动化方式按需创建 Microsoft Entra 应用注册。

在不太常见的场景中(自定义应用程序被授予了适当的权限),它可以使用 Microsoft Graph API 按需创建 Microsoft Entra 应用注册。 但是,自定义应用程序通常很难开发和部署,因为它必须以某种方式跟踪每个 Microsoft Entra 应用注册的身份验证凭据。 每当需要为单个服务主体进行身份验证和获取访问令牌时,它还必须获取对这些凭据的访问权限。

服务主体配置文件

服务主体配置文件功能旨在更轻松地管理 Power BI 中的组织内容,更高效地使用容量。 它们帮助解决三个特定的难题,涉及最低的开发人员工作量和开销。 这些难题包括:

  • 缩放到大量的工作区。
  • 优化 REST API 调用的性能。
  • 在客户租户级别隔离语义模型和数据源凭据。

使用服务主体配置文件设计多租户应用程序时,可以从上一部分所述的三种设计策略的优势中获益,同时避免其相关的弱点。

服务主体配置文件是在 Power BI 上下文中创建的本地帐户。 服务主体可以使用 Profiles REST API 操作创建新的服务主体配置文件。 服务主体可以为自定义应用程序创建和管理其自己的服务主体配置文件集,如下图所示。

示意图显示在 Power BI 中创建三个服务主体配置文件的服务主体。

服务主体与其创建的服务主体配置文件之间始终存在父子关系。 无法将服务主体配置文件创建为独立实体。 可以使用特定服务主体创建服务主体配置文件,该服务主体充当配置文件的父级。 此外,对于用户帐户或其他服务主体,服务主体配置文件永远不可见。 服务主体配置文件只能由创建它的服务主体查看和使用。

Microsoft Entra 不知道服务主体配置文件

虽然 Microsoft Entra 知道服务主体本身及其基础 Microsoft Entra 应用注册,但 Microsoft Entra ID 对服务主体配置文件没有任何了解。 这是因为服务主体配置文件由 Power BI 创建,它们仅存在于控制 Power BI 安全性和授权的 Power BI 服务子系统中。

Microsoft Entra ID 不知道服务主体配置文件这一事实既有优点也有缺点。 主要优点是“为客户嵌入内容”方案应用程序不需要任何特殊的 Microsoft Entra 权限来创建服务主体配置文件。 这也意味着应用程序可以创建和管理一组独立于 Microsoft Entra 的本地标识。

但也存在一些缺点。 由于 Microsoft Entra 不知道服务主体配置文件,因此无法将服务主体配置文件添加到 Microsoft Entra 组以隐式授予它对工作区的访问权限。 此外,外部数据源(如 Azure SQL 数据库或 Azure Synapse Analytics)在连接到数据库时无法将服务主体配置文件识别为标识。 因此,当要求使用具有每个客户租户唯一身份验证凭据的单独服务主体连接到这些数据源时,“每个工作区一个服务主体”设计策略(为每个客户租户创建一个服务主体)可能是一个更好的选择。

服务主体配置文件是一级安全主体

虽然 Microsoft Entra 不知道服务主体配置文件,但 Power BI 会将它们识别为一级安全主体。 与用户帐户或服务主体一样,可以将服务主体配置文件添加到工作区角色(作为管理员或成员)。 还可以将其设置为语义模型所有者和数据源凭据的所有者。 出于这些原因,为每个新客户租户创建新的服务主体配置文件是最佳做法。

显示多个客户租户的示意图,每个租户都有自己的服务主体配置文件。

提示

使用服务主体配置文件开发“为客户嵌入内容”方案应用程序时,只需创建单个 Microsoft Entra 应用注册即可向应用程序提供单个服务主体。 与其他多租户设计策略相比,此方法显著降低管理开销,在其他多租户设计策略中,需要在应用程序部署到生产环境后持续创建额外的 Microsoft Entra 应用注册。

以服务主体配置文件的形式执行 REST API 调用

应用程序可以使用服务主体配置文件的标识来执行 REST API 调用。 这意味着它可以执行一系列 REST API 调用来预配和设置新的客户租户。

  1. 当服务主体配置文件创建新的工作区时,Power BI 会自动将该配置文件添加为工作区管理员。
  2. 当服务主体配置文件导入 Power BI Desktop 文件以创建语义模型时,Power BI 会将该配置文件设置为语义模型所有者。
  3. 当服务主体配置文件设置数据源凭据时,Power BI 会将该配置文件设置为数据源凭据的所有者。

请务必了解服务主体在 Power BI 中有一个标识,该标识与其配置文件的标识不同。 这样,你可以选择作为开发人员。 可以使用服务主体配置文件的标识来执行 REST API 调用。 或者,可以在没有配置文件的情况下执行 REST API 调用,这种情况下使用父服务主体的标识。

建议在创建、查看或删除服务主体配置文件时,作为父服务主体执行 REST API 调用。 应使用服务主体配置文件来执行所有其他 REST API 调用。 这些其他调用可以创建工作区、导入 Power BI Desktop 文件、更新语义模型参数和设置数据源凭据。 它们还可以检索工作区项元数据并生成嵌入令牌。

假设需要为名为 Contoso 的客户设置客户租户。 第一步执行 REST API 调用,以创建服务主体配置文件,其显示名称设置为 Contoso。 此调用是使用服务主体的标识进行的。 所有剩余的设置步骤都使用服务主体配置文件来完成以下任务:

  1. 创建工作区。
  2. 将工作区与容量相关联。
  3. 导入 Power BI Desktop 文件。
  4. 设置语义模型参数。
  5. 设置数据源凭据。
  6. 设置计划的数据刷新。

请务必了解,必须使用用于创建客户租户的服务主体配置文件的标识来实现对工作区及其内容的访问。 请务必了解父服务主体不需要对工作区或其内容的访问权限。

提示

请记住:进行 REST API 调用时,使用服务主体创建和管理服务主体配置文件,并使用服务主体配置文件创建、设置和访问 Power BI 内容。

使用配置文件 REST API 操作

配置文件 REST API 操作组包括创建和管理服务主体配置文件的操作:

  • Create Profile
  • Delete Profile
  • Get Profile
  • Get Profiles
  • Update Profile

创建服务主体配置文件

使用创建配置文件 REST API 操作创建服务主体配置文件。 必须在请求正文中设置 displayName 属性,以便为新租户提供显示名称。 该值在服务主体拥有的所有配置文件中必须是唯一的。 如果服务主体已存在具有该显示名称的另一个配置文件,则调用将失败。

成功的调用将返回 id 属性,该属性是表示配置文件的 GUID。 开发使用服务主体配置文件的应用程序时,建议在自定义数据库中存储配置文件显示名称和 ID 值。 这样,应用程序就可以轻松地检索 ID。

如果使用 Power BI .NET SDK 进行编程,则可以使用 Profiles.CreateProfile 方法,该方法返回表示新配置文件的 ServicePrincipalProfile 对象。 它使确定 id 属性值变得简单。

下面是创建服务主体配置文件并向其授予工作区访问权限的示例。

// Create a service principal profile
string profileName = "Contoso";

var createRequest = new CreateOrUpdateProfileRequest(profileName);
var profile = pbiClient.Profiles.CreateProfile(createRequest);

// Retrieve the ID of the new profile
Guid profileId = profile.Id;

// Grant workspace access
var groupUser = new GroupUser {
    GroupUserAccessRight = "Admin",
    PrincipalType = "App",
    Identifier = ServicePrincipalId,
    Profile = new ServicePrincipalProfile {
        Id = profileId
    }
};

pbiClient.Groups.AddGroupUser(workspaceId, groupUser);

在 Power BI 服务的工作区的“访问”窗格中,可以确定哪些标识(包括安全主体)具有访问权限。

显示工作区访问窗格屏幕截图的屏幕截图。它显示具有管理员权限的显示名称为 Contoso 的服务主体配置文件。

删除服务主体配置文件

使用删除配置文件 REST API 操作删除服务主体配置文件。 此操作只能由父服务主体调用。

如果使用 Power BI .NET SDK 进行编程,可以使用 Profiles.DeleteProfile 方法。

检索所有服务主体配置文件

使用获取配置文件 REST API 操作检索属于调用服务主体的服务主体配置文件的列表。 此操作返回一个 JSON 有效负载,其中包含每个服务主体配置文件的 iddisplayName 属性。

如果使用 Power BI .NET SDK 进行编程,可以使用 Profiles.GetProfiles 方法。

通过使用服务主体配置文件执行 REST API 调用

使用服务主体配置文件进行 REST API 调用有两个要求:

  • 必须在“授权”标头中传递父服务主体的访问令牌。
  • 必须包含名为“X-PowerBI-profile-id”的标头,其中包含服务主体配置文件 ID 的值。

如果使用 Power BI .NET SDK,可以通过传入服务主体配置文件的 ID 显式设置 X-PowerBI-profile-id 标头值。

// Create the Power BI client
var tokenCredentials = new TokenCredentials(GetACcessToken(). "Bearer");
var uriPowerBiServiceApiRoot = new Uri(uriPowerBiServiceApiRoot);
var pbiClient = new PowerBIClient(uriPowerBiServiceApiRoot, tokenCredentials);

// Add X-PowerBI-profile-id header for service principal profile
string profileId = "11111111-1111-1111-1111-111111111111";
pbiClient.HttpClient.DefaultRequestHeaders.Add("X-PowerBI-profile-id", profileId);

// Retrieve workspaces by using the identity of service principal profile
var workspaces = pbiClient.Groups.GetGroups();

如上面的示例所示,将 X-PowerBI-profile-id 标头添加到 PowerBIClient 对象后,调用方法就很简单了,例如 Groups.GetGroups,它们将通过使用服务主体配置文件来执行。

PowerBIClient 对象设置 X-PowerBI-profile-id 标头有一种更方便的方法。 可以通过将配置文件的 ID 传递给构造函数来初始化对象。

// Create the Power BI client
string profileId = "11111111-1111-1111-1111-111111111111";

var tokenCredentials = new TokenCredentials(GetACcessToken(). "Bearer");
var uriPowerBiServiceApiRoot = new Uri(uriPowerBiServiceApiRoot);
var pbiClient = new PowerBiClient(uriPowerBiServiceApiRoot, tokenCredentials, profileId);

在编写多租户应用程序时,可能需要在作为父服务主体执行调用和作为服务主体配置文件执行调用之间切换。 管理上下文切换的有用方法是声明存储 PowerBIClient 对象的类级变量。 然后可以创建一个帮助程序方法,该方法使用正确的对象设置变量。

// Class-level variable that stores the PowerBIClient object
private PowerBIClient pbiClient;

// Helper method that sets the correct PowerBIClient object
private void SetCallingContext(string profileId = "") {

    if (profileId.Equals("")) {
        pbiClient = GetPowerBIClient();    
    }
    else {
        pbiClient = GetPowerBIClientForProfile(new Guid(profileId));
    }
}

需要创建或管理服务主体配置文件时,无需任何参数即可调用 SetCallingContext 方法。 这样,可以使用服务主体的标识来创建和管理配置文件。

// Always create and manage profiles as the service principal
SetCallingContext();

// Create a service principal profile
string profileName = "Contoso";

var createRequest = new CreateOrUpdateProfileRequest(profileName);
var profile = pbiClient.Profiles.CreateProfile(createRequest);

需要为新客户租户创建和设置工作区时,需要将该代码作为服务主体配置文件执行。 因此,应通过传入配置文件的 ID 来调用 SetCallingContext 方法。 这样,可以使用服务主体配置文件的标识来创建工作区。

// Always create and set up workspaces as a service principal profile
string profileId = "11111111-1111-1111-1111-111111111111";
SetCallingContext(profileId);

// Create a workspace
GroupCreationRequest request = new GroupCreationRequest(workspaceName);
Group workspace = pbiClient.Groups.CreateGroup(request);

使用特定服务主体配置文件创建和配置工作区后,应继续使用同一配置文件来创建和设置工作区内容。 无需调用 SetCallingContext 方法即可完成设置。

开发人员示例

建议下载名为 AppOwnsDataMultiTenant 的示例应用程序。

此示例应用程序是使用 .NET 6 和 ASP.NET 开发的,它演示了如何应用本文中所述的指南和建议。 可以查看代码,了解如何开发一个多租户应用程序,该应用程序使用服务主体配置文件实现“为客户嵌入内容”方案。

有关本文的详细信息,请参阅以下资源: