// 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