Appearance
Storage Accounts
Microtec ERP uses Azure Storage Accounts in two categories: backend storage (for file attachments uploaded through the Attachment microservice) and frontend storage (for hosting Angular MFE static files). Each environment has separate storage accounts for each category.
Naming Convention
Storage account names must be globally unique, alphanumeric, and 3–24 characters. The formula strips hyphens from the resource prefix:
Backend: micerpbe{env}sa → e.g. micerpbedevsa, micerpbestagesa
Frontend: micerpfr{env}sa → e.g. micerpfrdevsa, micerpfrstagesaAlphanumeric Only
Azure Storage Account names cannot contain hyphens. The naming convention removes all hyphens from mic-erp-be-{env} and mic-erp-fr-{env} and appends sa (storage account).
Account Inventory
Backend Storage Accounts (Attachments)
| Environment | Account Name | Resource Group |
|---|---|---|
| dev | micerpbedevsa | mic-erp-be-dev-storage-rg |
| stage | micerpbestagesa | mic-erp-be-stage-storage-rg |
| preprod | micerpbepreproдsa | mic-erp-be-preprod-storage-rg |
| uat | micerpbeuatsa | mic-erp-be-uat-storage-rg |
| production | micerpbeprodsa | mic-erp-be-prod-storage-rg |
Frontend Storage Accounts (MFE Hosting)
| Environment | Account Name | Resource Group |
|---|---|---|
| dev | micerpfrdevsa | mic-erp-fr-dev-storage-rg |
| stage | micerpfrstagesa | mic-erp-fr-stage-storage-rg |
| preprod | micerpfrpreprodsa | mic-erp-fr-preprod-storage-rg |
| uat | micerpfruatsa | mic-erp-fr-uat-storage-rg |
| production | micerpfrprodsa | mic-erp-fr-prod-storage-rg |
Backend Storage (Attachments)
The Attachment microservice stores all user-uploaded files in Blob Storage. Files are organized by tenant:
Container Structure
micerpbedevsa/
├── attachments/
│ ├── tenant-1/
│ │ ├── invoices/
│ │ │ └── 2025/01/invoice-abc123.pdf
│ │ └── hr/
│ │ └── employee-photos/photo-456.jpg
│ ├── tenant-2/
│ │ └── ...
│ └── ...
└── temp-uploads/ (24-hour TTL, lifecycle policy)Configuration
csharp
// Attachment service appsettings
{
"Attachment": {
"AzureStorage": {
"ConnectionString": "{from-kv}",
"ContainerName": "attachments",
"TempContainerName": "temp-uploads",
"MaxFileSizeBytes": 52428800, // 50 MB
"AllowedExtensions": [".pdf", ".jpg", ".png", ".xlsx", ".docx"]
}
}
}The connection string is stored in Key Vault secret Attachment--AzureStorage--ConnectionString.
Frontend MFE Storage Refactor
The connection string for frontend MFE static hosting was moved to the frontend storage account (micerpfr{env}sa) in session 13 (2026-03-31). Backend services should use micerpbe{env}sa for attachments only. Do not mix the two.
Lifecycle Policy
Temp uploads are automatically deleted after 24 hours via a lifecycle management rule:
json
{
"rules": [
{
"name": "delete-temp-uploads",
"type": "Lifecycle",
"definition": {
"filters": { "blobTypes": ["blockBlob"], "prefixMatch": ["temp-uploads/"] },
"actions": {
"baseBlob": {
"delete": { "daysAfterModificationGreaterThan": 1 }
}
}
}
}
]
}Frontend Storage (MFE Hosting)
Angular MFE builds are deployed to the $web container in the frontend storage account for static website hosting. Azure Front Door serves these files globally.
Static Website Configuration
bicep
// storage-frontend.bicep
resource frontendStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: frontendStorageName
properties: {
staticWebsite: {
enabled: true
indexDocument: 'index.html'
errorDocument404Path: 'index.html' // SPA fallback routing
}
allowBlobPublicAccess: false // AFD accesses via origin auth
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
}
}The errorDocument404Path is set to index.html to support Angular's client-side routing — all 404s return the SPA, which handles routing internally.
Deployment Structure
micerpfrdevsa/$web/
├── erp-home/
│ └── index.html, main.{hash}.js, ...
├── apps-accounting/
│ └── index.html, main.{hash}.js, ...
├── apps-hr/
│ └── ...
├── apps-finance/
├── apps-sales/
├── apps-purchase/
├── apps-inventory/
├── app-distribution/
└── fixed-assets/Upload via Pipeline
yaml
# From unified-frontend-pipeline.yml
- task: AzureCLI@2
displayName: 'Upload to Blob Storage'
inputs:
script: |
az storage blob upload-batch \
--account-name $(frontendStorageName) \
--destination '$web/$(projectName)' \
--source dist/$(projectName) \
--overwrite true \
--content-cache-control "max-age=31536000, immutable"Bicep Configuration
bicep
// storage-backend.bicep
resource backendStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: backendStorageName
location: location
sku: { name: environment == 'production' ? 'Standard_GRS' : 'Standard_LRS' }
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false // Trivy: AVD-AZU-0008 compliant
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
// Allow access from Container Apps outbound IPs via service endpoint
virtualNetworkRules: [
{ id: appSubnetId, action: 'Allow' }
]
}
}
}Production uses Standard_GRS (Geo-Redundant Storage) for 16-nine durability. Non-production environments use Standard_LRS (Locally Redundant) to minimize cost.
Access Control
Backend Storage Access
The Attachment service accesses backend storage using a connection string (stored in Key Vault). A managed-identity-based access pattern is planned for a future sprint.
Frontend Storage Access
AFD accesses the frontend storage $web container using origin authentication (SAS token embedded in AFD origin configuration). End users never get direct access to the storage account URL.
Monitoring
| Metric | Alert Threshold | Description |
|---|---|---|
UsedCapacity | > 80% of quota | Storage getting full |
Availability | < 99.9% | Storage service degraded |
E2ELatency | > 500ms | Slow blob reads |
Transactions (4xx) | > 10/min | Unauthorized access attempts |
Related Documentation
- Container Apps — Attachment service configuration
- Azure Front Door — Frontend origin group pointing to storage
- Static Web Apps — Alternative frontend hosting model
- CI/CD Angular Build — Frontend deployment pipeline
- Bicep Modules —
storage-backend.bicepandstorage-frontend.bicep