@@ -59,6 +59,15 @@ param speechServiceName string = ''
59
59
param speechServiceSkuName string // Set in main.parameters.json
60
60
param useGPT4V bool = false
61
61
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
+
62
71
@description ('Location for the OpenAI resource group' )
63
72
@allowed ([
64
73
'canadaeast'
@@ -198,6 +207,8 @@ param useSpeechOutputBrowser bool = false
198
207
param useSpeechOutputAzure bool = false
199
208
@description ('Use chat history feature in browser' )
200
209
param useChatHistoryBrowser bool = false
210
+ @description ('Use chat history feature in CosmosDB' )
211
+ param useChatHistoryCosmos bool = false
201
212
@description ('Show options to use vector embeddings for searching in the app UI' )
202
213
param useVectors bool = false
203
214
@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
266
277
name : !empty (speechServiceResourceGroupName ) ? speechServiceResourceGroupName : resourceGroup .name
267
278
}
268
279
280
+ resource cosmosDbResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing = if (!empty (cosmodDbResourceGroupName )) {
281
+ name : !empty (cosmodDbResourceGroupName ) ? cosmodDbResourceGroupName : resourceGroup .name
282
+ }
283
+
269
284
// Monitor application with Azure Monitor
270
285
module monitoring 'core/monitor/monitoring.bicep' = if (useApplicationInsights ) {
271
286
name : 'monitoring'
@@ -329,7 +344,12 @@ var appEnvVariables = {
329
344
USE_SPEECH_INPUT_BROWSER : useSpeechInputBrowser
330
345
USE_SPEECH_OUTPUT_BROWSER : useSpeechOutputBrowser
331
346
USE_SPEECH_OUTPUT_AZURE : useSpeechOutputAzure
347
+ // Chat history settings
332
348
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
333
353
// Shared by all OpenAI deployments
334
354
OPENAI_HOST : openAiHost
335
355
AZURE_OPENAI_EMB_MODEL_NAME : embedding .modelName
@@ -668,6 +688,64 @@ module userStorage 'core/storage/storage-account.bicep' = if (useUserUpload) {
668
688
}
669
689
}
670
690
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
+
671
749
// USER ROLES
672
750
var principalType = empty (runningOnGh ) && empty (runningOnAdo ) ? 'User' : 'ServicePrincipal'
673
751
@@ -762,6 +840,31 @@ module searchSvcContribRoleUser 'core/security/role.bicep' = {
762
840
}
763
841
}
764
842
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
+
765
868
// SYSTEM IDENTITIES
766
869
module openAiRoleBackend 'core/security/role.bicep' = if (isAzureOpenAiHost && deployAzureOpenAi ) {
767
870
scope : openAiResourceGroup
@@ -845,6 +948,23 @@ module speechRoleBackend 'core/security/role.bicep' = {
845
948
}
846
949
}
847
950
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
+
848
968
module isolation 'network-isolation.bicep' = {
849
969
name : 'networks'
850
970
scope : resourceGroup
@@ -891,6 +1011,11 @@ var otherPrivateEndpointConnections = (usePrivateEndpoint && deploymentTarget ==
891
1011
dnsZoneName : 'privatelink.azurewebsites.net'
892
1012
resourceIds : [backend .outputs .id ]
893
1013
}
1014
+ {
1015
+ groupId : 'cosmosdb'
1016
+ dnsZoneName : 'privatelink.documents.azure.com'
1017
+ resourceIds : (useAuthentication && useChatHistoryCosmos ) ? [cosmosDb .outputs .resourceId ] : []
1018
+ }
894
1019
]
895
1020
: []
896
1021
@@ -997,6 +1122,10 @@ output AZURE_SEARCH_SERVICE_RESOURCE_GROUP string = searchServiceResourceGroup.n
997
1122
output AZURE_SEARCH_SEMANTIC_RANKER string = actualSearchServiceSemanticRankerLevel
998
1123
output AZURE_SEARCH_SERVICE_ASSIGNED_USERID string = searchService .outputs .principalId
999
1124
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
+
1000
1129
output AZURE_STORAGE_ACCOUNT string = storage .outputs .name
1001
1130
output AZURE_STORAGE_CONTAINER string = storageContainerName
1002
1131
output AZURE_STORAGE_RESOURCE_GROUP string = storageResourceGroup .name
0 commit comments