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.
244 lines
11 KiB
Bicep
244 lines
11 KiB
Bicep
// DB Planet MCP Server Infrastructure
|
|
// Deploys: Resource Group, Managed Identity, Key Vault,
|
|
// Application Insights, Container App, Key Vault permissions
|
|
|
|
targetScope = 'subscription'
|
|
|
|
// ── Core parameters ──────────────────────────────────────────
|
|
@description('Environment name')
|
|
@allowed(['DEV', 'IAT', 'Prod'])
|
|
param environment string
|
|
|
|
@description('Azure region')
|
|
param location string = 'westeurope'
|
|
|
|
@description('Container image tag')
|
|
param imageTag string = 'latest'
|
|
|
|
@description('Set to true on second deployment after infra + permissions are in place')
|
|
param deployContainerApp bool = false
|
|
|
|
// ── Tags ─────────────────────────────────────────────────────
|
|
@description('Tags applied to all resources')
|
|
param tags object = {
|
|
CostReference: 'H-450007-52-01'
|
|
ReferenceID: 'A-113241'
|
|
ReferenceName: 'DB Planet MCP Server'
|
|
Environment: environment
|
|
}
|
|
|
|
// ── Existing infrastructure references ───────────────────────
|
|
@description('Central Key Vault name (shared secrets)')
|
|
param centralKeyVaultName string
|
|
|
|
@description('Central Key Vault resource group')
|
|
param centralKeyVaultResourceGroup string
|
|
|
|
@description('Container App Environment name')
|
|
param containerAppEnvironmentName string
|
|
|
|
@description('Container App Environment resource group')
|
|
param containerAppEnvironmentResourceGroup string
|
|
|
|
// ── Application configuration ────────────────────────────────
|
|
@description('Artifactory server URL')
|
|
param artifactoryServer string
|
|
|
|
@description('Registry username')
|
|
param registryUsername string
|
|
|
|
@description('DB Planet base URL (staging vs production)')
|
|
param dbplanetBaseUrl string
|
|
|
|
// ── Service Principal for Key Vault secrets management ───────
|
|
#disable-next-line secure-secrets-in-params // Object ID, not a secret
|
|
@description('Service Principal Object ID for Key Vault Secrets Officer role')
|
|
param secretsOfficerPrincipalId string = ''
|
|
|
|
// ── Custom domain (optional) ─────────────────────────────────
|
|
@description('Certificate name in Container App Environment')
|
|
param certificateName string = ''
|
|
|
|
@description('DNS zone name for custom domain')
|
|
param dnsZoneName string = ''
|
|
|
|
@description('DNS record name (subdomain)')
|
|
param dnsRecordName string = ''
|
|
|
|
@description('Resource group containing the DNS zone')
|
|
param dnsZoneResourceGroup string = 'Infrastructure'
|
|
|
|
// ── Derived values ───────────────────────────────────────────
|
|
var resourceGroupName = 'rg-dbplanet-mcp-${toLower(environment)}'
|
|
var resourceToken = toLower(uniqueString(subscription().id, resourceGroupName, location))
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 1. Resource Group
|
|
// ═══════════════════════════════════════════════════════════════
|
|
module rg 'modules/resource-group.bicep' = {
|
|
name: 'deploy-rg'
|
|
params: {
|
|
resourceGroupName: resourceGroupName
|
|
location: location
|
|
tags: tags
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 2. Managed Identity
|
|
// ═══════════════════════════════════════════════════════════════
|
|
module identity 'modules/managed-identity.bicep' = {
|
|
name: 'deploy-identity'
|
|
scope: resourceGroup(resourceGroupName)
|
|
dependsOn: [rg]
|
|
params: {
|
|
environment: environment
|
|
location: location
|
|
tags: tags
|
|
resourceToken: resourceToken
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 3. Key Vault (app-specific, public access)
|
|
// ═══════════════════════════════════════════════════════════════
|
|
module keyVault 'modules/keyvault.bicep' = {
|
|
name: 'deploy-keyvault'
|
|
scope: resourceGroup(resourceGroupName)
|
|
dependsOn: [rg]
|
|
params: {
|
|
environment: environment
|
|
location: location
|
|
tags: tags
|
|
resourceToken: resourceToken
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 4. Application Insights + Log Analytics
|
|
// ═══════════════════════════════════════════════════════════════
|
|
module appInsights 'modules/application-insights.bicep' = {
|
|
name: 'deploy-app-insights'
|
|
scope: resourceGroup(resourceGroupName)
|
|
params: {
|
|
environment: environment
|
|
location: location
|
|
tags: tags
|
|
resourceToken: resourceToken
|
|
keyVaultName: keyVault.outputs.keyVaultName
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 5. Container App Certificate (existing, optional)
|
|
// ═══════════════════════════════════════════════════════════════
|
|
module certificate 'modules/container-app-certificate.bicep' = if (!empty(certificateName)) {
|
|
name: 'deploy-certificate'
|
|
scope: resourceGroup(containerAppEnvironmentResourceGroup)
|
|
params: {
|
|
containerAppEnvironmentName: containerAppEnvironmentName
|
|
certificateName: certificateName
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 6. Container App
|
|
// ═══════════════════════════════════════════════════════════════
|
|
var customDomainName = !empty(dnsRecordName) && !empty(dnsZoneName) ? '${dnsRecordName}.${dnsZoneName}' : ''
|
|
|
|
module containerApp 'modules/container-app.bicep' = if (deployContainerApp) {
|
|
name: 'deploy-container-app'
|
|
scope: resourceGroup(resourceGroupName)
|
|
dependsOn: [rg]
|
|
params: {
|
|
environment: environment
|
|
location: location
|
|
tags: tags
|
|
resourceToken: resourceToken
|
|
centralKeyVaultName: centralKeyVaultName
|
|
centralKeyVaultResourceGroup: centralKeyVaultResourceGroup
|
|
appKeyVaultUri: keyVault.outputs.keyVaultUri
|
|
containerAppEnvironmentName: containerAppEnvironmentName
|
|
containerAppEnvironmentResourceGroup: containerAppEnvironmentResourceGroup
|
|
artifactoryServer: artifactoryServer
|
|
registryUsername: registryUsername
|
|
managedIdentityId: identity.outputs.managedIdentityId
|
|
imageTag: imageTag
|
|
dbplanetBaseUrl: dbplanetBaseUrl
|
|
customDomainName: !empty(certificateName) ? customDomainName : ''
|
|
containerAppCertificateId: !empty(certificateName) ? certificate.outputs.certificateId! : ''
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 7. DNS Record
|
|
// ═══════════════════════════════════════════════════════════════
|
|
module dnsRecord 'modules/dns-record.bicep' = if (deployContainerApp && !empty(dnsRecordName)) {
|
|
name: 'deploy-dns-record'
|
|
scope: resourceGroup(dnsZoneResourceGroup)
|
|
params: {
|
|
privateDnsZoneName: dnsZoneName
|
|
aRecordName: dnsRecordName
|
|
ipv4Address: containerApp.outputs.containerAppEnvironmentStaticIp!
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// 8. Key Vault Permissions
|
|
// ═══════════════════════════════════════════════════════════════
|
|
|
|
// 6a. Managed Identity → central Key Vault (read registry secrets)
|
|
module centralKvPermissions 'modules/key-vault-permissions.bicep' = {
|
|
name: 'deploy-central-kv-permissions'
|
|
scope: resourceGroup(centralKeyVaultResourceGroup)
|
|
params: {
|
|
keyVaultName: centralKeyVaultName
|
|
principalId: identity.outputs.managedIdentityPrincipalId
|
|
roleName: 'Key Vault Secrets User'
|
|
}
|
|
}
|
|
|
|
// 6b. Managed Identity → app Key Vault (read app secrets)
|
|
module appKvIdentityPermissions 'modules/key-vault-permissions.bicep' = {
|
|
name: 'deploy-app-kv-identity-permissions'
|
|
scope: resourceGroup(resourceGroupName)
|
|
params: {
|
|
keyVaultName: keyVault.outputs.keyVaultName
|
|
principalId: identity.outputs.managedIdentityPrincipalId
|
|
roleName: 'Key Vault Secrets User'
|
|
}
|
|
}
|
|
|
|
// 6c. Container App system identity → app Key Vault (read app secrets)
|
|
module appKvContainerAppPermissions 'modules/key-vault-permissions.bicep' = if (deployContainerApp) {
|
|
name: 'deploy-app-kv-ca-permissions'
|
|
scope: resourceGroup(resourceGroupName)
|
|
params: {
|
|
keyVaultName: keyVault.outputs.keyVaultName
|
|
principalId: containerApp.outputs.systemIdentityPrincipalId!
|
|
roleName: 'Key Vault Secrets User'
|
|
}
|
|
}
|
|
|
|
// 6d. Service Principal → app Key Vault (read + write secrets for CI/CD)
|
|
module appKvOfficerPermissions 'modules/key-vault-permissions.bicep' = if (!empty(secretsOfficerPrincipalId)) {
|
|
name: 'deploy-app-kv-officer-permissions'
|
|
scope: resourceGroup(resourceGroupName)
|
|
params: {
|
|
keyVaultName: keyVault.outputs.keyVaultName
|
|
principalId: secretsOfficerPrincipalId
|
|
roleName: 'Key Vault Secrets Officer'
|
|
}
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════════════════════
|
|
// Outputs
|
|
// ═══════════════════════════════════════════════════════════════
|
|
output resourceGroupName string = rg.outputs.resourceGroupName
|
|
output keyVaultName string = keyVault.outputs.keyVaultName
|
|
output keyVaultUri string = keyVault.outputs.keyVaultUri
|
|
output containerAppName string = deployContainerApp ? containerApp.outputs.containerAppName! : ''
|
|
output containerAppFqdn string = deployContainerApp ? containerApp.outputs.containerAppFqdn! : ''
|
|
output managedIdentityId string = identity.outputs.managedIdentityId
|
|
output appInsightsName string = appInsights.outputs.applicationInsightsName
|