Automating Azure Container Apps Deployment with PowerShell: A Complete CI/CD Guide
Deploying containerized apps to the cloud sounds simple… until you’re juggling Docker builds, Azure registries, environments, networking, and deployment configs at 11 PM before a release 😅
That’s exactly where automation saves you.
In this guide, you’ll learn how to use a PowerShell deployment script that turns Azure Container Apps deployment into a single-command workflow — handling build, test, push, and deployment with minimal manual effort.
Whether this is your first cloud deployment or your hundredth, this setup keeps things fast, repeatable, and reliable.
What is Azure Container Apps?
Azure Container Apps (ACA) is a fully managed, serverless container platform. You run containers — Azure handles infrastructure.
It’s ideal for:
Web apps
APIs
Microservices
Background jobs
Why developers love it:
- No server management
- Auto-scaling based on demand
- HTTPS out of the box
- Pay-per-use pricing
You focus on code. Azure handles the rest.
What This Deployment Script Does
Instead of manually running 10+ commands, this script automates the entire pipeline:
Environment validation (Azure CLI, Docker, login checks)
Docker image build
Optional local container testing
Azure Container Registry (ACR) setup
Image push to ACR
Container Apps environment setup
Deployment or update of your app
It works for first-time deployments and updates to existing apps.
Prerequisites
Before running the script, make sure you have:
| Tool | Purpose |
|---|---|
| Azure Subscription | To deploy resources |
| Azure CLI | Resource management |
| Docker Desktop (Linux mode) | Container builds |
| PowerShell 5.1+ | Script execution |
| Git (optional) | Version control |
Azure resources needed
Resource Group (you create this)
Azure Container Registry (ACR) (script can create)
Container Apps Environment (script can create)
First-Time Deployment Walkthrough
Step 1 — Create a Resource Group
az group create --name "my-resource-group" --location "eastus"
Step 2 — Choose an ACR Name
Your registry name must be:
5–50 characters
Alphanumeric only
Globally unique
Step 3 — Run the Deployment Script
.\deploy-azure.ps1 `
-ResourceGroup "my-resource-group" `
-ContainerName "sample-react-vite-app" `
-AcrName "myacr" `
-Environment "production" `
-Location "eastus"
Step 4 — Follow Prompts
The script will:
✔ Check Azure login
✔ Build Docker image
✔ Offer local testing
✔ Create/validate ACR
✔ Push image
✔ Deploy to Container Apps
Script Parameters Explained
| Parameter | Required | Description |
|---|---|---|
| ResourceGroup | ✅ | Azure Resource Group |
| ContainerName | ✅ | Name of Container App |
| AcrName | ✅ | Registry name (strict format) |
| Environment | ❌ | development / staging / production |
| Location | ❌ | Azure region |
| Port | ❌ | App listening port |
| DnsName | ❌ | Custom domain |
What Happens Behind the Scenes
Build Stage (Multi-Stage Docker)
Node builder compiles the React app
Lightweight nginx image serves it
Final image is small and optimized
Registry Management
Validates ACR naming rules
Creates registry if missing
Authenticates securely
Tags images:
production-latestproduction-20260204-150138
Deployment Logic
Detects if app exists
Creates new OR updates existing
Automatically pulls ACR credentials
Troubleshooting Quick Fixes
| Problem | Fix |
|---|---|
| Docker build fails | Start Docker Desktop |
| ACR login fails | az acr login --name your-acr |
| Invalid registry name | Use only alphanumeric |
| Deployment failed | Check resource group + permissions |
| Assets 404 | Rebuild React app |
After Deployment
You’ll get:
App name
Image path
Deployment status
Public HTTPS URL
Open the URL — Azure handles the SSL certificate automatically 🔒
Monitoring & Scaling
View logs
az containerapp logs show --name app --resource-group rg --follow
Scale replicas
az containerapp update --name app --resource-group rg --min-replicas 1 --max-replicas 3
Best Practices
✔ Use environment-specific builds
✔ Test locally before cloud deploy
✔ Use meaningful container names
✔ Keep images small (multi-stage builds)
✔ Monitor logs post-deploy
✔ Re-run script for updates
✔ Use versioned image tags for rollback
Advanced Scenarios
Multi-environment deployments (dev/staging/prod)
Custom DNS setup
Multi-region deployment
Dockerfile Strategy
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build:${BUILD_MODE}
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Result: small, fast, production-ready image.
Cost Tips
Start with minimal CPU/memory
Scale only when needed
Monitor usage
Use environment-specific settings
Conclusion
This PowerShell automation turns Azure deployment into:
- Faster releases
- Fewer manual errors
- Repeatable process
- More developer focus on code
From multi-step chaos → single-command deployment.
Next Steps
Install prerequisites
Create resource group
Pick ACR name
Run script
Open app URL
Set up monitoring
Happy deploying!
Final Script:
# Azure Container Apps Deployment Script
# This script builds and deploys the Sample React Vite App to Azure Container Apps
param(
[Parameter(Mandatory=$true)]
[string]$ResourceGroup,
[Parameter(Mandatory=$true)]
[string]$ContainerName,
[Parameter(Mandatory=$true)]
[string]$AcrName,
[Parameter(Mandatory=$false)]
[ValidateSet("development", "staging", "production")]
[string]$Environment = "production",
[Parameter(Mandatory=$false)]
[string]$Location = "eastus",
[Parameter(Mandatory=$false)]
[int]$Port = 80,
[Parameter(Mandatory=$false)]
[string]$DnsName = $null
)
# Set error action preference
$ErrorActionPreference = "Stop"
# Validate ACR name
if ($AcrName -notmatch '^[a-zA-Z0-9]{5,50}$') {
Write-Host "ERROR: Invalid ACR name '$AcrName'" -ForegroundColor Red
Write-Host "ACR names must:" -ForegroundColor Yellow
Write-Host " - Contain only alphanumeric characters (no hyphens, underscores, or special characters)" -ForegroundColor Yellow
Write-Host " - Be between 5 and 50 characters long" -ForegroundColor Yellow
Write-Host "" -ForegroundColor Yellow
Write-Host "Example valid names: myacr123, myacr, contoso2024" -ForegroundColor Cyan
exit 1
}
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Azure Container Deployment Script" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# Variables
$ImageName = "sample-react-vite-app"
$ImageTag = "$Environment-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
$FullImageName = "$AcrName.azurecr.io/${ImageName}:${ImageTag}"
$LatestImageName = "$AcrName.azurecr.io/${ImageName}:${Environment}-latest"
# Step 1: Check Azure CLI installation
Write-Host "Step 1: Checking Azure CLI..." -ForegroundColor Yellow
$azCheck = Get-Command az -ErrorAction SilentlyContinue
if (-not $azCheck) {
Write-Host "Azure CLI is not installed. Please install it from https://aka.ms/InstallAzureCLI" -ForegroundColor Red
exit 1
}
Write-Host "Azure CLI is installed" -ForegroundColor Green
# Step 2: Check if logged in to Azure
Write-Host "`nStep 2: Checking Azure login..." -ForegroundColor Yellow
$account = az account show 2>$null
if (-not $account) {
Write-Host "Not logged in to Azure. Logging in..." -ForegroundColor Yellow
az login
} else {
Write-Host "Already logged in to Azure" -ForegroundColor Green
}
# Step 3: Build Docker image
Write-Host "`nStep 3: Building Docker image..." -ForegroundColor Yellow
Write-Host "Image: $FullImageName" -ForegroundColor Cyan
docker build --build-arg BUILD_MODE=$Environment -t ${ImageName}:${ImageTag} -t ${ImageName}:${Environment}-latest .
if ($LASTEXITCODE -ne 0) {
Write-Host "Docker build failed" -ForegroundColor Red
exit 1
}
Write-Host "Docker image built successfully" -ForegroundColor Green
# Step 4: Test Docker image locally (optional)
Write-Host "`nStep 4: Testing Docker image locally..." -ForegroundColor Yellow
$testResponse = Read-Host "Do you want to test the image locally before deployment? (y/n)"
if ($testResponse -eq 'y') {
Write-Host "Starting container on port 3000..." -ForegroundColor Cyan
docker run -d -p 3000:80 --name ${ImageName}-test ${ImageName}:${ImageTag}
Write-Host "Container started. Visit http://localhost:3000 to test" -ForegroundColor Green
Write-Host "Press Enter to continue with deployment (this will stop the test container)..."
Read-Host
docker stop ${ImageName}-test
docker rm ${ImageName}-test
}
# Step 5: Check and create Azure Container Registry if needed
Write-Host "`nStep 5: Checking Azure Container Registry..." -ForegroundColor Yellow
$acrExists = az acr show --name $AcrName --resource-group $ResourceGroup 2>$null
if (-not $acrExists) {
Write-Host "ACR '$AcrName' does not exist. Creating..." -ForegroundColor Yellow
az acr create --name $AcrName --resource-group $ResourceGroup --sku Basic --location $Location
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to create ACR" -ForegroundColor Red
exit 1
}
Write-Host "ACR created successfully" -ForegroundColor Green
} else {
Write-Host "ACR '$AcrName' exists" -ForegroundColor Green
}
# Step 6: Login to Azure Container Registry
Write-Host "`nStep 6: Logging in to Azure Container Registry..." -ForegroundColor Yellow
az acr login --name $AcrName
if ($LASTEXITCODE -ne 0) {
Write-Host "ACR login failed. Ensure Docker Desktop is running and you have permissions to the ACR." -ForegroundColor Red
exit 1
}
Write-Host "Logged in to ACR" -ForegroundColor Green
# Step 7: Tag and push image to ACR
Write-Host "`nStep 7: Pushing image to Azure Container Registry..." -ForegroundColor Yellow
docker tag ${ImageName}:${ImageTag} $FullImageName
docker tag ${ImageName}:${ImageTag} $LatestImageName
docker push $FullImageName
docker push $LatestImageName
if ($LASTEXITCODE -ne 0) {
Write-Host "Docker push failed" -ForegroundColor Red
exit 1
}
Write-Host "Image pushed to ACR" -ForegroundColor Green
# Step 7: Create or update Azure Container Apps Environment
Write-Host "`nStep 8: Setting up Container Apps Environment..." -ForegroundColor Yellow
$EnvName = "commonservices-app-env"
# Check if environment exists
Write-Host "Checking if Container Apps Environment exists..." -ForegroundColor Cyan
$null = az containerapp env show --name $EnvName --resource-group $ResourceGroup 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Host "Creating Container Apps Environment..." -ForegroundColor Cyan
az containerapp env create `
--name $EnvName `
--resource-group $ResourceGroup `
--location $Location
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to create Container Apps Environment" -ForegroundColor Red
exit 1
}
Write-Host "Container Apps Environment created" -ForegroundColor Green
} else {
Write-Host "Container Apps Environment already exists" -ForegroundColor Green
}
# Step 8: Deploy to Azure Container Apps
Write-Host "`nStep 9: Deploying to Azure Container Apps..." -ForegroundColor Yellow
# Get ACR credentials
$acrPassword = az acr credential show --name $AcrName --query "passwords[0].value" -o tsv
# Check if container app exists
Write-Host "Checking if Container App exists..." -ForegroundColor Cyan
$appListJson = az containerapp list --resource-group $ResourceGroup --output json 2>$null
if ($appListJson) {
$apps = $appListJson | ConvertFrom-Json
$existingApp = $apps | Where-Object { $_.name -eq $ContainerName }
} else {
$existingApp = $null
}
if ($existingApp) {
Write-Host "Container App exists. Updating..." -ForegroundColor Cyan
# Update registry credentials first
az containerapp registry set `
--name $ContainerName `
--resource-group $ResourceGroup `
--server "$AcrName.azurecr.io" `
--username $AcrName `
--password $acrPassword
# Then update the image
az containerapp update `
--name $ContainerName `
--resource-group $ResourceGroup `
--image $FullImageName
} else {
Write-Host "Container App does not exist. Creating new..." -ForegroundColor Cyan
az containerapp create `
--name $ContainerName `
--resource-group $ResourceGroup `
--environment $EnvName `
--image $FullImageName `
--target-port 80 `
--ingress external `
--registry-server "$AcrName.azurecr.io" `
--registry-username $AcrName `
--registry-password $acrPassword `
--cpu 0.5 `
--memory 1.0Gi
}
if ($LASTEXITCODE -ne 0) {
Write-Host "Container App deployment failed" -ForegroundColor Red
exit 1
}
Write-Host "Container App deployed successfully" -ForegroundColor Green
# Step 9: Get container app details
Write-Host "`nStep 10: Retrieving container app details..." -ForegroundColor Yellow
$appInfo = az containerapp show --name $ContainerName --resource-group $ResourceGroup --output json | ConvertFrom-Json
Write-Host "`n========================================" -ForegroundColor Cyan
Write-Host "Deployment Complete!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "Container App Name: $ContainerName" -ForegroundColor White
Write-Host "Image: $FullImageName" -ForegroundColor White
Write-Host "Status: $($appInfo.properties.provisioningState)" -ForegroundColor White
Write-Host "HTTPS URL: https://$($appInfo.properties.configuration.ingress.fqdn)" -ForegroundColor Yellow
Write-Host ""
Write-Host "Note: Container Apps provides automatic HTTPS!" -ForegroundColor Green
Write-Host "`nUseful commands:" -ForegroundColor Cyan
Write-Host " View logs: az containerapp logs show --name $ContainerName --resource-group $ResourceGroup --follow" -ForegroundColor Gray
Write-Host " Scale: az containerapp update --name $ContainerName --resource-group $ResourceGroup --min-replicas 1 --max-replicas 3" -ForegroundColor Gray
Write-Host " Delete: az containerapp delete --name $ContainerName --resource-group $ResourceGroup --yes" -ForegroundColor Gray
Write-Host " Restart: az containerapp revision restart --name $ContainerName --resource-group $ResourceGroup" -ForegroundColor Gray
Write-Host ""
No comments:
Post a Comment