From 47de2b8061d421cbfcc795241b86b3bee8556adf Mon Sep 17 00:00:00 2001 From: namtran6701 Date: Tue, 3 Mar 2026 23:10:22 -0500 Subject: [PATCH] feat: provision SQL infrastructure with Azure AD admin access and parameters --- infra/core/security/sql-aad-access.bicep | 18 ++++++++ infra/main.bicep | 58 ++++++++++++++++++++---- infra/main.parameters.json | 9 ++++ 3 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 infra/core/security/sql-aad-access.bicep diff --git a/infra/core/security/sql-aad-access.bicep b/infra/core/security/sql-aad-access.bicep new file mode 100644 index 00000000..73165dc1 --- /dev/null +++ b/infra/core/security/sql-aad-access.bicep @@ -0,0 +1,18 @@ +param sqlServerName string +param aadAdminLogin string +param aadAdminObjectId string + +resource sqlServer 'Microsoft.Sql/servers@2021-11-01' existing = { + name: sqlServerName +} + +resource sqlADAdmin 'Microsoft.Sql/servers/administrators@2021-11-01' = { + parent: sqlServer + name: 'ActiveDirectory' + properties: { + administratorType: 'ActiveDirectory' + login: aadAdminLogin + sid: aadAdminObjectId + tenantId: tenant().tenantId + } +} diff --git a/infra/main.bicep b/infra/main.bicep index 372b0637..4275efae 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -594,15 +594,17 @@ var mcpAzureSearchIndexVar = !empty(mcpAzureSearchIndex) ? mcpAzureSearchIndex : param mcpUserDataContainer string = '' var mcpUserDataContainerVar = !empty(mcpUserDataContainer) ? mcpUserDataContainer : '' -@description('Pulse SQL Server for MCP function app') -param pulseSqlServer string = '' -var pulseSqlServerVar = !empty(pulseSqlServer) ? pulseSqlServer : '' +var sqlServerName = 'sql0-${resourceToken}' +var sqlDatabaseName = 'sqldb0-${resourceToken}' -@description('Pulse SQL Database for MCP function app') -param pulseSqlDatabase string = '' -var pulseSqlDatabaseVar = !empty(pulseSqlDatabase) ? pulseSqlDatabase : '' +@description('SQL Server administrator login (auto-generated password stored in KV)') +param sqlAdminLogin string = 'sqladmin' -@description('Pulse SQL Table for MCP function app') +@description('SQL Server administrator password — auto-generated, stored in KV, not used by app') +@secure() +param sqlAdminPassword string + +@description('SQL Table name for MCP function app') param pulseSqlTable string = '' var pulseSqlTableVar = !empty(pulseSqlTable) ? pulseSqlTable : '' @@ -995,6 +997,23 @@ module keyvaultpe './core/network/private-endpoint.bicep' = if (networkIsolation } } +// SQL Server for MCP function app +module sqlServer './core/db/sqlserver.bicep' = { + name: 'sqlserver' + scope: resourceGroup + params: { + name: sqlServerName + location: location + tags: tags + administratorLogin: sqlAdminLogin + administratorLoginPassword: sqlAdminPassword + databaseName: sqlDatabaseName + keyVaultName: keyVault.outputs.name + secretName: 'sqlAdminPassword' + publicNetworkAccess: 'Enabled' + } +} + // Create an App Service Plan module appServicePlan './core/host/appserviceplan.bicep' = { name: 'appserviceplan' @@ -1309,6 +1328,27 @@ module mcpServerCosmosAccess './core/security/cosmos-access.bicep' = { } } +// SQL DB Contributor (control plane) for MCP function's managed identity +module mcpServerSqlAccess './core/security/sqlserver-access.bicep' = { + name: 'mcp-server-sql-access' + scope: resourceGroup + params: { + sqlServerName: sqlServer.outputs.name + principalId: mcpServer.outputs.identityPrincipalId + } +} + +// Set MCP function's managed identity as the SQL Azure AD admin (data plane access) +module mcpServerSqlAdminAccess './core/security/sql-aad-access.bicep' = { + name: 'mcp-server-sql-aad-access' + scope: resourceGroup + params: { + sqlServerName: sqlServer.outputs.name + aadAdminLogin: mcpServerFunctionAppName + aadAdminObjectId: mcpServer.outputs.identityPrincipalId + } +} + // Give the MCP Resource Token function access to AOAI module mcpServerOaiAccess './core/security/openai-access.bicep' = { name: 'mcp-server-openai-access' @@ -2032,11 +2072,11 @@ module mcpServer './core/host/functions.bicep' = { } { name: 'SQL_SERVER' - value: pulseSqlServerVar + value: sqlServer.outputs.serverFullyQualifiedDomainName } { name: 'SQL_DATABASE' - value: pulseSqlDatabaseVar + value: sqlServer.outputs.databaseName } { name: 'SQL_TABLE' diff --git a/infra/main.parameters.json b/infra/main.parameters.json index e23620a3..19bb9f3b 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -232,6 +232,15 @@ }, "webAppUserFeedbackUrl": { "value": "${USER_FEEDBACK_URL}" + }, + "sqlAdminLogin": { + "value": "${SQL_ADMIN_LOGIN=sqladmin}" + }, + "sqlAdminPassword": { + "value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} sqlAdminPassword)" + }, + "pulseSqlTable": { + "value": "${SQL_TABLE}" } } }