使用Pulumi分两步创建带系统分配托管标识的Azure ContainerApp时遭遇「无法创建已存在资源」错误的咨询
Pulumi分两步创建带系统分配托管标识的Azure ContainerApp时遭遇「无法创建已存在资源」错误的咨询
背景
我正在使用 Pulumi(Python)结合pulumi-azure-native和pulumi-azuread部署一个带有系统分配托管标识的Azure容器应用,该标识需要加入一个已有的Entra ID组(该组已拥有ACR的AcrPull权限和Key Vault的Key Vault Secrets User权限)。
由于托管标识的principal_id只有在容器应用创建后才存在,而ACR注册表配置要求标识在应用拉取私有镜像前已拥有AcrPull权限,我尝试采用三步法:
- 创建带有系统分配标识和公共引导镜像的容器应用(不配置ACR)。
- 将标识的服务主体加入已有的Entra组。
- 更新容器应用,配置私有镜像并设置
registries[].identity = "system"。
我将步骤1和3建模为两个独立的ContainerApp Pulumi资源,指向同一个Azure资源名称,最终资源设置了parent=bootstrap和depends_on=[membership]。
问题
pulumi up在第二个ContainerApp资源上失败,错误信息如下:
error: cannot create already existing resource '/subscriptions/xxx/resourceGroups/rg-demo/providers/Microsoft.App/containerApps/orchestrator-demo'
这是合理的:Pulumi试图创建orchestrator-final作为新资源,但Azure资源已由orchestrator-bootstrap创建,orchestrator-final资源从未进入状态。
代码
# __main__.py import pulumi import pulumi_azure_native as azure_native import pulumi_azuread as azuread from pulumi_azure_native.app import ( ConfigurationArgs, ContainerApp, ContainerArgs, ContainerResourcesArgs, ManagedServiceIdentityArgs, ManagedServiceIdentityType, RegistryCredentialsArgs, ScaleArgs, TemplateArgs, ) # Minimal repro goal: # - Existing Container App Environment and ACR # - New Container App with system-assigned managed identity # - Add that identity's service principal to an existing Entra group # - Use group-based AcrPull instead of a direct role assignment on the identity config = pulumi.Config() resource_group_name = config.require("resourceGroupName") location = config.require("location") container_app_env_name = config.require("containerAppEnvironmentName") container_app_name = config.get("containerAppName") entra_group_display_name = config.require("entraGroupDisplayName") acr_name = config.require("acrName") acr_resource_group_name = config.require("acrResourceGroupName") private_image = config.require("privateImage") bootstrap_image = ( config.get("bootstrapImage") or "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest" ) managed_environment = azure_native.app.get_managed_environment_output( environment_name=container_app_env_name, resource_group_name=resource_group_name, ) acr = azure_native.containerregistry.get_registry_output( registry_name=acr_name, resource_group_name=acr_resource_group_name, ) entra_group = azuread.get_group_output(display_name=entra_group_display_name) bootstrap = ContainerApp( "orchestrator-bootstrap", container_app_name=container_app_name, resource_group_name=resource_group_name, location=location, managed_environment_id=managed_environment.id, identity=ManagedServiceIdentityArgs(type=ManagedServiceIdentityType.SYSTEM_ASSIGNED), configuration=ConfigurationArgs( ingress=None, active_revisions_mode="Single", ), template=TemplateArgs( containers=[ ContainerArgs( name=container_app_name, image=bootstrap_image, resources=ContainerResourcesArgs(cpu=0.25, memory="0.5Gi"), ) ], scale=ScaleArgs(min_replicas=0, max_replicas=1), ), opts=pulumi.ResourceOptions( # The update resource below manages template/configuration. ignore_changes=["configuration", "template"], ), ) membership = azuread.GroupMember( "orchestrator-group-membership", group_object_id=entra_group.object_id, member_object_id=bootstrap.identity.principal_id, opts=pulumi.ResourceOptions( depends_on=[bootstrap], # First run can fail if identity propagation in Entra is not complete. custom_timeouts=pulumi.CustomTimeouts(create="10m"), ), ) app = ContainerApp( "orchestrator-final", container_app_name=container_app_name, resource_group_name=resource_group_name, location=location, managed_environment_id=managed_environment.id, identity=ManagedServiceIdentityArgs(type=ManagedServiceIdentityType.SYSTEM_ASSIGNED), configuration=ConfigurationArgs( ingress=None, active_revisions_mode="Single", registries=[ RegistryCredentialsArgs( server=acr.login_server, identity="system", ) ], ), template=TemplateArgs( containers=[ ContainerArgs( name=container_app_name, image=private_image, resources=ContainerResourcesArgs(cpu=0.25, memory="0.5Gi"), ) ], scale=ScaleArgs(min_replicas=1, max_replicas=1), ), opts=pulumi.ResourceOptions( parent=bootstrap, depends_on=[membership], ), ) pulumi.export("managedIdentityPrincipalId", app.identity.principal_id) pulumi.export("groupObjectId", entra_group.object_id) pulumi.export("acrLoginServer", acr.login_server)
错误信息
pulumi up命令失败,输出如下:
pulumi up View in Browser (Ctrl+O): https://app.pulumi.com/xxx/so-containerapp-mi-group/example/updates/2 Type Name Status Info pulumi:pulumi:Stack so-containerapp-mi-group-example **failed** 1 error + ├─ azure-native:app:ContainerApp orchestrator-bootstrap created (17s) + │ └─ azure-native:app:ContainerApp orchestrator-final **creating failed** 1 error + └─ azuread:index:GroupMember orchestrator-group-membership created (1s) Diagnostics: azure-native:app:ContainerApp (orchestrator-final): error: cannot create already existing resource '/subscriptions/xxx/resourceGroups/rg-demo/providers/Microsoft.App/containerApps/orchestrator-demo' pulumi:pulumi:Stack (so-containerapp-mi-group-example): error: update failed
错误后的栈详情:
pulumi stack Current stack is example: Owner: laurent-laporte-pro-org Last updated: 1 minute ago (2026-03-03 18:41:24.392413 +0100 CET) Pulumi version used: v3.224.0 Current stack resources (5): TYPE NAME pulumi:pulumi:Stack so-containerapp-mi-group-example ├─ azure-native:app:ContainerApp orchestrator-bootstrap ├─ azuread:index/groupMember:GroupMember orchestrator-group-membership ├─ pulumi:providers:azure-native default_3_13_0 └─ pulumi:providers:azuread default_6_8_1 Current stack outputs (0): No output values currently in this stack
已考虑的方案
- 使用单个
ContainerApp资源并依赖GroupMember——但我无法在应用创建前将标识加入组,也无法在标识拥有权限前配置ACR访问。 - 在最终资源上使用
import/pulumi.ResourceOptions(import_=...)——但这感觉不够优雅,我不确定这是正确的模式。
问题
- 这种双
ContainerApp资源模式(引导+最终)是否是正确的方法?如果是,我该如何告诉Pulumiorchestrator-final应该更新已存在的资源而非尝试创建它? - 或者,是否有更简洁的Pulumi原生模式来处理这种循环依赖(需要资源存在以获取其标识,需要标识拥有权限后资源才能使用它)?
环境
pulumi: v3.224.0pulumi-azure-native: 3.13.0pulumi-azuread: 6.8.1- Python 3.12
备注:内容来源于stack exchange,提问作者Laurent LAPORTE
相关产品推荐
相关产品推荐

