Skip to content

Commit bbc74de

Browse files
committed
Add a Cosmos DB resource for chat history
1 parent b60274a commit bbc74de

File tree

3 files changed

+177
-3
lines changed

3 files changed

+177
-3
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
metadata name = 'DocumentDB Database Account SQL Role Assignments.'
2+
metadata description = 'Assign RBAC role for data plane access to Azure Cosmos DB for NoSQL.'
3+
4+
@description('Name of the Azure Cosmos DB for NoSQL account.')
5+
param databaseAccountName string
6+
7+
@description('Id of the identity/principal to assign this role in the context of the account.')
8+
param principalId string = ''
9+
10+
@description('Id of the role definition to assign to the targeted principal in the context of the account.')
11+
param roleDefinitionId string
12+
13+
resource databaseAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' existing = {
14+
name: databaseAccountName
15+
}
16+
17+
resource sqlRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15' = {
18+
name: guid(databaseAccount.id, principalId, roleDefinitionId)
19+
parent: databaseAccount
20+
properties: {
21+
principalId: principalId
22+
roleDefinitionId: roleDefinitionId
23+
scope: databaseAccount.id
24+
}
25+
}
26+
27+
output sqlRoleAssignmentId string = sqlRoleAssignment.id

infra/main.bicep

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ param speechServiceName string = ''
5959
param speechServiceSkuName string // Set in main.parameters.json
6060
param 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
198207
param useSpeechOutputAzure bool = false
199208
@description('Use chat history feature in browser')
200209
param 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')
202213
param 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
270285
module 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
672750
var 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
766869
module 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+
848968
module 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
9971122
output AZURE_SEARCH_SEMANTIC_RANKER string = actualSearchServiceSemanticRankerLevel
9981123
output 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+
10001129
output AZURE_STORAGE_ACCOUNT string = storage.outputs.name
10011130
output AZURE_STORAGE_CONTAINER string = storageContainerName
10021131
output AZURE_STORAGE_RESOURCE_GROUP string = storageResourceGroup.name

infra/main.parameters.json

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,6 @@
185185
"useSpeechOutputAzure": {
186186
"value": "${USE_SPEECH_OUTPUT_AZURE=false}"
187187
},
188-
"useChatHistoryBrowser": {
189-
"value": "${USE_CHAT_HISTORY_BROWSER=false}"
190-
},
191188
"speechServiceName": {
192189
"value": "${AZURE_SPEECH_SERVICE}"
193190
},
@@ -200,6 +197,27 @@
200197
"speechServiceLocation": {
201198
"value": "${AZURE_SPEECH_SERVICE_LOCATION}"
202199
},
200+
"useChatHistoryBrowser": {
201+
"value": "${USE_CHAT_HISTORY_BROWSER=false}"
202+
},
203+
"useChatHistoryCosmos": {
204+
"value": "${USE_CHAT_HISTORY_COSMOS=false}"
205+
},
206+
"cosmosDbSkuName": {
207+
"value": "${AZURE_COSMOSDB_SKU=serverless}"
208+
},
209+
"cosmodDbResourceGroupName": {
210+
"value": "${AZURE_COSMOSDB_RESOURCE_GROUP}"
211+
},
212+
"cosmosDbLocation": {
213+
"value": "${AZURE_COSMOSDB_LOCATION}"
214+
},
215+
"cosmosDbAccountName": {
216+
"value": "${AZURE_COSMOSDB_ACCOUNT}"
217+
},
218+
"cosmosDbThroughput": {
219+
"value": "${AZURE_COSMOSDB_THROUGHPUT}"
220+
},
203221
"useAuthentication": {
204222
"value": "${AZURE_USE_AUTHENTICATION=false}"
205223
},

0 commit comments

Comments
 (0)