当你在一个 Azure 租户中托管私有 Bicep 模块注册表,并希望在面向另一租户的部署中使用这些模块时,就需要让执行部署的标识能够向源租户中的 Azure 容器注册表 (ACR) 进行身份验证。 Azure Lighthouse 允许你向来自一个租户的主体授予对另一个租户中资源的访问权限,从而无需维护单独的凭据即可解决此问题。
本文介绍如何执行以下操作:
- 理解为何跨租户登记访问需要Lighthouse
- 创建并部署一个Lighthouse委派,赋予部署身份AcrPull权限
- 配置一个从 跨租户注册表部署的 Azure DevOps 管道
为什么Lighthouse解决了跨租户登记访问问题
当 Bicep 编译引用存储在注册表中的模块的文件(例如,br:myregistry.azurecr.io/modules/storage:v1),它会在部署前恢复该模块。 恢复步骤会根据 credentialPrecedence 中的 ,使用你正在运行的工具(Azure CLI 或 Azure PowerShell)的凭据向 ACR 进行身份验证。
当部署目标位于租户 B 中,而注册表位于租户 A 中时,用于部署的标识需要:
- 租户 A 中 ACR 的 AcrPull 权限(用于在编译期间还原模块)
- 租户B中的部署权限(用于部署编译后的资源)
单个服务主体不能在两个无关租户之间原生地担任角色。 Azure Lighthouse 通过将租户 B 中的主体映射到租户 A,解决了这一问题,因此你可以将租户 A 中的角色分配给该主体,而无需在租户 A 的目录中管理该主体。
Azure Lighthouse委托的工作原理
灯塔代表团由两种资源组成:
| Resource | Scope | Purpose |
|---|---|---|
Microsoft.ManagedServices/registrationDefinitions |
Subscription | 定义服务提供内容:哪个租户负责管理哪些内容,以及应授予哪些角色 |
Microsoft.ManagedServices/registrationAssignments |
资源组 | 将委派应用于特定的资源组 |
你把两个资源都部署到租 户A,也就是拥有ACR的租户。 该委托将 租户 B 中的主体授予对包含 ACR 的资源组的 AcrPull 角色。
先决条件
- 位于租户 A中的一个 ACR,用于托管你的 Bicep 模块。
- 租户 B中用于执行部署的服务主体或托管标识(例如 Azure DevOps 服务连接)。
- 租户 B目录中主体的对象 ID。 Lighthouse 从管理租户(租户 B)获取主体 ID,并将其映射到租户 A。
- 在租户 A 中 ACR 所在的订阅上具有 参与者 或 所有者 权限,或者具有包含
Microsoft.Authorization/roleAssignments/write、Microsoft.Authorization/roleAssignments/delete和Microsoft.Authorization/roleAssignments/read的自定义角色。
步骤1:将灯塔代表团部署到租户A处
代表团需要两份Bicep文件。 在租户 A 中于订阅范围内部署主文件。分配模块以你的 ACR 所在的资源组为目标。
主代表团档案
targetScope = 'subscription'
@description('A unique name for this Lighthouse offer.')
param mspOfferName string = 'bicepAcrDelegation'
@description('A description for this Lighthouse offer.')
param mspOfferDescription string = 'Grants AcrPull to the deploying tenant for Bicep module restore.'
@description('The tenant ID of the managing tenant (tenant B - the deploying tenant).')
param managedByTenantId string // Enter the tenant B GUID
@description('Principals in tenant B to receive AcrPull in tenant A.')
param authorizations array // Example: [{principalId:'<object-id-in-tenant-B>', roleDefinitionId:'7f951dda-4ed3-4680-a7ca-43fe172d538d', principalIdDisplayName:'Bicep registry deployer'}]
@description('The resource group in tenant A that contains the ACR.')
param rgName string = 'bicep-registry-rg'
var registrationName = guid(mspOfferName, 'definition')
var assignmentName = guid(mspOfferName, 'assignment')
resource acrDelegationDefinition 'Microsoft.ManagedServices/registrationDefinitions@2022-10-01' = {
name: registrationName
properties: {
registrationDefinitionName: mspOfferName
description: mspOfferDescription
managedByTenantId: managedByTenantId
authorizations: authorizations
}
}
module acrDelegationAssignment './acrDelegationAssignment.bicep' = {
name: assignmentName
scope: resourceGroup(rgName)
params: {
acrDefinitionResourceId: acrDelegationDefinition.id
assignmentName: assignmentName
}
}
output mspOfferName string = 'Managed by ${mspOfferName}'
output authorizations array = authorizations
分配模块(acrDelegationAssignment.bicep)
将此文件与主文件一起保存为 acrDelegationAssignment.bicep:
targetScope = 'resourceGroup'
param acrDefinitionResourceId string
param assignmentName string
resource acrDelegationAssignment 'Microsoft.ManagedServices/registrationAssignments@2022-10-01' = {
name: assignmentName
properties: {
registrationDefinitionId: acrDefinitionResourceId
}
}
部署到租户A。
部署账户必须在租户A的订阅上拥有相应权限。通过Azure CLI向租户A认证,然后在订阅范围内部署。 分配模块会自动将其作用域缩小到资源组:
az cloud set -n AzureChinaCloud
az login --tenant <tenant-A-id>
az deployment sub create \
--location chinanorth3 \
--subscription <tenant-A-subscription-id> \
--template-file main.bicep \
--parameters managedByTenantId='<tenant-B-id>' \
authorizations='[{"principalId":"<object-id-from-tenant-B>","roleDefinitionId":"7f951dda-4ed3-4680-a7ca-43fe172d538d","principalIdDisplayName":"Bicep registry deployer"}]'
你只需要部署这个代表团一次。 一旦安装好,后续模块部署时你就不需要再动租户A了。
步骤2:配置 bicepconfig.json
在你的 Bicep 项目中,配置bicepconfig.json以定义注册表别名并设置凭证提供者。 当你在管道中使用 Azure CLI 时,设置credentialPrecedence为["AzureCLI"]:
{
"moduleAliases": {
"br": {
"RegistryAlias": {
"registry": "myregistry.azurecr.io",
"modulePath": "bicep/modules"
}
}
},
"cloud": {
"credentialPrecedence": [
"AzureCLI"
]
}
}
在这种配置下,Bicep 在还原模块时使用 Azure CLI 令牌缓存,这也是你的管道中 az deployment 命令使用的同一个令牌。 由于Lighthouse委派将AcrPull授予租户B的服务主体,该令牌现在可以访问租户A的ACR。
步骤3:配置您的Azure DevOps流水线
同时使用针对 租户B 的服务连接来完成这两个任务。 租户 A 无需单独登录,Lighthouse 委派会以透明方式处理跨租户访问。
trigger: none
pool:
vmImage: ubuntu-latest
jobs:
- job: Deploy
steps:
- task: AzureCLI@2
displayName: Upgrade Bicep CLI
inputs:
azureSubscription: 'my-tenant-b-service-connection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: az bicep upgrade
- task: AzureCLI@2
displayName: Deploy Bicep template
inputs:
azureSubscription: 'my-tenant-b-service-connection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az deployment group create \
--name $(Build.BuildNumber) \
--resource-group my-target-rg \
--template-file main.bicep
az deployment group create 命令:
- 编译
main.bicep,通过使用 Azure CLI 令牌(由 Lighthouse 委派允许)从租户 A 的 ACR 触发模块恢复。 - 将生成的ARM模板部署到租户B的资源组
验证委派是否正常工作
如果灯塔委派未到位或被撤销,你可能会遇到类似错误:
ERROR: /home/runner/work/main.bicep(6,13) : Error BCP192: Unable to restore the module with reference
"br:myregistry.azurecr.io/bicep/modules/storage:v1": Unhandled exception:
Azure.RequestFailedException: Service request failed.
这个错误意味着编译过程中使用的令牌注册表上没有 AcrPull。 请确认:
- 灯塔注册定义和分配已成功部署到租户A。
- 委派中的
principalId与你的管道所使用的服务连接的服务主体对象 ID 匹配。 -
credentialPrecedence中的bicepconfig.json包括"AzureCLI"(如果你使用 PowerShell,则为"AzurePowerShell")。