Skip to content

Configuration Management

Configuration for Microtec ERP backend services follows a layered hierarchy: base settings in code, environment overrides in JSON files, and secrets injected from Azure Key Vault at runtime.


Configuration Hierarchy

Priority (highest wins)
─────────────────────────────────────────────────────────
  Level 3: Azure Key Vault secrets (injected at runtime)
  Level 2: appsettings.{Environment}.json
  Level 1: appsettings.json (base defaults)
─────────────────────────────────────────────────────────

.NET's built-in IConfiguration merges these layers at startup. Higher levels override lower levels for the same key.


appsettings.json — Base Defaults

Contains non-secret defaults that apply to all environments:

json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.EntityFrameworkCore.Database.Command": "Warning"
    }
  },
  "AllowedHosts": "*",
  "HealthChecks": {
    "Enabled": true,
    "Path": "/health"
  },
  "OpenTelemetry": {
    "EnableTracing": true,
    "EnableMetrics": true,
    "ServiceName": "$(serviceName)"
  }
}

No secrets or environment-specific URLs belong here.


appsettings.{Environment}.json

Environment name is set via the ASPNETCORE_ENVIRONMENT variable. Valid values:

Variable ValueFile Loaded
Developmentappsettings.Development.json
stageappsettings.stage.json
preprodappsettings.preprod.json
uatappsettings.uat.json
productionappsettings.production.json

Example appsettings.Development.json:

json
{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "Seq": {
    "ServerUrl": "http://localhost:1234"
  },
  "OpenTelemetry": {
    "OtlpEndpoint": "http://localhost:4317"
  }
}

Azure Key Vault — Secret Injection

Secrets must never appear in appsettings.json or environment-specific JSON files. All secrets are stored in the environment's Azure Key Vault and referenced via the keyvaultref: pattern.

Key Vault Reference Pattern in services-config.json

The pipeline reads Devops/azure/config/container-backend/services-config.json and resolves KV references when creating Container Apps environment variables:

json
{
  "services": [
    {
      "name": "accounting",
      "environmentVariables": [
        {
          "name": "ConnectionStrings__DefaultConnection",
          "value": "keyvaultref:https://mic-erp-be-dev-skv.vault.azure.net/secrets/ConnectionStrings--DefaultConnection"
        },
        {
          "name": "RedisConfiguration__Password",
          "value": "keyvaultref:https://mic-erp-be-dev-skv.vault.azure.net/secrets/RedisConfiguration--Password"
        }
      ]
    }
  ]
}

The keyvaultref: prefix tells the Container Apps runtime to fetch the secret value from Key Vault at container startup and inject it as an environment variable.


Double-Dash Convention

Azure Key Vault secret names do not support colons (:) — the standard .NET configuration section separator. The Microtec platform uses a double-dash (--) as the KV secret naming convention:

C# config pathKV secret name
ConnectionStrings:DefaultConnectionConnectionStrings--DefaultConnection
RedisConfiguration:PasswordRedisConfiguration--Password
MonogoDb:ConnectionStringMonogoDb--ConnectionString
Keycloak:ClientSecretKeycloak--ClientSecret
RabbitMq:HostRabbitMq--Host
Jwt:SecretKeyJwt--SecretKey

.NET's configuration system automatically maps -- back to : when environment variables or KV references are loaded.


CRITICAL: The MonogoDb Typo

Do NOT fix this typo.

The NuGet package Microtec.Persistence (consumed by all backend services) reads the MongoDB connection string under the key MonogoDb__ConnectionString — with the letters o and g transposed.

This typo exists in the published NuGet package and is intentionally preserved in all configuration files and Key Vault secrets to maintain compatibility:

LocationKey (with typo)
appsettings.json"MonogoDb": { "ConnectionString": "..." }
Key Vault secret nameMonogoDb--ConnectionString
Container App env varMonogoDb__ConnectionString

If you "fix" this to MongoDB or MongoDb, the services will silently fail to connect to MongoDB with no error about the missing key (they will use the default null connection string).


ASPNETCORE_ENVIRONMENT Variable

Set via services-config.json per environment:

json
{
  "name": "ASPNETCORE_ENVIRONMENT",
  "value": "preprod"
}

The pipeline substitutes $(targetEnvironment) at deployment time based on the branch being built.


DataProtection Encrypted Connection Strings

Warning: Some older services store DataProtection-encrypted connection strings in their configuration. These are encrypted with a machine-specific key and cannot be decrypted on a new server or container.

If you see a connection string that begins with CfDJ8 (the ASP.NET Core DataProtection DPAPI prefix), it is encrypted with the key of the machine that created it.

Resolution:

  1. Do not copy encrypted connection strings between environments.
  2. Store the plaintext secret in Key Vault and use keyvaultref: instead.
  3. If a service cannot start due to a DataProtection error, the key ring has been lost — regenerate the secret and store it in KV.

Configuration Validation at Startup

All services validate required configuration at startup using IOptions<T> with data annotations:

csharp
// In Program.cs
builder.Services
    .AddOptions<DatabaseSettings>()
    .BindConfiguration("ConnectionStrings")
    .ValidateDataAnnotations()
    .ValidateOnStart();   // Fail fast if required config is missing

If a required KV secret is missing or the keyvaultref: cannot be resolved, the container will fail to start with a clear error log — it will not silently use a null/default value.


Local Development Configuration

For local development, secrets are stored in User Secrets (never committed to git):

bash
# Initialize user secrets for a project
cd Platforms/Src/AppsPortal/Accounting/AppsPortal.Apis
dotnet user-secrets init

# Set a secret
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost,1433;..."
dotnet user-secrets set "RedisConfiguration:Password" "yourRedisPassword"
dotnet user-secrets set "MonogoDb:ConnectionString" "mongodb://localhost:27017"

User secrets are stored at ~/.microsoft/usersecrets/{userSecretsId}/secrets.json and take priority over all appsettings*.json files in development.


Configuration in the Pipeline

The pipeline script Devops/azure/scripts/infra/Build-BicepParams.ps1 reads services-config.json and generates Bicep parameter files, substituting keyvaultref: patterns and resolving per-environment values:

powershell
# Conceptual flow in Build-BicepParams.ps1
$config = Get-Content services-config.json | ConvertFrom-Json
foreach ($service in $config.services) {
    $envVars = $service.environmentVariables | ForEach-Object {
        if ($_.value -like "keyvaultref:*") {
            # Resolve KV reference to actual secret ID
            @{ name = $_.name; secretRef = Resolve-KvRef $_.value $environment }
        } else {
            @{ name = $_.name; value = $_.value }
        }
    }
}

Internal Documentation — Microtec Platform Team