Я использую Pulumi (Python) с pulumi-azure-native и pulumi-azuread для развертывания приложения-контейнера Azure с назначаемым системой управляемым удостоверением, где это удостоверение должно быть членом существующей группы идентификаторов Entra (которая уже имеет AcrPull в ACR и секретах хранилища ключей). Пользователь в хранилище ключей).
Поскольку Principal_id управляемого удостоверения существует только после создания приложения-контейнера, а конфигурация реестра ACR требует, чтобы удостоверение уже имело AcrPull, прежде чем приложение сможет получить частный образ, я пробую трехэтапный подход:
- Создайте приложение-контейнер с помощью назначенное системой удостоверение и общедоступный загрузочный образ (без конфигурации ACR).
- Добавьте субъект-службу удостоверения в существующую группу Entra.
- Обновите приложение-контейнер, используя частный образ и реестры[].identity = "system".
Проблема
Код: Выделить всё
pulumi upКод: Выделить всё
error: cannot create already existing resource
'/subscriptions/xxx/resourceGroups/rg-demo/providers/Microsoft.App/containerApps/orchestrator-demo'
Код
Код: Выделить всё
# __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 до того, как удостоверение получит роль.
- Использование импорта / pulumi.ResourceOptions(import_=...) на последнем ресурсе — но это кажется хакерским, и я не уверен, что это правильный шаблон.
- Это два--шаблон ресурса (bootstrap + Final) правильный подход, и если да, то как мне сказать Pulumi, что оркестратор-final должен обновить существующий ресурс, а не пытаться его создать?
Код: Выделить всё
ContainerApp - В качестве альтернативы, существует ли более чистый шаблон Pulumi для обработки такого рода циклической зависимости (необходимо, чтобы ресурс существовал, чтобы получить его идентификатор, необходимо, чтобы идентификатор имел роль, прежде чем ресурс сможет его использовать)?
- : v3.224.0
Код: Выделить всё
pulumi - : 3.13.0
Код: Выделить всё
pulumi-azure-native - : 6.8.1
Код: Выделить всё
pulumi-azuread - Python 3.12
Подробнее здесь: https://stackoverflow.com/questions/799 ... -in-2-step