Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)
Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
207 lines
6.6 KiB
Bicep
207 lines
6.6 KiB
Bicep
// Container App module for DB Planet MCP Server
|
|
|
|
@description('The environment name (DEV, IAT, Prod)')
|
|
param environment string
|
|
|
|
@description('The location for the Container App')
|
|
param location string
|
|
|
|
@description('Tags to apply to resources')
|
|
param tags object
|
|
|
|
@description('Unique token for resource naming')
|
|
param resourceToken string
|
|
|
|
@description('Existing central Key Vault name (shared secrets)')
|
|
param centralKeyVaultName string
|
|
|
|
@description('Existing central Key Vault resource group')
|
|
param centralKeyVaultResourceGroup string
|
|
|
|
@description('App-specific Key Vault URI (created by keyvault.bicep)')
|
|
param appKeyVaultUri string
|
|
|
|
@description('Existing Container App Environment name')
|
|
param containerAppEnvironmentName string
|
|
|
|
@description('Existing Container App Environment resource group')
|
|
param containerAppEnvironmentResourceGroup string
|
|
|
|
@description('Artifactory server URL for container registry')
|
|
param artifactoryServer string
|
|
|
|
@description('Registry username for container registry authentication')
|
|
param registryUsername string
|
|
|
|
@description('Managed Identity ID for Key Vault access')
|
|
param managedIdentityId string
|
|
|
|
@description('Container image tag/version')
|
|
param imageTag string = 'latest'
|
|
|
|
@description('DB Planet base URL (staging vs production)')
|
|
param dbplanetBaseUrl string
|
|
|
|
@description('Custom domain name for the Container App')
|
|
param customDomainName string = ''
|
|
|
|
@description('Container App Certificate ID (if custom domain is configured)')
|
|
param containerAppCertificateId string = ''
|
|
|
|
var containerAppName = 'ca-dbp-${toLower(environment)}-${take(resourceToken, 8)}'
|
|
|
|
// Custom domain configuration
|
|
var customDomain = !empty(customDomainName) && !empty(containerAppCertificateId) ? [
|
|
{
|
|
name: customDomainName
|
|
certificateId: containerAppCertificateId
|
|
bindingType: 'SniEnabled'
|
|
}
|
|
] : []
|
|
|
|
// Reference existing central Key Vault
|
|
resource centralKeyVault 'Microsoft.KeyVault/vaults@2024-11-01' existing = {
|
|
name: centralKeyVaultName
|
|
scope: resourceGroup(centralKeyVaultResourceGroup)
|
|
}
|
|
|
|
|
|
// Reference existing Container App Environment
|
|
resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2025-01-01' existing = {
|
|
name: containerAppEnvironmentName
|
|
scope: resourceGroup(containerAppEnvironmentResourceGroup)
|
|
}
|
|
|
|
// Container App
|
|
resource containerApp 'Microsoft.App/containerApps@2025-01-01' = {
|
|
name: containerAppName
|
|
location: location
|
|
tags: tags
|
|
identity: {
|
|
type: 'SystemAssigned, UserAssigned'
|
|
userAssignedIdentities: {
|
|
'${managedIdentityId}': {}
|
|
}
|
|
}
|
|
properties: {
|
|
managedEnvironmentId: containerAppEnvironment.id
|
|
workloadProfileName: 'Consumption'
|
|
configuration: {
|
|
ingress: {
|
|
external: true
|
|
targetPort: 4000
|
|
allowInsecure: false
|
|
customDomains: customDomain
|
|
traffic: [
|
|
{
|
|
weight: 100
|
|
latestRevision: true
|
|
}
|
|
]
|
|
}
|
|
secrets: [
|
|
{
|
|
name: 'artifactory-token'
|
|
keyVaultUrl: '${centralKeyVault.properties.vaultUri}secrets/artifactory-registry-token'
|
|
identity: managedIdentityId
|
|
}
|
|
{
|
|
name: 'appinsights-connection-string'
|
|
keyVaultUrl: '${appKeyVaultUri}secrets/applicationinsights-connection-string'
|
|
identity: 'system'
|
|
}
|
|
{
|
|
name: 'dbplanet-username'
|
|
keyVaultUrl: '${appKeyVaultUri}secrets/DBPLANET-USERNAME'
|
|
identity: 'system'
|
|
}
|
|
{
|
|
name: 'dbplanet-password'
|
|
keyVaultUrl: '${appKeyVaultUri}secrets/DBPLANET-PASSWORD'
|
|
identity: 'system'
|
|
}
|
|
{
|
|
name: 'dbplanet-client-id'
|
|
keyVaultUrl: '${appKeyVaultUri}secrets/DBPLANET-CLIENT-ID'
|
|
identity: 'system'
|
|
}
|
|
{
|
|
name: 'dbplanet-client-secret'
|
|
keyVaultUrl: '${appKeyVaultUri}secrets/DBPLANET-CLIENT-SECRET'
|
|
identity: 'system'
|
|
}
|
|
]
|
|
registries: [
|
|
{
|
|
server: artifactoryServer
|
|
username: registryUsername
|
|
passwordSecretRef: 'artifactory-token'
|
|
}
|
|
]
|
|
}
|
|
template: {
|
|
containers: [
|
|
{
|
|
name: 'dbplanet-mcp'
|
|
image: '${artifactoryServer}/db-planet-mcp-server:${imageTag}'
|
|
resources: {
|
|
cpu: json(environment == 'Prod' ? '1.0' : '0.5')
|
|
memory: environment == 'Prod' ? '2Gi' : '1Gi'
|
|
}
|
|
env: [
|
|
{ name: 'DBPLANET_BASE_URL', value: dbplanetBaseUrl }
|
|
{ name: 'DBPLANET_USERNAME', secretRef: 'dbplanet-username' }
|
|
{ name: 'DBPLANET_PASSWORD', secretRef: 'dbplanet-password' }
|
|
{ name: 'DBPLANET_CLIENT_ID', secretRef: 'dbplanet-client-id' }
|
|
{ name: 'DBPLANET_CLIENT_SECRET', secretRef: 'dbplanet-client-secret' }
|
|
{ name: 'NODE_ENV', value: environment == 'Prod' ? 'production' : 'development' }
|
|
{ name: 'HTTP_PROXY', value: 'http://webproxy.comp.db.de:8080' }
|
|
{ name: 'HTTPS_PROXY', value: 'http://webproxy.comp.db.de:8080' }
|
|
{ name: 'http_proxy', value: 'http://webproxy.comp.db.de:8080' }
|
|
{ name: 'https_proxy', value: 'http://webproxy.comp.db.de:8080' }
|
|
{ name: 'NO_PROXY', value: '169.254.169.254,169.254.170.2,cognitiveservices.azure.com,internal.deutschebahn.com,db.de' }
|
|
{ name: 'no_proxy', value: '169.254.169.254,169.254.170.2,cognitiveservices.azure.com,internal.deutschebahn.com,db.de' }
|
|
{ name: 'STREAMABLE_HTTP', value: 'true' }
|
|
{ name: 'REMOTE_AUTHORIZATION', value: 'true' }
|
|
{ name: 'HOST', value: '0.0.0.0' }
|
|
{
|
|
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
|
|
secretRef: 'appinsights-connection-string'
|
|
}
|
|
]
|
|
probes: [
|
|
{
|
|
type: 'Readiness'
|
|
httpGet: {
|
|
path: '/health'
|
|
port: 4000
|
|
}
|
|
initialDelaySeconds: 5
|
|
periodSeconds: 30
|
|
}
|
|
{
|
|
type: 'Liveness'
|
|
httpGet: {
|
|
path: '/health'
|
|
port: 4000
|
|
}
|
|
initialDelaySeconds: 10
|
|
periodSeconds: 60
|
|
}
|
|
]
|
|
}
|
|
]
|
|
scale: {
|
|
minReplicas: 1
|
|
maxReplicas: 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
output containerAppName string = containerApp.name
|
|
output containerAppId string = containerApp.id
|
|
output containerAppFqdn string = containerApp.properties.configuration.ingress.fqdn
|
|
output systemIdentityPrincipalId string = containerApp.identity.principalId
|
|
output containerAppEnvironmentStaticIp string = containerAppEnvironment.properties.staticIp
|