Appearance
Container Registry (ACR)
Each Microtec ERP environment has a dedicated Azure Container Registry (ACR) for storing Docker images. The ACR stores images for all 13 backend services plus the custom Keycloak image. Container Apps pull images from ACR using a user-assigned managed identity — no registry credentials are needed.
Naming Convention
ACR names must be globally unique, 5–50 characters, alphanumeric only (no hyphens). The formula:
micerpbe{env}acr| Environment | ACR Name | Resource Group |
|---|---|---|
| dev | micerpbedevacr | mic-erp-be-dev-global-rg |
| stage | micerpbestageacr | mic-erp-be-stage-global-rg |
| preprod | micerpbepreprodacr | mic-erp-be-preprod-global-rg |
| uat | micerpbeuatacr | mic-erp-be-uat-global-rg |
| production | micerpbeprdacr | mic-erp-be-production-global-rg |
Naming Formula in Bicep
bicep
var acrName = replace('${resourcePrefix}${environment}acr', '-', '')
// resourcePrefix = 'micerpbe'
// environment = 'dev' | 'stage' | 'preprod' | 'uat' | 'prd' ← production uses 'prd'
// Result: 'micerpbedevacr', 'micerpbepreprodacr', 'micerpbeprdacr', etc.Production Uses 'prd', Not 'production'
The production ACR name is micerpbeprdacr (using the short prd token), NOT micerpbeprodacr or micerpbeproductionacr. This matches the envShort convention used in all alphanumeric resource names.
SKU
| Environment | SKU | Geo-Replication |
|---|---|---|
| dev | Basic | No |
| stage | Standard | No |
| preprod | Standard | No |
| uat | Standard | No |
| production | Premium | Yes (UK South → Saudi) |
The Premium SKU in production enables private endpoints (ACR is not publicly accessible) and geo-replication for DR coverage.
Image Repository Structure
All backend service images follow a consistent repository path:
{acrName}.azurecr.io/
├── gateway/ gateway.api
├── accounting/ accounting service
├── hr/ hr service
├── finance/ finance service
├── sales/ sales service
├── purchase/ purchase service
├── inventory/ inventory service
├── distribution/ distribution service
├── fixed-assets/ fixed assets service
├── notification/ notification service
├── workflow/ workflow service
├── attachment/ attachment service
└── keycloak/ custom keycloak image (with SPIs + themes)Image Tagging Convention
Tags encode the branch/version and build ID:
| Environment | Tag Pattern | Example |
|---|---|---|
| dev | {branch-name}-{build-id} | feature-invoice-fix-1042 |
| stage | {branch-name}-{build-id} | stage-1055 |
| preprod | {version}-rc{n} | 1.5.0-rc2 |
| uat | {version}-rc{n} | 1.5.0-rc2 |
| production | {version} | 1.5.0 |
Additionally, the latest tag always points to the most recently deployed image in each environment:
bash
# After successful deploy, also tag as latest
docker tag {image}:{specific-tag} {image}:latest
az acr import ... --forceNever Deploy latest in Production
The latest tag is useful for debugging but must never be used as the deployment tag in production. Always use the semantic version tag (1.5.0). Using latest makes rollbacks ambiguous.
Managed Identity Pull
Container Apps pull images from ACR using a user-assigned managed identity. No registry username or password is required.
Managed Identity Assignment
bicep
// managed-identity.bicep
resource acrRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(acr.id, managedIdentity.id, 'AcrPull')
scope: acr
properties: {
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions',
'7f951dda-4ed3-4680-a7ca-43fe172d538d' // AcrPull built-in role
)
principalId: managedIdentity.properties.principalId
principalType: 'ServicePrincipal'
}
}Container App Registry Configuration
bicep
// In containerApp.bicep
registries: [
{
server: '${acrName}.azurecr.io'
identity: managedIdentityId // No username/password needed
}
]Pipeline: Build and Push
Images are built in the Azure DevOps pipeline using the build-and-push.yml template. See Build Docker for full pipeline YAML.
Summary:
ACR Tasks (Image Patching)
For base image security patches, ACR Tasks can rebuild images when the base image is updated:
bash
# Create an ACR task that triggers on mcr.microsoft.com/dotnet/aspnet update
az acr task create \
--registry micerpbedevacr \
--name patch-base-images \
--image gateway:{{.Run.ID}} \
--context https://github.com/microtec/erp.git#main:Platforms/Src/Gateway \
--file Dockerfile \
--base-image-trigger-enabled true \
--base-image-trigger-type RuntimeACR Tasks Are Supplemental
ACR Tasks are used for emergency base-image patching only. Routine rebuilds go through the full Azure DevOps pipeline (which includes SAST, Trivy, and approval gates).
Image Retention Policy
Old images are cleaned up automatically:
bicep
resource acrPolicy 'Microsoft.ContainerRegistry/registries/agentPools@2023-01-01-preview' = { ... }
// Retention: keep last 10 tags per repository, delete after 30 days
// Configured via:
az acr config retention update \
--registry {acrName} \
--status enabled \
--days 30 \
--type UntaggedManifestsManual cleanup for a specific repository:
bash
# List all tags for a service
az acr repository show-tags \
--name micerpbedevacr \
--repository accounting \
--orderby time_desc
# Delete a specific tag
az acr repository delete \
--name micerpbedevacr \
--image accounting:feature-old-1001 \
--yesGeo-Replication (Production)
The production ACR (micerpbeprdacr) replicates to a secondary region for DR:
bicep
resource acrReplication 'Microsoft.ContainerRegistry/registries/replications@2023-01-01-preview' = {
name: 'saudiArabiaNorth'
parent: acr
location: 'saudinortharabia'
properties: {
zoneRedundancy: 'Disabled'
}
}Container Apps in the production region pull from the nearest replication endpoint automatically.
Troubleshooting
| Symptom | Likely Cause | Resolution |
|---|---|---|
| Container App fails to pull image | Managed identity missing AcrPull role | Run az role assignment create with AcrPull role |
unauthorized: authentication required in pipeline | Service connection not authenticated | Verify Azure DevOps service connection has AcrPush role |
| Image not found after push | Wrong ACR name or tag | Verify tag format; check environment ACR name formula |
| Old images consuming storage | Retention policy not configured | Enable untagged manifest cleanup |
Related Documentation
- Build Docker — Full pipeline YAML for building and pushing images
- Container Apps — How images are pulled at runtime
- Naming Conventions — ACR naming formula details
- Bicep Modules —
container-registry.bicepmodule