Appearance
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 Value | File Loaded |
|---|---|
Development | appsettings.Development.json |
stage | appsettings.stage.json |
preprod | appsettings.preprod.json |
uat | appsettings.uat.json |
production | appsettings.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 path | KV secret name |
|---|---|
ConnectionStrings:DefaultConnection | ConnectionStrings--DefaultConnection |
RedisConfiguration:Password | RedisConfiguration--Password |
MonogoDb:ConnectionString | MonogoDb--ConnectionString |
Keycloak:ClientSecret | Keycloak--ClientSecret |
RabbitMq:Host | RabbitMq--Host |
Jwt:SecretKey | Jwt--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:
| Location | Key (with typo) |
|---|---|
appsettings.json | "MonogoDb": { "ConnectionString": "..." } |
| Key Vault secret name | MonogoDb--ConnectionString |
| Container App env var | MonogoDb__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:
- Do not copy encrypted connection strings between environments.
- Store the plaintext secret in Key Vault and use
keyvaultref:instead. - 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 missingIf 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 }
}
}
}