@@ -59,6 +59,15 @@ param speechServiceName string = ''
5959param speechServiceSkuName string // Set in main.parameters.json
6060param useGPT4V bool = false
6161
62+ @allowed (['free' , 'provisioned' , 'serverless' ])
63+ param cosmosDbSkuName string // Set in main.parameters.json
64+ param cosmodDbResourceGroupName string = ''
65+ param cosmosDbLocation string = ''
66+ param cosmosDbAccountName string = ''
67+ param cosmosDbThroughput int = 400
68+ param chatHistoryDatabaseName string = 'chat-database'
69+ param chatHistoryContainerName string = 'chat-history'
70+
6271@description ('Location for the OpenAI resource group' )
6372@allowed ([
6473 'canadaeast'
@@ -198,6 +207,8 @@ param useSpeechOutputBrowser bool = false
198207param useSpeechOutputAzure bool = false
199208@description ('Use chat history feature in browser' )
200209param useChatHistoryBrowser bool = false
210+ @description ('Use chat history feature in CosmosDB' )
211+ param useChatHistoryCosmos bool = false
201212@description ('Show options to use vector embeddings for searching in the app UI' )
202213param useVectors bool = false
203214@description ('Use Built-in integrated Vectorization feature of AI Search to vectorize and ingest documents' )
@@ -266,6 +277,10 @@ resource speechResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' exi
266277 name : !empty (speechServiceResourceGroupName ) ? speechServiceResourceGroupName : resourceGroup .name
267278}
268279
280+ resource cosmosDbResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing = if (!empty (cosmodDbResourceGroupName )) {
281+ name : !empty (cosmodDbResourceGroupName ) ? cosmodDbResourceGroupName : resourceGroup .name
282+ }
283+
269284// Monitor application with Azure Monitor
270285module monitoring 'core/monitor/monitoring.bicep' = if (useApplicationInsights ) {
271286 name : 'monitoring'
@@ -329,7 +344,12 @@ var appEnvVariables = {
329344 USE_SPEECH_INPUT_BROWSER : useSpeechInputBrowser
330345 USE_SPEECH_OUTPUT_BROWSER : useSpeechOutputBrowser
331346 USE_SPEECH_OUTPUT_AZURE : useSpeechOutputAzure
347+ // Chat history settings
332348 USE_CHAT_HISTORY_BROWSER : useChatHistoryBrowser
349+ USE_CHAT_HISTORY_COSMOS : useChatHistoryCosmos
350+ AZURE_COSMOSDB_ACCOUNT : (useAuthentication && useChatHistoryCosmos ) ? cosmosDb .outputs .name : ''
351+ AZURE_CHAT_HISTORY_DATABASE : chatHistoryDatabaseName
352+ AZURE_CHAT_HISTORY_CONTAINER : chatHistoryContainerName
333353 // Shared by all OpenAI deployments
334354 OPENAI_HOST : openAiHost
335355 AZURE_OPENAI_EMB_MODEL_NAME : embedding .modelName
@@ -668,6 +688,64 @@ module userStorage 'core/storage/storage-account.bicep' = if (useUserUpload) {
668688 }
669689}
670690
691+ module cosmosDb 'br/public:avm/res/document-db/database-account:0.6.1' = if (useAuthentication && useChatHistoryCosmos ) {
692+ name : 'cosmosdb'
693+ scope : cosmosDbResourceGroup
694+ params : {
695+ name : !empty (cosmosDbAccountName ) ? cosmosDbAccountName : '${abbrs .documentDBDatabaseAccounts }${resourceToken }'
696+ location : !empty (cosmosDbLocation ) ? cosmosDbLocation : location
697+ locations : [
698+ {
699+ locationName : !empty (cosmosDbLocation ) ? cosmosDbLocation : location
700+ failoverPriority : 0
701+ isZoneRedundant : false
702+ }
703+ ]
704+ enableFreeTier : cosmosDbSkuName == 'free'
705+ capabilitiesToAdd : cosmosDbSkuName == 'serverless' ? ['EnableServerless' ] : []
706+ networkRestrictions : {
707+ ipRules : []
708+ networkAclBypass : bypass
709+ publicNetworkAccess : publicNetworkAccess
710+ virtualNetworkRules : []
711+ }
712+ sqlDatabases : [
713+ {
714+ name : chatHistoryDatabaseName
715+ throughput : (cosmosDbSkuName == 'serverless' ) ? null : cosmosDbThroughput
716+ containers : [
717+ {
718+ name : chatHistoryContainerName
719+ paths : [
720+ '/entra_oid'
721+ ]
722+ indexingPolicy : {
723+ indexingMode : 'consistent'
724+ automatic : true
725+ includedPaths : [
726+ {
727+ path : '/*'
728+ }
729+ ]
730+ excludedPaths : [
731+ {
732+ path : '/title/?'
733+ }
734+ {
735+ path : '/answers/*'
736+ }
737+ {
738+ path : '/"_etag"/?'
739+ }
740+ ]
741+ }
742+ }
743+ ]
744+ }
745+ ]
746+ }
747+ }
748+
671749// USER ROLES
672750var principalType = empty (runningOnGh ) && empty (runningOnAdo ) ? 'User' : 'ServicePrincipal'
673751
@@ -762,6 +840,31 @@ module searchSvcContribRoleUser 'core/security/role.bicep' = {
762840 }
763841}
764842
843+ module cosmosDbAccountContribRoleUser 'core/security/role.bicep' = if (useAuthentication && useChatHistoryCosmos ) {
844+ scope : cosmosDbResourceGroup
845+ name : 'cosmosdb-account-contrib-role-user'
846+ params : {
847+ principalId : principalId
848+ roleDefinitionId : '5bd9cd88-fe45-4216-938b-f97437e15450'
849+ principalType : principalType
850+ }
851+ }
852+
853+ // RBAC for Cosmos DB
854+ // https://learn.microsoft.com/azure/cosmos-db/nosql/security/how-to-grant-data-plane-role-based-access
855+ module cosmosDbDataContribRoleUser 'core/security/documentdb-sql-role.bicep' = if (useAuthentication && useChatHistoryCosmos ) {
856+ scope : cosmosDbResourceGroup
857+ name : 'cosmosdb-data-contrib-role-user'
858+ params : {
859+ databaseAccountName : (useAuthentication && useChatHistoryCosmos ) ? cosmosDb .outputs .name : ''
860+ principalId : principalId
861+ // Cosmos DB Built-in Data Contributor role
862+ roleDefinitionId : (useAuthentication && useChatHistoryCosmos )
863+ ? '/${subscription ().id }/resourceGroups/${cosmosDb .outputs .resourceGroupName }/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDb .outputs .name }/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002'
864+ : ''
865+ }
866+ }
867+
765868// SYSTEM IDENTITIES
766869module openAiRoleBackend 'core/security/role.bicep' = if (isAzureOpenAiHost && deployAzureOpenAi ) {
767870 scope : openAiResourceGroup
@@ -845,6 +948,23 @@ module speechRoleBackend 'core/security/role.bicep' = {
845948 }
846949}
847950
951+ // RBAC for Cosmos DB
952+ // https://learn.microsoft.com/azure/cosmos-db/nosql/security/how-to-grant-data-plane-role-based-access
953+ module cosmosDbRoleBackend 'core/security/documentdb-sql-role.bicep' = if (useAuthentication && useChatHistoryCosmos ) {
954+ scope : cosmosDbResourceGroup
955+ name : 'cosmosdb-role-backend'
956+ params : {
957+ databaseAccountName : (useAuthentication && useChatHistoryCosmos ) ? cosmosDb .outputs .name : ''
958+ principalId : (deploymentTarget == 'appservice' )
959+ ? backend .outputs .identityPrincipalId
960+ : acaBackend .outputs .identityPrincipalId
961+ // Cosmos DB Built-in Data Contributor role
962+ roleDefinitionId : (useAuthentication && useChatHistoryCosmos )
963+ ? '/${subscription ().id }/resourceGroups/${cosmosDb .outputs .resourceGroupName }/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosDb .outputs .name }/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002'
964+ : ''
965+ }
966+ }
967+
848968module isolation 'network-isolation.bicep' = {
849969 name : 'networks'
850970 scope : resourceGroup
@@ -891,6 +1011,11 @@ var otherPrivateEndpointConnections = (usePrivateEndpoint && deploymentTarget ==
8911011 dnsZoneName : 'privatelink.azurewebsites.net'
8921012 resourceIds : [backend .outputs .id ]
8931013 }
1014+ {
1015+ groupId : 'cosmosdb'
1016+ dnsZoneName : 'privatelink.documents.azure.com'
1017+ resourceIds : (useAuthentication && useChatHistoryCosmos ) ? [cosmosDb .outputs .resourceId ] : []
1018+ }
8941019 ]
8951020 : []
8961021
@@ -997,6 +1122,10 @@ output AZURE_SEARCH_SERVICE_RESOURCE_GROUP string = searchServiceResourceGroup.n
9971122output AZURE_SEARCH_SEMANTIC_RANKER string = actualSearchServiceSemanticRankerLevel
9981123output AZURE_SEARCH_SERVICE_ASSIGNED_USERID string = searchService .outputs .principalId
9991124
1125+ output AZURE_COSMOSDB_ACCOUNT string = (useAuthentication && useChatHistoryCosmos ) ? cosmosDb .outputs .name : ''
1126+ output AZURE_CHAT_HISTORY_DATABASE string = chatHistoryDatabaseName
1127+ output AZURE_CHAT_HISTORY_CONTAINER string = chatHistoryContainerName
1128+
10001129output AZURE_STORAGE_ACCOUNT string = storage .outputs .name
10011130output AZURE_STORAGE_CONTAINER string = storageContainerName
10021131output AZURE_STORAGE_RESOURCE_GROUP string = storageResourceGroup .name
0 commit comments