Skip to content

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
EnvironmentACR NameResource Group
devmicerpbedevacrmic-erp-be-dev-global-rg
stagemicerpbestageacrmic-erp-be-stage-global-rg
preprodmicerpbepreprodacrmic-erp-be-preprod-global-rg
uatmicerpbeuatacrmic-erp-be-uat-global-rg
productionmicerpbeprdacrmic-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

EnvironmentSKUGeo-Replication
devBasicNo
stageStandardNo
preprodStandardNo
uatStandardNo
productionPremiumYes (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:

EnvironmentTag PatternExample
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 ... --force

Never 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 Runtime

ACR 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 UntaggedManifests

Manual 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 \
  --yes

Geo-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

SymptomLikely CauseResolution
Container App fails to pull imageManaged identity missing AcrPull roleRun az role assignment create with AcrPull role
unauthorized: authentication required in pipelineService connection not authenticatedVerify Azure DevOps service connection has AcrPush role
Image not found after pushWrong ACR name or tagVerify tag format; check environment ACR name formula
Old images consuming storageRetention policy not configuredEnable untagged manifest cleanup

Internal Documentation — Microtec Platform Team