Terraform
Define a Container Apps environment and app as code with the AzAPI provider, apply it with a single command, and reach for the full resource reference when you need more.
- An Azure subscription with permission to create resource groups.
- Terraform installed, and Azure CLI (
az) signed in withaz login(the AzAPI provider uses that context). - The AzAPI provider, which maps directly to the Azure resource schema, so every property below matches the ARM/Bicep reference one-to-one.
What you can do
The Microsoft.App/containerApps resource defines a running app declaratively. In a single azapi_resource block you can:
- Pin the container image, CPU, and memory, and add init containers, environment variables, probes, and volume mounts.
- Publish an ingress endpoint — external (public) or internal — with a target port, transport, CORS, IP restrictions, and revision traffic splitting.
- Authenticate to private registries and reference secrets, including values pulled from Key Vault via managed identity.
- Control scaling with min/max replicas and KEDA rules, including scale-to-zero.
- Attach a managed identity so the app reaches Azure services without credentials in code.
For the full property list, see the Microsoft.App/containerApps AzAPI reference.
Core objects
A minimal deployment has two resources: an environment that hosts apps, and the app itself.
| Object | Purpose |
|---|---|
azapi_resource (Microsoft.App/managedEnvironments) | The boundary that hosts one or more apps and jobs, and provides shared networking and logging. |
azapi_resource (Microsoft.App/containerApps) | The app. Its body.properties split into configuration and template. |
properties.environmentId | Links the app to its environment. |
properties.configuration | Non-versioned settings shared across revisions: ingress, registries, secrets, and activeRevisionsMode. |
properties.template | The versioned app definition: containers, scale, and volumes. A change here creates a new revision. |
configuration.ingress | The endpoint — external, targetPort, transport, and traffic weights. |
template.scale | minReplicas, maxReplicas, and KEDA rules (set minReplicas = 0 to scale to zero). |
Example 1 — Express app with ingress enabled
The basic setup is an Express environment (kind = "Express") hosting an app that runs a public image, publishes a public HTTPS endpoint, and scales to zero when idle. External ingress makes it reachable from the internet, and outbound egress is allowed by default.
terraform {
required_providers {
azapi = {
source = "Azure/azapi"
}
}
}
provider "azapi" {}
variable "location" {
type = string
default = "eastus"
}
resource "azapi_resource" "rg" {
type = "Microsoft.Resources/resourceGroups@2021-04-01"
name = "my-rg"
location = var.location
}
# Express environment — the simplest, fastest-to-provision Container Apps environment.
resource "azapi_resource" "environment" {
type = "Microsoft.App/managedEnvironments@2026-01-01"
parent_id = azapi_resource.rg.id
name = "my-express-env"
location = var.location
body = {
kind = "Express" # provisions an Express environment
properties = {}
}
}
resource "azapi_resource" "app" {
type = "Microsoft.App/containerApps@2026-01-01"
parent_id = azapi_resource.rg.id
name = "my-express-app"
location = var.location
body = {
properties = {
environmentId = azapi_resource.environment.id
configuration = {
ingress = {
external = true # public HTTPS endpoint
targetPort = 80
transport = "auto"
}
}
template = {
containers = [
{
name = "main"
image = "mcr.microsoft.com/k8se/quickstart:latest"
resources = {
cpu = 0.25
memory = "0.5Gi"
}
}
]
scale = {
minReplicas = 0 # scale to zero when there's no traffic
maxReplicas = 4
}
}
}
}
response_export_values = ["properties.configuration.ingress.fqdn"]
}
output "app_url" {
value = "https://${azapi_resource.app.output.properties.configuration.ingress.fqdn}"
}
Example 2 — App that pulls from Azure Container Registry
A typical app runs your own image from a private registry. It authenticates to ACR with a system-assigned managed identity, so there are no registry passwords in state, and it scales on HTTP concurrency.
variable "acr_login_server" {
type = string
description = "Login server of your registry, e.g. myregistry.azurecr.io"
}
variable "image" {
type = string
description = "Full image reference, e.g. myregistry.azurecr.io/my-api:1.0.0"
}
resource "azapi_resource" "environment" {
type = "Microsoft.App/managedEnvironments@2026-01-01"
parent_id = azapi_resource.rg.id
name = "my-env"
location = var.location
body = {
properties = {}
}
}
resource "azapi_resource" "app" {
type = "Microsoft.App/containerApps@2026-01-01"
parent_id = azapi_resource.rg.id
name = "my-api"
location = var.location
identity {
type = "SystemAssigned" # grant this identity the AcrPull role on your registry
}
body = {
properties = {
environmentId = azapi_resource.environment.id
configuration = {
ingress = {
external = true
targetPort = 8080
}
registries = [
{
server = var.acr_login_server
identity = "system" # pull with the managed identity — no secrets
}
]
}
template = {
containers = [
{
name = "main"
image = var.image
resources = {
cpu = 0.5
memory = "1Gi"
}
}
]
scale = {
minReplicas = 1
maxReplicas = 10
rules = [
{
name = "http-scale"
http = {
metadata = {
concurrentRequests = "100"
}
}
}
]
}
}
}
}
}
The app's managed identity needs the AcrPull role on the registry before the first pull succeeds. Assign it with az role assignment create or an azapi_resource of type Microsoft.Authorization/roleAssignments in the same configuration.
Deploy the configuration
Save either example as main.tf, then initialize and apply the configuration.
- Bash
- PowerShell
az login
terraform init
terraform apply
# Example 2 — pass your registry and image
terraform apply \
-var="acr_login_server=myregistry.azurecr.io" \
-var="image=myregistry.azurecr.io/my-api:1.0.0"
az login
terraform init
terraform apply
# Example 2 — pass your registry and image
terraform apply `
-var="acr_login_server=myregistry.azurecr.io" `
-var="image=myregistry.azurecr.io/my-api:1.0.0"
Learn more
Microsoft.App/containerAppsAzAPI reference — every property, with Bicep and ARM variants.Microsoft.App/managedEnvironmentsreference — environment options like Log Analytics and VNet.- AzAPI provider documentation — how
azapi_resourcemaps to Azure resource types.