Appearance
Key Vault Configuration
Each Microtec ERP environment has a dedicated Azure Key Vault that stores all application secrets, connection strings, and API keys. Container Apps reference secrets directly from Key Vault using managed identity — no secrets are stored in pipeline variables or environment variables as plaintext.
Key Vault Inventory
Inconsistent Naming — Use Actual Names
Key Vault names across environments do NOT follow a consistent formula. Always use the actual live names listed below. Do NOT derive KV names programmatically using a naming pattern — the names evolved over time and cannot be changed without migrating all secret references.
| Environment | Key Vault Name | Resource Group |
|---|---|---|
| dev | mic-erp-be-dev-skv | mic-erp-be-dev-keyvault-rg |
| stage | mic-erp-stg-kv | mic-erp-be-stage-keyvault-rg |
| preprod | mic-erp-be-preprod-skv | mic-erp-be-preprod-keyvault-rg |
| uat | mic-erp-uat-kv | mic-erp-be-uat-keyvault-rg |
| production | (contact platform team) | mic-erp-be-prod-keyvault-rg |
Secret Naming Convention
Azure Key Vault does not allow colons (:) in secret names. ASP.NET Core configuration uses : as the hierarchy separator (e.g., ConnectionStrings:DefaultConnection). The convention maps : to -- (double-dash):
appsettings.json key → Key Vault secret name
─────────────────────────────────────────────────────
ConnectionStrings:Default → ConnectionStrings--Default
Redis:Configuration → Redis--Configuration
AzureServiceBus:ConnectionString → AzureServiceBus--ConnectionString
Jwt:Authority → Jwt--AuthorityExample Secret Names
| Secret Name (KV) | appsettings.json Path | Description |
|---|---|---|
ConnectionStrings--Default | ConnectionStrings:Default | SQL Server tenant connection |
ConnectionStrings--Admin | ConnectionStrings:Admin | Admin database connection |
Redis--Configuration--Password | Redis:Configuration:Password | Redis password |
AzureServiceBus--ConnectionString | AzureServiceBus:ConnectionString | Service Bus connection |
MonogoDb--ConnectionString | MonogoDb:ConnectionString | MongoDB (intentional typo — see warning) |
Jwt--Authority | Jwt:Authority | Keycloak realm URL |
Notification--SendGrid--ApiKey | Notification:SendGrid:ApiKey | SendGrid API key |
Attachment--AzureStorage--ConnectionString | Attachment:AzureStorage:ConnectionString | Blob storage |
MonogoDb Typo is Intentional
The secret name MonogoDb--ConnectionString (MonoGO, not MonGO) matches a typo in the NuGet package Microtec.Persistence. The package reads the configuration key MonogoDb:ConnectionString. Do NOT correct this typo — fixing it would break all environments.
KV Reference Pattern in Container Apps
Container Apps inject Key Vault secrets using the keyvaultref: scheme. The secret value is retrieved at container startup and is available as an environment variable:
Syntax
keyvaultref:https://{vault-name}.vault.azure.net/secrets/{secret-name}Bicep Configuration
bicep
// In container-apps.bicep
resource accountingApp 'Microsoft.App/containerApps@2023-05-01' = {
name: 'accounting'
properties: {
configuration: {
secrets: [
{
name: 'db-connection'
keyVaultUrl: 'https://${keyVaultName}.vault.azure.net/secrets/ConnectionStrings--Default'
identity: managedIdentityId
}
{
name: 'redis-password'
keyVaultUrl: 'https://${keyVaultName}.vault.azure.net/secrets/Redis--Configuration--Password'
identity: managedIdentityId
}
]
}
template: {
containers: [
{
env: [
{
name: 'ConnectionStrings__Default'
secretRef: 'db-connection'
}
{
name: 'Redis__Configuration__Password'
secretRef: 'redis-password'
}
]
}
]
}
}
}Double Underscore in Env Vars
When .NET reads environment variables, it maps __ (double underscore) to : in the configuration hierarchy. So ConnectionStrings__Default in an environment variable is equivalent to ConnectionStrings:Default in appsettings.json.
Managed Identity Access
Container Apps access Key Vault using the user-assigned managed identity. No credentials required:
RBAC Assignment
Each managed identity needs the Key Vault Secrets User role on the Key Vault:
bicep
// In key-vault.bicep
resource kvRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(keyVault.id, managedIdentity.id, 'Key Vault Secrets User')
scope: keyVault
properties: {
roleDefinitionId: subscriptionResourceId(
'Microsoft.Authorization/roleDefinitions',
'4633458b-17de-408a-b874-0445c86b69e6' // Key Vault Secrets User
)
principalId: managedIdentity.properties.principalId
principalType: 'ServicePrincipal'
}
}Secret Categories by Service
Shared Secrets (all services reference these)
| Secret | Description |
|---|---|
ConnectionStrings--Admin | Admin DB (cross-service tenant lookups) |
Jwt--Authority | Keycloak realm URL for token validation |
Jwt--Audience | Keycloak client ID for token audience check |
AzureServiceBus--ConnectionString | Async messaging |
Redis--Configuration | Distributed cache (format: host:port,password=x,ssl=true) |
Service-Specific Secrets
| Service | Secret | Description |
|---|---|---|
| Notification | Notification--SendGrid--ApiKey | Email delivery |
| Notification | Notification--Twilio--AccountSid | SMS delivery |
| Attachment | Attachment--AzureStorage--ConnectionString | File uploads |
| Workflow | Workflow--SignalR--ConnectionString | Real-time workflow events |
| Zatca | Zatca--CertificateBase64 | Saudi e-invoicing certificate |
| Zatca | Zatca--PrivateKey | Signing key for e-invoices |
| Gateway | Gateway--XApiKey | Internal service authentication key |
Stage Redis Credentials
Stage Redis Reference
Stage Redis is a named cache instance:
- Host:
mic-erp-be-stage-redis.uksouth.redis.azure.net:10000 - SSL:
true - Password: stored in
mic-erp-stg-kv→ secret nameRedisConfiguration--Password
Rotation and Incident Response
Secret Rotation Procedure
XApiKey Reference
The internal XApiKey value 3bb564df-0f24-4ea6-82c1-d99f368cac8a is used for direct internal service-to-service calls (bypassing Keycloak). This value must be rotated if the Gateway or any private service is compromised.
Force Container Restart After Rotation
bash
# Force all replicas to restart and pick up new secret
az containerapp revision restart \
--name accounting \
--resource-group mic-erp-be-dev-apps-rg \
--revision $(az containerapp revision list \
--name accounting \
--resource-group mic-erp-be-dev-apps-rg \
--query "[?properties.active].name | [0]" -o tsv)Diagnostics
Verify a Container is Reading a Secret
bash
# Check environment variables in a running container
az containerapp exec \
--name accounting \
--resource-group mic-erp-be-dev-apps-rg \
--command "env | grep ConnectionStrings"Check KV Access Errors
bash
# Look for KV access denied errors in container logs
az containerapp logs show \
--name accounting \
--resource-group mic-erp-be-dev-apps-rg \
--tail 100 | grep -i "keyvault\|secret\|unauthorized"Related Documentation
- Container Apps — How secrets are injected via
secretRef - Infrastructure Overview — Managed identity configuration
- Naming Conventions — KV naming rationale
- CI/CD Services Config — How secrets are referenced in config JSON