In this blog I am going to show how to deploy Container App and pull an image from Azure Container Registry using user assigned managed identity for authentication. I will be achieving this using Terraform and AzAPI.
Image below shows a diagram of what I will be deploying; it shows container app accessing a container registry using a user created identity.

Terraform
The terraform will create:
- Resource Group
- Log Analytics Workspace
- Container App Environment
- Container App
- User created identity
- Assign IAM permissions to an already created container registry for the user created identity
- Assign user created identity to the container App
- Successfully deploy container app with image stored in container registry
Resource Group
resource "azurerm_resource_group" "rg" {
name = "${var.aca_name}-rg"
location = var.location
}
Log Analytics Workspace
resource "azurerm_log_analytics_workspace" "loganalytics" {
name = "${var.aca_name}-la"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "PerGB2018"
retention_in_days = 30
}
Data resources associated with already created Container Registry
data "azurerm_resource_group" "acr" {
name = "tamops-acr-github"
}
data "azurerm_container_registry" "acr" {
name = "tamopsactionacr"
resource_group_name = data.azurerm_resource_group.acr.name
}
Container App Environment deployed using AzAPI and Log Analytics workspace associated to send logs
resource "azapi_resource" "containerapp_environment" {
type = "Microsoft.App/managedEnvironments@2022-03-01"
name = "${var.aca_name}env"
parent_id = azurerm_resource_group.rg.id
location = azurerm_resource_group.rg.location
body = jsonencode({
properties = {
appLogsConfiguration = {
destination = "log-analytics"
logAnalyticsConfiguration = {
customerId = azurerm_log_analytics_workspace.loganalytics.workspace_id
sharedKey = azurerm_log_analytics_workspace.loganalytics.primary_shared_key
}
}
}
})
}
Create user assigned identity and associated IAM role assignment
resource "azurerm_user_assigned_identity" "containerapp" {
location = azurerm_resource_group.rg.location
name = "containerappmi"
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_role_assignment" "containerapp" {
scope = data.azurerm_resource_group.acr.id
role_definition_name = "acrpull"
principal_id = azurerm_user_assigned_identity.containerapp.principal_id
depends_on = [
azurerm_user_assigned_identity.containerapp
]
}
Create Azure Container App using AzAPI, notice:
- Highlighted lines 8-11: Showing assignment of user assigned identity created above
- Highlighted lines 21-26: Showing registry being assigned to the container registry
- Highlighted line 31: Showing image assigned from container registry
resource "azapi_resource" "containerapp" {
type = "Microsoft.App/containerapps@2022-03-01"
name = var.aca_name
parent_id = azurerm_resource_group.rg.id
location = azurerm_resource_group.rg.location
identity {
type = "SystemAssigned, UserAssigned"
identity_ids = [azurerm_user_assigned_identity.containerapp.id]
}
body = jsonencode({
properties = {
managedEnvironmentId = azapi_resource.containerapp_environment.id
configuration = {
ingress = {
external : true,
targetPort : 80
},
"registries" : [
{
"server" : data.azurerm_container_registry.acr.login_server,
"identity" : azurerm_user_assigned_identity.containerapp.id
}
]
}
template = {
containers = [
{
image = "${data.azurerm_container_registry.acr.login_server}/aspcoresample:76ef8d9511d310649729a28563fdf6d133338e30",
name = "firstcontainerappacracr"
resources = {
cpu = 0.25
memory = "0.5Gi"
},
"probes" : [
{
"type" : "Liveness",
"httpGet" : {
"path" : "/",
"port" : 80,
"scheme" : "HTTP"
},
"periodSeconds" : 10
},
{
"type" : "Readiness",
"httpGet" : {
"path" : "/",
"port" : 80,
"scheme" : "HTTP"
},
"periodSeconds" : 10
},
{
"type" : "Startup",
"httpGet" : {
"path" : "/",
"port" : 80,
"scheme" : "HTTP"
},
"periodSeconds" : 10
}
]
}
]
scale = {
minReplicas = 0,
maxReplicas = 2
}
}
}
})
ignore_missing_property = true
depends_on = [
azapi_resource.containerapp_environment
]
}
The terraform files here – have been setup to run locally, with running a successful plan
terraform init
terraform plan
Terraform Plan output:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azapi_resource.containerapp will be created
+ resource "azapi_resource" "containerapp" {
+ body = (known after apply)
+ id = (known after apply)
+ ignore_casing = false
+ ignore_missing_property = true
+ location = "uksouth"
+ name = "tamopsacablog"
+ output = (known after apply)
+ parent_id = (known after apply)
+ schema_validation_enabled = true
+ tags = (known after apply)
+ type = "Microsoft.App/containerapps@2022-03-01"
+ identity {
+ identity_ids = (known after apply)
+ principal_id = (known after apply)
+ tenant_id = (known after apply)
+ type = "SystemAssigned, UserAssigned"
}
}
# azapi_resource.containerapp_environment will be created
+ resource "azapi_resource" "containerapp_environment" {
+ body = (sensitive)
+ id = (known after apply)
+ ignore_casing = false
+ ignore_missing_property = true
+ location = "uksouth"
+ name = "tamopsacablogenv"
+ output = (known after apply)
+ parent_id = (known after apply)
+ schema_validation_enabled = true
+ tags = (known after apply)
+ type = "Microsoft.App/managedEnvironments@2022-03-01"
+ identity {
+ identity_ids = (known after apply)
+ principal_id = (known after apply)
+ tenant_id = (known after apply)
+ type = (known after apply)
}
}
# azurerm_log_analytics_workspace.loganalytics will be created
+ resource "azurerm_log_analytics_workspace" "loganalytics" {
+ daily_quota_gb = -1
+ id = (known after apply)
+ internet_ingestion_enabled = true
+ internet_query_enabled = true
+ location = "uksouth"
+ name = "tamopsacablog-la"
+ primary_shared_key = (sensitive value)
+ reservation_capacity_in_gb_per_day = (known after apply)
+ resource_group_name = "tamopsacablog-rg"
+ retention_in_days = 30
+ secondary_shared_key = (sensitive value)
+ sku = "PerGB2018"
+ workspace_id = (known after apply)
}
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "uksouth"
+ name = "tamopsacablog-rg"
}
# azurerm_role_assignment.containerapp will be created
+ resource "azurerm_role_assignment" "containerapp" {
+ id = (known after apply)
+ name = (known after apply)
+ principal_id = (known after apply)
+ principal_type = (known after apply)
+ role_definition_id = (known after apply)
+ role_definition_name = "acrpull"
+ scope = "/subscriptions/04109105-f3ca-44ac-a3a7-66b4936112c3/resourceGroups/tamops-acr-github"
+ skip_service_principal_aad_check = (known after apply)
}
# azurerm_user_assigned_identity.containerapp will be created
+ resource "azurerm_user_assigned_identity" "containerapp" {
+ client_id = (known after apply)
+ id = (known after apply)
+ location = "uksouth"
+ name = "containerappmi"
+ principal_id = (known after apply)
+ resource_group_name = "tamopsacablog-rg"
+ tenant_id = (known after apply)
}
Plan: 6 to add, 0 to change, 0 to destroy.
Running terraform apply, we can review the deployed resources in Azure
terraform apply

Reviewing container app identities, we can see the user assigned identity successfully

Reviewing the container app URL, we can see the container image has been deployed successfully!

Awesome! Successfully deployed Container App and pulled an image from Azure Container Registry using user assigned managed identity for authentication.
Very Helpful, thank you Thomas!
Glad you found it helpful – thanks for the feedback!!