在 Azure DevOps 中使用服务主体和托管标识
Azure DevOps Services
注意
Azure Active Directory 现已更名为 Microsoft Entra ID。 有关详细信息,请参阅 Azure AD 的新名称。
将 Microsoft Entra 服务主体和托管标识作为应用程序标识添加到 Azure DevOps Services 组织中,以授予他们访问组织资源的权限。 对于许多团队,当你对公司中支持自动化工作流的应用程序进行身份验证时,此功能可以是个人访问令牌(PAT)的可行首选替代方法。
关于服务主体和托管标识
服务主体 是Microsoft Entra 应用程序中的安全对象,用于定义应用程序在给定租户中可以执行的操作。 它们是在应用程序注册过程中在 Azure 门户中设置的,并配置为访问 Azure 资源,例如 Azure DevOps。 通过将服务主体添加到组织并在其上设置权限,我们可以确定服务主体是否有权访问组织资源以及哪些资源。
托管标识 是另一个Microsoft Entra 功能,其作用类似于应用程序的服务主体。 这些对象为 Azure 资源提供标识,并允许支持 Microsoft Entra 身份验证的服务轻松共享凭据。 它们是一个有吸引力的选项,因为Microsoft Entra ID 负责凭据管理和轮换。 虽然在 Azure 门户中设置托管标识可能看起来不同,但 Azure DevOps 会将这两个安全对象视为组织中具有定义权限的新应用程序标识。 在本文的其余部分,除非另有说明,否则我们会将托管标识和服务主体互换为服务主体。
使用以下步骤向 Azure DevOps 对这些标识进行身份验证,以允许他们代表自己执行操作。
配置托管标识和服务主体
实现可能有所不同,但在高级别上,以下步骤可帮助你开始在工作流中使用服务主体。 请考虑查看我们 示例应用之一,继续操作。
1.创建新的托管标识或应用程序服务主体
选项 1:创建应用程序服务主体
创建新的应用程序注册时,会在 Microsoft Entra ID 中创建应用程序对象。 应用程序服务主体是给定租户的此应用程序对象的表示形式。 将应用程序注册为多租户应用程序时,有一个唯一的服务主体对象表示应用程序添加到的每个租户的应用程序对象。
其他信息:
选项 2:创建托管标识
在 Azure 门户中创建托管标识与使用服务主体设置应用程序有很大不同。 在开始创建过程之前,必须先考虑要创建的托管标识类型:
- 系统分配的托管标识: 某些 Azure 服务允许直接在服务实例上启用托管标识。 启用系统分配的托管标识时,会在 Microsoft Entra ID 中创建标识。 此标识与该服务实例的生命周期相关联。 当资源被删除时,Azure 会自动为你删除标识。 按照设计,只有该 Azure 资源可使用此标识从 Microsoft Entra ID 请求令牌。
- 用户分配的托管标识 还可以通过创建用户分配的托管标识并将其分配给 Azure 服务的一个或多个实例,以独立 Azure 资源的形式创建托管标识。 对于用户分配的托管标识,标识与使用它的资源分开管理。
有关详细信息,请参阅以下文章和视频:
2.将服务主体添加到 Azure DevOps 组织
在 Microsoft Entra 管理中心配置服务主体后,必须在 Azure DevOps 中通过向组织添加服务主体来执行相同的操作。 可以通过 “用户”页 或使用 ServicePrincipalEntitlements API 添加它们。 由于用户无法以交互方式登录,因此可将用户添加到组织、项目或团队的用户帐户必须添加他们。 启用“允许团队和项目管理员邀请新用户”策略时,此类用户包括项目集合管理员 (PCA) 或项目管理员和团队管理员。
提示
若要将服务主体添加到组织,请输入应用程序或托管标识的显示名称。 如果选择通过 ServicePrincipalEntitlements
API 以编程方式添加服务主体,请确保传入 服务主体的对象 ID ,而不是应用程序的对象 ID。
如果你是 PCA,还可以向服务主体授予对特定项目的访问权限并分配许可证。 如果你不是 PCA,则必须联系 PCA 以更新任何项目成员身份或许可证访问级别。
注意
只能为组织连接到的租户添加托管标识或服务主体。 可以将服务主体设置为多租户,以同时访问多个租户。 托管标识只能属于单个租户。 若要访问其他租户中的托管标识,请参阅 常见问题解答中的解决方法。
3.设置服务主体的权限
将服务主体添加到组织后,可以像对待标准用户帐户一样对待它们。 可以直接对服务主体分配权限,将其添加到安全组和团队,将其分配到任何访问级别,并将其从组织中删除。 还可以使用 Service Principal Graph APIs
对服务主体执行 CRUD 操作。
设置这些权限可能与在 Microsoft Entra 应用程序中为其他 Azure 资源设置应用程序权限的方式不同。 Azure DevOps 不依赖于通过Azure 门户的应用程序注册可用的“应用程序权限”设置。 这些应用权限将权限应用于绑定到租户的所有组织中的服务主体,并且不了解 Azure DevOps 中可用的组织、项目或对象权限。 为了为服务主体提供更精细的权限,我们依赖于自己的权限模型,而不是 Entra ID。
4. 管理服务主体
管理服务主体与用户帐户在以下主要方面有所不同:
- 服务主体没有电子邮件,因此无法通过电子邮件邀请他们加入组织。
- 许可的组规则当前不适用于服务主体。 如果要将访问级别分配给服务主体,最好直接执行此操作。
- 可将服务主体添加到 Microsoft Entra 组(在 Azure 门户中)。 目前存在技术限制,使我们无法在 Microsoft Entra 组成员列表中显示它们。 此限制不适用于 Azure DevOps 组。 也就是说,服务主体仍继承在他们所属的Microsoft Entra 组之上设置的任何组权限。
- Microsoft Entra 组中的用户不会仅因为管理员创建了一个组并将 Microsoft Entra 组添加到其中而立即成为 Azure DevOps 组织的一部分。 我们有一个名为“具体化”的过程,一旦用户从Microsoft Entra 组首次登录到组织,就会发生这种情况。 登录组织的用户允许我们确定应向哪些用户授予许可证。 由于服务主体无法登录,因此管理员必须将其显式添加到组织,如前所述。
- 无法在 Azure DevOps 上修改服务主体的显示名称或头像。
- 服务主体在被添加到每个组织中时都会获取许可证,即使选择了多组织计费。
5. 获取 Microsoft Entra ID 令牌
(a) 以编程方式获取 Microsoft Entra ID 令牌
可以通过遵循 Microsoft Entra ID 文档来完成为托管标识获取访问令牌。 请参阅服务主体和托管标识的示例。
返回的访问令牌是具有已定义角色的 JSON Web 令牌 (JWT),可用于使用令牌作为持有者来访问组织资源。
(b) 使用 Azure CLI 获取 Microsoft Entra ID 令牌
对于临时操作,通过 Azure CLI 获取一次性 Microsoft Entra ID 令牌可能更容易。 对于不需要定期轮换永久性令牌的操作(如 API 调用或 git 克隆操作),这种方法是首选方法。
先决条件
- Azure 租户 ID 和订阅 ID:请确保订阅与连接到尝试访问的 Azure DevOps 组织的租户相关联。 如果不知道租户或订阅 ID,可以在 Azure 门户中找到它。
- Azure 应用客户端 ID 和客户端机密
- Azure CLI
这些说明由 Databricks 文档提供,可以在 它们的页面找到更多详细信息。
- 使用
az devops login
命令以服务主体身份登录到 Azure CLI。 - 按照屏幕上的说明操作并完成登录。
# To authenticate a service principal with a password or cert:
az login --service-principal -u <app-id> -p <password-or-cert> --tenant <tenant>
# To authenticate a managed identity:
az login --identity
- 通过输入以下命令为已登录的服务主体设置正确的订阅:
az account set -s <subscription-id>
- 使用
az account get-access-token
Azure DevOps 资源 ID499b84ac-1321-427f-aa17-267ca6975798
生成 Microsoft Entra ID 访问令牌:
$accessToken = az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query "accessToken" --output tsv
- 现在,可以按常规使用
az cli
命令。 让我们尝试通过将Bearer
令牌放入标头中来调用 Azure DevOps API:
$apiVersion = "7.1-preview.1"
$uri = "https://dev.azure.com/${yourOrgname}/_apis/projects?api-version=${apiVersion}"
$headers = @{
Accept = "application/json"
Authorization = "Bearer $accessToken"
}
Invoke-RestMethod -Uri $uri -Headers $headers -Method Get | Select-Object -ExpandProperty value ` | Select-Object id, name
注意
使用 Azure DevOps 应用程序 ID(而不是资源 URI)生成令牌。
6. 使用 Microsoft Entra ID 令牌对 Azure DevOps 资源进行身份验证
在以下视频示例中,我们从使用 PAT 进行身份验证转向使用服务主体中的令牌。 我们首先使用客户端密码进行身份验证,然后转到使用客户端证书。
另一个示例演示了如何在 Azure 函数中使用用户分配的托管标识连接到 Azure DevOps。
按照这些示例,在 示例应用集合中查找应用代码。
除了进行 Azure DevOps REST API 调用以外,还可以在这些文档中找到一些使用服务主体进行身份验证的常见方案:
- 使用 Nuget.exe 或 dotnet将服务主体连接到 NuGet 源。
- 使用服务主体命令行将扩展发布到 Visual Studio Marketplace。
- 在 Azure 管道中创建无密钥服务连接,由服务主体或托管标识支持。
- 将服务主体与 Git 凭据管理器配合使用克隆存储库
服务主体与用户有何不同?
- 无法在 Azure DevOps 上修改服务主体的显示名称或头像。
- 即使选择了多组织计费,服务主体也算作它添加到的每个组织的许可证。
- 服务主体不能组织所有者或创建组织。
- 服务主体无法创建令牌,例如 个人访问令牌 (PAC) 或 SSH 密钥。 他们可以生成自己的Microsoft Entra ID 令牌,这些令牌可用于调用 Azure DevOps REST API。
- 不支持服务主体的 Azure DevOps OAuth 。
常见问题
常规
问:为什么应使用服务主体或托管标识而不是 PAT?
答:我们的许多客户寻求服务主体或托管标识来替换现有的 PAT (个人访问令牌) 。 此类 PAC 通常属于服务帐户 (共享团队帐户) ,该帐户使用它们通过 Azure DevOps 资源对应用程序进行身份验证。 PAC 必须经常 (至少 180 天) 轮换一次。 如果不当存储,PAT 可能会被不当使用,并且其寿命通常较长。 Microsoft Entra 令牌每小时过期一次,从而降低泄露时的整体风险系数。 对于常见的 PAT 方案,我们 分享一些示例,了解如何尝试改用 Microsoft Entra 令牌。
无法使用服务主体创建个人访问令牌。
问:服务主体和托管标识的速率限制是什么?
答:服务主体和托管标识的 速率限制 与用户相同。
问:使用此功能的成本会更高吗?
答:根据访问级别,服务主体和托管标识的定价与用户类似。 一个值得注意的变化涉及我们如何对待服务主体的“多组织计费”。 无论用户加入多少个组织,用户都只能算作一个许可证。 服务主体按用户所属的每个组织计为一个许可证。 此方案类似于标准“基于用户分配的计费”。
问:是否可以将不同租户中的托管标识添加到我的组织?
答:只能从组织连接到的同一租户添加托管标识。 但是,我们提供了一种解决方法,可用于在“资源租户”中设置托管标识,其中所有资源都位于其中。 然后,可以在组织连接的“目标租户”中启用服务主体使用它。 执行以下步骤作为解决方法:
- 在资源租户的 Azure 门户 中创建用户分配的托管标识。
- 将其连接到 虚拟机,并向其分配此托管标识 。
- 创建 密钥保管库 并生成 证书 (类型不能为“PEM”) 。 生成此证书时,还会生成同名的机密,我们稍后会用到该机密。
- 授予对托管标识的访问权限,以便它可以从密钥保管库读取私钥。 使用“获取/列表”权限在密钥保管库中创建访问策略(在“机密权限”下)并在“选择主体”下搜索托管标识。
- 下载“CER”格式的已创建证书,确保它不包含证书的专用部分。
- 在目标租户中创建新的应用程序注册。
- 在“证书和机密”选项卡中将下载的证书上传到此新应用程序。
- 将此应用程序的服务主体添加到 Azure DevOps 组织,我们希望它能够访问并记住使用任何所需权限设置服务主体。
- 从该服务主体获取 Microsoft Entra 访问令牌,该服务主体使用托管标识证书,以下是代码示例:
注意
始终定期轮换证书。
public static async Task<string> GetSecret(string keyVaultName, string secretName)
{
var keyVaultUri = new Uri("https://" + keyVaultName + ".vault.azure.net");
var client = new SecretClient(keyVaultUri, new ManagedIdentityCredential());
var keyVaultSecret = await client.GetSecretAsync(secretName);
var secret = keyVaultSecret.Value;
return secret.Value;
}
private static async Task<AuthenticationResult> GetAppRegistrationAADAccessToken(string applicationClientID, string appTenantId)
{
IConfidentialClientApplication app;
byte[] privateKeyBytes = Convert.FromBase64String(GetSecret(keyVaultName, secretName));
X509Certificate2 certificateWithPrivateKey = new X509Certificate2(privateKeyBytes, (string)null, X509KeyStorageFlags.MachineKeySet);
app = ConfidentialClientApplicationBuilder.Create(applicationClientID)
.WithCertificate(certificateWithPrivateKey)
.WithAuthority(new Uri(string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", appTenantId)))
.Build();
app.AddInMemoryTokenCache();
string AdoAppClientID = "499b84ac-1321-427f-aa17-267ca6975798/.default";
string[] scopes = new string[] { AdoAppClientID };
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
return result;
}
潜在错误
名称或标识符为 '{repoName
}' 的 Git 存储库不存在,或者你没有正在尝试执行的操作的权限。
确保服务主体至少拥有访问存储库的“基本”许可证。 “利益干系人”许可证是不够的。
未能创建对象 ID 为“{provided objectId
}”的服务主体
连接到组织的租户中没有服务主体 provided objectId
。 一个常见原因是传入应用注册的对象 ID,而不是其服务主体的对象 ID。 请记住,服务主体是表示给定租户的应用程序的对象,它不是应用程序本身。
service principal object ID
可以在租户的“企业应用程序”页中找到 。 搜索应用程序的名称,然后选择返回的“企业应用程序”结果。 此结果是服务主体/企业应用程序的页面,可以使用此页上的对象 ID 在 Azure DevOps 中创建服务主体。
拒绝访问:{ID of the caller identity
} 需要对资源用户执行以下操作的权限:添加用户
此错误可能是由于以下原因之一造成的:
- 你不是组织的所有者、项目集合管理员或项目或团队管理员。
- 你是项目或团队管理员,但禁用策略“允许团队和项目管理员邀请新用户”。
- 你是可以邀请新用户的项目或团队管理员,但在邀请新用户时尝试分配许可证。 不允许项目或团队管理员将许可证分配给新用户。 任何新受邀用户将添加到 新用户的默认访问级别。 请联系 PCA 以更改许可证访问级别。
即使我们知道组织中存在 Azure AD 服务主体,Azure DevOps Graph 列表 API 仍然返回空列表。
即使仍有更多用户要返回的页面,Azure DevOps Graph 列表 API 也可能返回空列表。 continuationToken
使用 循环访问列表,最终可以找到返回服务主体的页面。 continuationToken
如果返回 ,则意味着可以通过 API 获取更多结果。 虽然我们计划改进此逻辑,但目前可能第一个 X 结果返回空。
TF401444:在 Web 浏览器中至少以 {tenantId
'tenantId\
servicePrincipalObjectId'} 身份登录一次,以便访问该服务。
如果未邀请服务主体加入组织,则可能遇到以下错误。 确保将服务主体添加到相应的组织,并具有访问任何所需资源所需的所有权限。