Bicep
Define a Container Apps environment and app as code, deploy 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.
- Azure CLI (
az) installed and signed in withaz login. - The Bicep CLI, which ships with the Azure CLI (
az bicep installif it isn't already present).
What you can do
The Microsoft.App/containerApps resource defines a running app declaratively. In a single template 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 Bicep reference.
Core objects
A minimal deployment has two resources: an environment that hosts apps, and the app itself.
| Object | Purpose |
|---|---|
Microsoft.App/managedEnvironments | The boundary that hosts one or more apps and jobs, and provides shared networking and logging. |
Microsoft.App/containerApps | The app. Its 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.
@description('Region for all resources.')
param location string = resourceGroup().location
param environmentName string = 'my-express-env'
param appName string = 'my-express-app'
// Express environment — the simplest, fastest-to-provision Container Apps environment.
resource environment 'Microsoft.App/managedEnvironments@2026-01-01' = {
name: environmentName
location: location
kind: 'Express' // provisions an Express environment
properties: {}
}
resource app 'Microsoft.App/containerApps@2026-01-01' = {
name: appName
location: location
properties: {
environmentId: 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: json('0.25')
memory: '0.5Gi'
}
}
]
scale: {
minReplicas: 0 // scale to zero when there's no traffic
maxReplicas: 4
}
}
}
}
output appUrl string = 'https://${app.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 the template, and it scales on HTTP concurrency.
param location string = resourceGroup().location
param environmentName string = 'my-env'
param appName string = 'my-api'
@description('Login server of your registry, e.g. myregistry.azurecr.io')
param acrLoginServer string
@description('Full image reference, e.g. myregistry.azurecr.io/my-api:1.0.0')
param image string
resource environment 'Microsoft.App/managedEnvironments@2026-01-01' = {
name: environmentName
location: location
properties: {}
}
resource app 'Microsoft.App/containerApps@2026-01-01' = {
name: appName
location: location
identity: {
type: 'SystemAssigned' // grant this identity the AcrPull role on your registry
}
properties: {
environmentId: environment.id
configuration: {
ingress: {
external: true
targetPort: 8080
}
registries: [
{
server: acrLoginServer
identity: 'system' // pull with the managed identity — no secrets
}
]
}
template: {
containers: [
{
name: 'main'
image: image
resources: {
cpu: json('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 Microsoft.Authorization/roleAssignments resource in the same template.
Deploy the template
Save either example as main.bicep, then deploy it to a resource group.
- Bash
- PowerShell
az group create --name my-rg --location eastus
# Example 1
az deployment group create --resource-group my-rg --template-file main.bicep
# Example 2 — pass your registry and image
az deployment group create --resource-group my-rg --template-file main.bicep \
--parameters acrLoginServer=myregistry.azurecr.io image=myregistry.azurecr.io/my-api:1.0.0
az group create --name my-rg --location eastus
# Example 1
az deployment group create --resource-group my-rg --template-file main.bicep
# Example 2 — pass your registry and image
az deployment group create --resource-group my-rg --template-file main.bicep `
--parameters acrLoginServer=myregistry.azurecr.io image=myregistry.azurecr.io/my-api:1.0.0
Learn more
Microsoft.App/containerAppsBicep reference — every property, with ARM and Terraform variants.Microsoft.App/managedEnvironmentsreference — environment options like Log Analytics and VNet.- Azure Container Apps overview — concepts behind the resources above.