Automating Azure Container App Deployment with Terraform and ACR Integration

Computer Scientist/ Cloud Engineer/DevOps Engineer / Technical writer
Prerequisites
Azure account (active)
Docker Desktop installed
Python installed
VS code installed (with Azure account and Azure CLI extensions installed)
terraform installed
Before You Get Started
Clone the starter repository. to your local environment
Create folder for your project: mkdir -p
Navigate into it: cd
Clone the repo: git clone
Confirm you can see lab1: ls
Navigate to lab1 and work from there: cd lab1
Run the application locally to confirm it works. Check the repo for instructions on how to run it.
Follow instruction on readme to run without docker
Validation:
Homepage:
Health check:
- press
ctrl+cto proceed
Step 1: Modify the Application and Run Locally
Update the application using environment variables so that the homepage (/) displays:
Your Full Name
Cloud Platform → Azure
Environment → test
Version → v1.0.0
Status → healthy
For the environment variables, use export at run time before running
uvicorn app.main:app --reloadFor exampleexport ENGINEER_NAME="Alice Wonderland" export CLOUD_PLATFORM="Azure" export Environment="test"Validation:
- Homepage:
Health check:

Step 2: Containerize the Application
Write or update the Dockerfile if necessary
Ensure the application runs on a defined port
Start Docker Desktop and ensure it is running
Build and run the container locally: see repo for instruction
Validation
- Image builds successfully
- Container runs correctly
Step 3: Create Needed infrastructure using Terraform
For this project, the following Azure infrastructures should be created first before the deployment of the container app.
Resource Group
Azure Container Registry
Virtual Network
Subnet
Log Analytics Workspace
Container Apps Environment
Create terraform manifest file
- Create a terraform directory in the root directory and create two terraform manifest files: one for the dependency infrastructure and the other for container app
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.65.0"
}
}
}
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "rg" {
name = "lab1-RG"
location = "westus"
}
resource "azurerm_container_registry" "acr" {
name = "lab1acr689"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
sku = "Basic"
admin_enabled = true
}
resource "azurerm_virtual_network" "vnet" {
name = "lab1-vnet"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "subnet" {
name = "containerapps-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.0.0/23"]
delegation {
name = "containerapps-delegation"
service_delegation {
name = "Microsoft.App/environments"
}
}
}
resource "azurerm_log_analytics_workspace" "loganalytic" {
name = "lab1-loganalytic"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "PerGB2018"
retention_in_days = 30
}
resource "azurerm_container_app_environment" "env" {
name = "lab1-containerapp-env"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
log_analytics_workspace_id = azurerm_log_analytics_workspace.loganalytic.id
infrastructure_subnet_id = azurerm_subnet.subnet.id
}
Authenticate to Azure with your account (use work or school account option), run:
az loginInitialize terraform, plan and apply: run:
terraform init
terraform plan
terraform apply
- Authenticate to Azure Container Registry (ACR), Tag and push your image to ACR: run
az acr login --name lab1acr689docker tag lab1-starter-app lab1acr689.azurecr.io/lab1-starter-app:v1.0.0docker push lab1acr689.azurecr.io/lab1-starter-app:v1.0.0
Validation
- Ensure ACR exists
Run: az acr show --name lab1acr689 --output table
- Ensure Image exists in ACR: run
az acr repository list --name lab1acr689 --output table
- Ensure Image tag matches version: run
az acr repository show-tags \
--name lab1acr689 \
--repository lab1-starter-app \
--output table
- On the portal
Step 4: Deploy Container App using terraform
Create the second manifest file ( main.tf ) file and add your terraform code to deploy an azure container app.
You could create your own manifest file or use the template below- run:
nanomain.tf. In your configuration, ensure:Image is from ACR
External ingress is enabled
Target port matches container
Environment variables
Scaling settings
Template for main.tf
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.65.0"
}
}
}
provider "azurerm" {
features {}
}
# Existing resources
data "azurerm_resource_group" "rg" {
name = "lab1-RG"
}
data "azurerm_container_registry" "acr" {
name = "lab1acr689"
resource_group_name = "lab1-RG"
}
data "azurerm_container_app_environment" "env" {
name = "lab1-containerapp-env"
resource_group_name = "lab1-RG"
}
# ✅ Create User Assigned Identity FIRST
resource "azurerm_user_assigned_identity" "uai" {
name = "lab1-containerapp-identity"
resource_group_name = data.azurerm_resource_group.rg.name
location = data.azurerm_resource_group.rg.location
}
# ✅ Assign AcrPull BEFORE app creation
resource "azurerm_role_assignment" "acr_pull" {
scope = data.azurerm_container_registry.acr.id
role_definition_name = "AcrPull"
principal_id = azurerm_user_assigned_identity.uai.principal_id
}
# ✅ Container App uses the identity
resource "azurerm_container_app" "app" {
name = "lab1-container-app"
resource_group_name = data.azurerm_resource_group.rg.name
container_app_environment_id = data.azurerm_container_app_environment.env.id
revision_mode = "Single"
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.uai.id]
}
template {
container {
name = "lab1-starter-app"
image = "${data.azurerm_container_registry.acr.login_server}/lab1-starter-app:v1.0.0"
cpu = 0.25
memory = "0.5Gi"
env {
name = "ENGINEER_NAME"
value = "Celestina Odili"
}
env {
name = "CLOUD_PLATFORM"
value = "Azure"
}
env {
name = "APP_NAME"
value = "Cloud Lab Starter App"
}
env {
name = "ENVIRONMENT"
value = "test"
}
env {
name = "APP_VERSION"
value = "v1.0.0"
}
env {
name = "APP_STATUS"
value = "healthy"
}
}
min_replicas = 1
max_replicas = 3
}
ingress {
external_enabled = true
target_port = 8000
traffic_weight {
latest_revision = true
percentage = 100
}
}
registry {
server = data.azurerm_container_registry.acr.login_server
identity = azurerm_user_assigned_identity.uai.id
}
depends_on = [
azurerm_role_assignment.acr_pull
]
}
- Initialize, plan and apply Terraform
Terraform initTerraform planTerraform apply
Validation
Confirm that Container App is running and Public URL is available:
on the portal
run the command shown
az containerapp show
-- name lab1-container-app \
--resource-group lab1-RG \
--query "properties.provisioningState" \
az containerapp show
--name <APP_NAME>
--resource-group <RESOURCE_GROUP>
--query "properties.provisioningState"
A revision is active:
on the portal
Page loads successfully
- All resources are created successfully and Environment is active
Challenges and Resolutions
During this project, I encountered both development and infrastructure-related challenges that required troubleshooting and adjustments.
On the development side, I initially had issues creating and activating a Python virtual environment. Using python3 -m venv .venv and source .venv/bin/activate did not work in my Windows environment. I resolved this by switching to python -m venv .venv and activating it with source .venv/Scripts/activate, which is the correct approach for Windows systems.
On the infrastructure side, I faced repeated deployment issues when using a single Terraform configuration file for all resources. These issues included unintended resource recreation, destroy conflicts, and situations where the Azure Container App deployed successfully but failed to pull the container image from Azure Container Registry (ACR). To address this, I restructured the Terraform setup into two stages: one for provisioning core infrastructure (Resource Group, ACR, networking, and Container Apps environment) and another dedicated to deploying the Container App itself. This separation reduced dependency conflicts, improved deployment reliability, and better reflected a production-style DevOps workflow.
Additionally, I observed that if the container image could not be pulled from ACR, the Container App would still be created but would fail to start. This reinforced the importance of ensuring correct image tagging, availability, and authentication, as the application cannot run unless Azure successfully retrieves the container image.
Conclusion
This project highlights how Azure Container Apps simplifies application deployment while still requiring a clear understanding of how its components interact. From routing traffic through Azure’s ingress to ensuring the container port aligns perfectly with the application’s listening port, every configuration detail plays a critical role in making the application accessible and reliable.
The challenges encountered—ranging from local environment setup issues to Terraform deployment conflicts—reinforced the importance of choosing the right tools and structuring infrastructure thoughtfully. Refactoring the Terraform configuration into separate stages not only resolved deployment errors but also introduced a more maintainable, production-aligned workflow.
Additionally, the experience underscored key operational concepts such as the dependency on container images being successfully pulled from Azure Container Registry and the flexibility provided by environment variables in managing application configuration without modifying code.
Overall, while the setup works effectively, there is clear room for improvement in a production scenario. Enhancing security using Azure Key Vault can also be used in place of managed identity. Also, implementing a robust CI/CD pipeline, would ensure the system is more scalable and automated. This project serves as a strong foundation for building more resilient cloud-native applications using modern DevOps practices.



