Appearance
Services Configuration (services-config.json)
services-config.json is the single source of truth for all 13 Microtec ERP backend services across all 5 environments. Every deployment pipeline reads this file to determine what to build, deploy, and how to scale.
Location: Devops/azure/config/container-backend/services-config.json
Why a Single Config File
Before services-config.json, service configuration was scattered across:
- Individual pipeline YAML files (one per service × 5 environments = 65+ files)
- Bicep parameter files
- Azure DevOps variable groups
Centralizing into one file means:
- Adding a new service requires editing one file
- Scaling a service in all environments is a single diff
- Pipelines are generic templates that read the config dynamically
Build-BicepParams.ps1generates Bicep params from this file automatically
File Structure
json
{
"version": "1.0.0",
"description": "Container Backend Deployment Configuration",
"repositories": {
"Platforms": { "type": "git", "name": "Platforms", "buildType": "dotnet" },
"InfrastructureServices": { "type": "git", "name": "InfrastructureServices", "buildType": "dotnet" },
"WorkflowDesigner": { "type": "git", "name": "WorkflowDesigner", "buildType": "dotnet" },
"KeycloakProviders": { "type": "git", "name": "KeycloakProviders", "buildType": "docker" }
},
"infrastructure": {
"resourcePrefix": "mic-erp-be",
"namingConventions": {
"resourceGroup": "{prefix}-{env}-{purpose}-rg",
"containerApp": "{prefix}-{envShort}-{name}",
"containerAppsEnvironment": "{prefix}-{env}-cae-{profile}"
}
},
"environments": [
{ "name": "dev", "shortName": "dev", "acrName": "micerpbedevacr" },
{ "name": "stage", "shortName": "stg", "acrName": "micerpbestageacr" },
{ "name": "preprod", "shortName": "pp", "acrName": "micerpbepreprodacr" },
{ "name": "uat", "shortName": "uat", "acrName": "micerpbeuatacr" },
{ "name": "production", "shortName": "prd", "acrName": "micerpbeprdacr" }
],
"services": [ ... 13 services ... ]
}Version 1.0.0
The current config file is version 1.0.0. It uses a repositories object (5 repos), an infrastructure block with naming conventions, and an environments array (not a dict). There is no top-level defaults block.
Top-Level Blocks
repositories
Defines the 4 source repositories declared in the config. Each service references one via its repository field.
| Key | Repo Name | Build Type |
|---|---|---|
Platforms | Platforms | dotnet |
InfrastructureServices | InfrastructureServices | dotnet |
WorkflowDesigner | WorkflowDesigner | dotnet |
KeycloakProviders | KeycloakProviders | docker |
MicrotecHR
The Hr.Personnel.Apis service references "repository": "MicrotecHR", but MicrotecHR is defined separately in the pipeline YAML resources block (not in the repositories section of this config file). The all-repos-pipeline.yml explicitly checks out Microtec HR/Microtec HR.
infrastructure
Contains the resource naming prefix (mic-erp-be) and naming convention templates used for resource groups, container apps, and Container Apps environments.
environments
An array of environment objects (not a dictionary). Each entry contains:
| Field | Description | Example |
|---|---|---|
name | Full environment name | production |
shortName | Abbreviated form used in resource names | prd |
acrName | Azure Container Registry name | micerpbeprdacr |
services
Array of 13 service objects. See All 13 Services below.
Per-Service Fields Reference
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Service name (e.g., Gateway.API) |
displayName | string | Yes | Human-readable name for pipeline logs |
repository | string | Yes | Key referencing one of the repositories entries |
imageName | string | Yes | Docker image name in ACR (e.g., gateway-api) |
dockerfilePath | string | Yes | Relative path to Dockerfile within the repo |
buildContext | string | Yes | Docker build context (usually .) |
networkProfile | public or private | Yes | Use public for Gateway + Keycloak only |
cpu | string | Yes | vCPU allocation (e.g., "0.5") |
memory | string | Yes | Memory allocation (e.g., "1Gi") |
minReplicas | integer | Yes | Minimum replica count |
maxReplicas | integer | Yes | Maximum replica count |
healthCheckPath | string | Yes | Health probe HTTP path (e.g., /health) |
port | integer | Yes | Container port |
envVars | array | No | Secret references from secretRegistry |
staticEnvVars | object | No | Non-secret env vars with fixed values |
profile | string | No | Service profile key from serviceProfiles (inheritance) |
Template Tokens
| Token | Replaced With |
|---|---|
{environment} | dev, stage, preprod, uat, production |
{domain} | microtec-test.com, microtecstage.com, etc. |
{acrName} | ACR login server (e.g., micerpbedevacr) |
{resourcePrefix} | mic-erp-be |
All 13 Services
| # | Service Name | Image Name | Repository | Network |
|---|---|---|---|---|
| 1 | Gateway.API | gateway-api | Platforms | public |
| 2 | AppsPortal.Apis | appsportal-apis | Platforms | private |
| 3 | Inventory.Apis | inventory-apis | Platforms | private |
| 4 | BusinessOwners.Apis | bo-apis | Platforms | private |
| 5 | BusinessOwners.AdminPortal | adminportal-apis | Platforms | private |
| 6 | Integration.Apis | integration-apis | Platforms | private |
| 7 | Platforms.Worker | worker | Platforms | private |
| 8 | Attachment.Apis | attachment-apis | InfrastructureServices | private |
| 9 | Notification.Apis | notification-apis | InfrastructureServices | private |
| 10 | Template.Blazor | template-apis | InfrastructureServices | private |
| 11 | Workflows.Apis | workflows-apis | WorkflowDesigner | private |
| 12 | Hr.Personnel.Apis | hr-apis | MicrotecHR | private |
| 13 | Keycloak | keycloak | KeycloakProviders | public |
Keycloak uses buildType: docker
Keycloak is built from KeycloakProviders with buildType: docker, meaning the full Dockerfile is run directly rather than a .NET SDK build. SPI JARs are compiled and copied in during the Docker build.
How to Add a New Service
Follow these steps to register a new microservice in the deployment ecosystem:
Step 1: Create the service entry in services-config.json
json
{
"name": "Zatca.Apis",
"displayName": "ZATCA E-Invoicing",
"repository": "InfrastructureServices",
"toggleName": "svc_zatcaApi",
"imageName": "zatca-apis",
"dockerfilePath": "Src/Zatca/Zatca.Apis/Dockerfile",
"buildContext": ".",
"projectPath": "Src/Zatca",
"deployTarget": "container-app",
"imageSource": "build",
"networkProfile": "private",
"port": 8080,
"healthCheckPath": "/health",
"minReplicas": 0,
"maxReplicas": 3,
"cpu": "0.5",
"memory": "1Gi",
"envVars": [
"ASPNETCORE_ENVIRONMENT",
"ZatcaConfiguration__Url",
"ZatcaConfiguration__ClientId",
"ZatcaConfiguration__ClientSecret"
],
"staticEnvVars": {},
"profile": "dotnet-erp-module"
}Step 2: Add secrets to Key Vault (all environments)
bash
# For each environment KV
az keyvault secret set \
--vault-name mic-erp-be-dev-skv \
--name "ZatcaConfiguration--Url" \
--value "https://zatca-api.example.com"Step 3: Add Gateway route (if service needs external access)
Update the reverse proxy config in Platforms/Src/Gateway/Gateway.Yarp/.
Step 4: Add service toggle to the relevant pipeline YAML
Add a svc_zatcaApi boolean parameter to the appropriate pipeline in pipelines/container-backend/.
Step 5: Run full deploy
bash
# Commit the services-config.json change and push to trigger the pipeline
git add Devops/azure/config/container-backend/services-config.json
git commit -m "feat: add zatca service to deployment config"
git push origin stageStep 6: Verify deployment
bash
az containerapp show \
--name zatca-apis \
--resource-group mic-erp-be-stage-apps-rg \
--query "properties.latestRevisionName" -o tsvConfig Validation
The Validate-ServicesConfig.ps1 script validates the config file before pipelines use it:
- Schema validation against
azure/config/container-backend/schema/services-config.schema.json - All required fields present per service entry
networkProfileispublicorprivateimageNamematches Azure Container App naming rules (lowercase, alphanumeric, hyphens)- No duplicate service names
repositorykey exists in therepositoriesblock
If validation fails, the pipeline aborts at the Initialize stage with a descriptive error.
Related Documentation
- CI/CD Overview — How this file feeds all pipelines
- Orchestrators — Which orchestrators read this file
- Fast Deploy Path — When config changes require full deploy
- Container Apps — How scaling config maps to KEDA triggers
- Key Vault — Secret naming (
--convention)