Skip to content

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.

EnvironmentKey Vault NameResource Group
devmic-erp-be-dev-skvmic-erp-be-dev-keyvault-rg
stagemic-erp-stg-kvmic-erp-be-stage-keyvault-rg
preprodmic-erp-be-preprod-skvmic-erp-be-preprod-keyvault-rg
uatmic-erp-uat-kvmic-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--Authority

Example Secret Names

Secret Name (KV)appsettings.json PathDescription
ConnectionStrings--DefaultConnectionStrings:DefaultSQL Server tenant connection
ConnectionStrings--AdminConnectionStrings:AdminAdmin database connection
Redis--Configuration--PasswordRedis:Configuration:PasswordRedis password
AzureServiceBus--ConnectionStringAzureServiceBus:ConnectionStringService Bus connection
MonogoDb--ConnectionStringMonogoDb:ConnectionStringMongoDB (intentional typo — see warning)
Jwt--AuthorityJwt:AuthorityKeycloak realm URL
Notification--SendGrid--ApiKeyNotification:SendGrid:ApiKeySendGrid API key
Attachment--AzureStorage--ConnectionStringAttachment:AzureStorage:ConnectionStringBlob 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)

SecretDescription
ConnectionStrings--AdminAdmin DB (cross-service tenant lookups)
Jwt--AuthorityKeycloak realm URL for token validation
Jwt--AudienceKeycloak client ID for token audience check
AzureServiceBus--ConnectionStringAsync messaging
Redis--ConfigurationDistributed cache (format: host:port,password=x,ssl=true)

Service-Specific Secrets

ServiceSecretDescription
NotificationNotification--SendGrid--ApiKeyEmail delivery
NotificationNotification--Twilio--AccountSidSMS delivery
AttachmentAttachment--AzureStorage--ConnectionStringFile uploads
WorkflowWorkflow--SignalR--ConnectionStringReal-time workflow events
ZatcaZatca--CertificateBase64Saudi e-invoicing certificate
ZatcaZatca--PrivateKeySigning key for e-invoices
GatewayGateway--XApiKeyInternal 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 name RedisConfiguration--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"

Internal Documentation — Microtec Platform Team