Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Prepare Azure environments for new workloads—subscriptions, networking, identity, and landing zones
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/services/container-apps/terraform.md
1# Container Apps Terraform Patterns23> **⚠️ Container Registry Naming:** ACR names must be alphanumeric only (5-50 characters). Use Terraform's `replace()` function when constructing the value passed to `azurecaf_name`, or otherwise strip hyphens manually.45> **⚠️ Two-Phase Deployment (Mandatory):** To avoid the chicken-and-egg problem where Terraform tries to create a Container App referencing an ACR image that doesn't exist yet:6> - **Phase 1 (`terraform apply`):** Deploy ACR and Container App with a **public placeholder image** and **no `registry` block**.7> - **Phase 2 (post-apply CLI):** Build/push the app image to ACR, configure the registry/identity link, then update the Container App image.8>9> This mirrors the [Bicep two-phase pattern](bicep.md). Without it, `terraform apply` fails with `ContainerAppOperationError` because the image doesn't exist in ACR yet.1011> **⚠️ ACR Authentication — Managed Identity Only:** Do **not** use `admin_enabled = true` on `azurerm_container_registry` or add a `registry` block with `username`/`password_secret_name` to the Container App. Admin credentials are a security risk and leak secrets into Terraform state. Always use **managed identity** with an `AcrPull` role assignment, and configure the registry link via `az containerapp registry set --identity system` in Phase 2.1213## Phase 1: Container App Resource (No Registry Block)1415```hcl16# Placeholder image allows provisioning before the app image exists in ACR.17# No registry block in Terraform during Phase 1 — the registry/identity link is18# configured via CLI after provisioning (see Phase 2 below).19# Do NOT add a registry block with username/password_secret_name — use managed identity.20resource "azurerm_container_app" "api" {21name = azurecaf_name.container_app.result22container_app_environment_id = azurerm_container_app_environment.env.id23resource_group_name = azurerm_resource_group.rg.name24revision_mode = "Single"2526identity {27type = "SystemAssigned"28}2930tags = merge(var.tags, {31"azd-service-name" = "api"32})3334template {35min_replicas = 136max_replicas = 33738container {39name = "api"40image = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"41cpu = 0.2542memory = "0.5Gi"43}44}4546ingress {47external_enabled = true48target_port = 80804950traffic_weight {51latest_revision = true52percentage = 10053}54}5556# Phase 2 updates the image and configures the registry/identity link via CLI.57# Prevent Terraform from reverting those changes on subsequent applies.58# Do NOT add a registry block here — use `az containerapp registry set --identity system`.59lifecycle {60ignore_changes = [61template[0].container[0].image,62registry,63]64}65}66```6768## AcrPull Role Assignment6970Deploy the `AcrPull` role assignment as a **separate resource** that depends on the Container App (to read its system-assigned identity principal ID). Neither the ACR nor the Container App depends on this resource, so there is no circular dependency.7172```hcl73resource "azurerm_role_assignment" "api_acr_pull" {74scope = azurerm_container_registry.acr.id75role_definition_name = "AcrPull"76principal_id = azurerm_container_app.api.identity[0].principal_id77principal_type = "ServicePrincipal"78}79```8081> 💡 **Tip:** Always set `principal_type = "ServicePrincipal"` for managed identities. This skips the Graph API lookup and speeds up role assignment propagation.8283## Phase 2: Post-Apply Deployment (CLI)8485After `terraform apply` succeeds, run these commands to build the real image and switch the Container App to it:8687```bash88ACR_NAME=$(terraform output -raw acr_name)89ACR_SERVER=$(terraform output -raw acr_login_server)90APP_NAME=$(terraform output -raw container_app_name)91RG_NAME=$(terraform output -raw resource_group_name)9293# 1. Build and push the application image to ACR94az acr build --registry $ACR_NAME --image myapp:latest ./src/api9596# 2. Configure the registry/identity link (managed identity, no passwords)97az containerapp registry set \98--name $APP_NAME \99--resource-group $RG_NAME \100--server $ACR_SERVER \101--identity system102103# 3. Update the Container App to use the real image104az containerapp update \105--name $APP_NAME \106--resource-group $RG_NAME \107--image $ACR_SERVER/myapp:latest108```109110**PowerShell:**111```powershell112$AcrName = terraform output -raw acr_name113$AcrServer = terraform output -raw acr_login_server114$AppName = terraform output -raw container_app_name115$RgName = terraform output -raw resource_group_name116117# 1. Build and push the application image to ACR118az acr build --registry $AcrName --image myapp:latest ./src/api119120# 2. Configure the registry/identity link (managed identity, no passwords)121az containerapp registry set `122--name $AppName `123--resource-group $RgName `124--server $AcrServer `125--identity system126127# 3. Update the Container App to use the real image128az containerapp update `129--name $AppName `130--resource-group $RgName `131--image "$AcrServer/myapp:latest"132```133134> ⚠️ **Warning:** Step 2 requires the `AcrPull` role assignment to have propagated (1–5 minutes after `terraform apply`). If the image pull fails, wait and retry. See the **azure-deploy** skill's `references/pre-deploy-checklist.md` (Container Apps + ACR pre-deploy RBAC health check).135136## Terraform Outputs137138Export the values needed by Phase 2:139140```hcl141output "acr_name" {142value = azurerm_container_registry.acr.name143}144145output "acr_login_server" {146value = azurerm_container_registry.acr.login_server147}148149output "container_app_name" {150value = azurerm_container_app.api.name151}152153output "resource_group_name" {154value = azurerm_resource_group.rg.name155}156157output "api_url" {158value = "https://${azurerm_container_app.api.ingress[0].fqdn}"159}160```161162## Container Apps Environment163164```hcl165resource "azurerm_container_app_environment" "env" {166name = azurecaf_name.container_app_env.result167location = azurerm_resource_group.rg.location168resource_group_name = azurerm_resource_group.rg.name169log_analytics_workspace_id = azurerm_log_analytics_workspace.logs.id170}171```172