508 lines
19 KiB
Bicep
Raw Permalink Normal View History

2024-06-26 15:45:06 -04:00
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/*
This bicep script can be used as a starting point and customized to fit the specific needs of an Azure environment.
The script should be executed as a resource group deployment using Azure CLI (az deployment group ...)
2024-06-26 15:45:06 -04:00
The script will deploy the following resources in a specified resource group:
AI Search
Application Insights
Azure OpenAI (optional)
Azure Container Registry (optional)
2024-06-26 15:45:06 -04:00
CosmosDB
Blob Storage
Azure Kubernetes Service
2024-06-26 15:45:06 -04:00
API Management
Log Analytics
Private Endpoints
Managed Identity
*/
@minLength(1)
@maxLength(64)
@description('Name of the resource group that GraphRAG will be deployed in.')
param resourceGroupName string = az.resourceGroup().name
// optional parameters with reasonable defaults unless explicitly overridden (most names will be auto-generated if not provided)
@description('Unique name to append to each resource')
param resourceBaseName string = ''
var resourceBaseNameFinal = !empty(resourceBaseName) ? resourceBaseName : toLower(uniqueString(az.resourceGroup().id))
2024-06-26 15:45:06 -04:00
@description('Cloud region for all resources')
param location string = az.resourceGroup().location
2024-06-26 15:45:06 -04:00
//
// start APIM parameters
//
2024-06-26 15:45:06 -04:00
@minLength(1)
@description('Name of the publisher of the API Management service.')
param apiPublisherName string = 'Microsoft'
2024-06-26 15:45:06 -04:00
@minLength(1)
@description('Email address of the publisher of the API Management service.')
param apiPublisherEmail string = 'publisher@microsoft.com'
2024-06-26 15:45:06 -04:00
@description('Whether or not to restore the API Management service from a soft-deleted state.')
2024-08-09 22:22:49 -04:00
param restoreAPIM bool = false
param apimTier string = 'Developer'
param apimName string = ''
//
// end APIM parameters
//
//
// start ACR parameters
//
@description('Whether or not to deploy a new ACR resource instead of connecting to an existing service.')
param deployAcr bool = false
// if existing ACR is used, the login server endpoint (i.e. <registry>.azurecr.io) must be provided
param existingAcrLoginServer string = ''
@description('The ACR token username. This is only used if an existing ACR is used.')
param acrTokenName string = ''
@secure()
@description('The ACR token password. This is only used if an existing ACR is used.')
param acrTokenPassword string = ''
param graphragImageName string = 'graphrag'
param graphragImageVersion string = 'latest'
//
// end ACR parameters
//
//
// start AOAI parameters
//
@description('Whether or not to deploy a new AOAI resource instead of connecting to an existing service.')
param deployAoai bool = true
@description('Resource id of an existing AOAI resource.')
param existingAoaiId string = ''
@description('Name of the AOAI LLM model to use. Must match official model id. For more information: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models')
@allowed(['gpt-4', 'gpt-4o', 'gpt-4o-mini'])
param llmModelName string = 'gpt-4o'
@description('Deployment name of the AOAI LLM model to use. For more information: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models')
param llmModelDeploymentName string = 'gpt-4o'
@description('Model version of the AOAI LLM model to use.')
@allowed(['2024-08-06', 'turbo-2024-04-09'])
param llmModelVersion string = '2024-08-06'
@description('Quota of the AOAI LLM model to use.')
@minValue(1)
param llmModelQuota int = 1
@description('Name of the AOAI embedding model to use. Must match official model id. For more information: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models')
@allowed(['text-embedding-ada-002', 'text-embedding-3-large'])
param embeddingModelName string = 'text-embedding-ada-002'
@description('Deployment name of the AOAI embedding model to use. Must match official model id. For more information: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models')
param embeddingModelDeploymentName string = 'text-embedding-ada-002'
@description('Model version of the AOAI embedding model to use.')
@allowed(['2', '1'])
param embeddingModelVersion string = '2'
@description('Quota of the AOAI embedding model to use.')
@minValue(1)
param embeddingModelQuota int = 1
//
// end AOAI parameters
//
//
// start AKS parameters
//
@description('The AKS namespace to install GraphRAG in.')
param aksNamespace string = 'graphrag'
2024-06-26 15:45:06 -04:00
var workloadIdentityName = '${abbrs.managedIdentityUserAssignedIdentities}${resourceBaseNameFinal}'
var aksServiceAccountName = '${aksNamespace}-workload-sa'
var workloadIdentitySubject = 'system:serviceaccount:${aksNamespace}:${aksServiceAccountName}'
var dnsDomain = 'graphrag.io'
var appHostname = 'graphrag.${dnsDomain}'
var appUrl = 'http://${appHostname}'
//
// end AKS parameters
//
var abbrs = loadJsonContent('abbreviations.json')
var tags = { 'azd-env-name': resourceGroupName }
param utcString string = utcNow()
@description('This parameter will only get defined during a managed app deployment.')
param managedAppStorageAccountName string = ''
@secure()
@description('This parameter will only get defined during a managed app deployment.')
param managedAppStorageAccountKey string = ''
@description('Whether to use private endpoint connections or not.')
param enablePrivateEndpoints bool = true
@description('Role definitions for various RBAC roles that will be assigned at deployment time. Learn more: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles')
2024-06-26 15:45:06 -04:00
var roles = {
acrPull: resourceId(
'Microsoft.Authorization/roleDefinitions',
'7f951dda-4ed3-4680-a7ca-43fe172d538d' // ACR Pull Role
)
networkContributor: resourceId(
'Microsoft.Authorization/roleDefinitions',
'4d97b98b-1d4f-4787-a291-c67834d212e7' // Network Contributor Role
)
privateDnsZoneContributor: resourceId(
'Microsoft.Authorization/roleDefinitions',
'b12aa53e-6015-4669-85d0-8515ebb3ae7f' // Private DNS Zone Contributor Role
)
2024-06-26 15:45:06 -04:00
}
// apply RBAC role assignments to the AKS workload identity
module aksWorkloadIdentityRBAC 'core/rbac/workload-identity-rbac.bicep' = {
name: 'aks-workload-identity-rbac-assignments'
params: {
principalId: workloadIdentity.outputs.principalId
principalType: 'ServicePrincipal'
aiSearchName: aiSearch.outputs.name
appInsightsName: appInsights.outputs.name
cosmosDbName: cosmosdb.outputs.name
storageName: storage.outputs.name
aoaiId: deployAoai ? aoai.outputs.id : existingAoaiId
}
}
// apply necessary RBAC role assignments to the AKS service
module aksRBAC 'core/rbac/aks-rbac.bicep' = {
name: 'aks-rbac-assignments'
params: {
roleAssignments: [
{
principalId: aks.outputs.kubeletPrincipalId
principalType: 'ServicePrincipal'
roleDefinitionId: roles.acrPull
}
{
principalId: aks.outputs.ingressWebAppIdentity
principalType: 'ServicePrincipal'
roleDefinitionId: roles.privateDnsZoneContributor
}
{
principalId: aks.outputs.systemIdentity
principalType: 'ServicePrincipal'
roleDefinitionId: roles.networkContributor
}
]
}
}
2024-06-26 15:45:06 -04:00
module log 'core/log-analytics/log.bicep' = {
name: 'log-analytics-deployment'
params: {
2024-06-26 15:45:06 -04:00
name: '${abbrs.operationalInsightsWorkspaces}${resourceBaseNameFinal}'
location: location
2024-07-15 16:42:22 -07:00
publicNetworkAccessForIngestion: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
2024-06-26 15:45:06 -04:00
}
}
module nsg 'core/vnet/nsg.bicep' = {
name: 'nsg-deployment'
params: {
nsgName: '${abbrs.networkNetworkSecurityGroups}${resourceBaseNameFinal}'
location: location
}
}
module vnet 'core/vnet/vnet.bicep' = {
name: 'vnet-deployment'
params: {
vnetName: '${abbrs.networkVirtualNetworks}${resourceBaseNameFinal}'
location: location
subnetPrefix: abbrs.networkVirtualNetworksSubnets
apimTier: apimTier
nsgID: nsg.outputs.id
}
}
module aoai 'core/aoai/aoai.bicep' = if (deployAoai) {
name: 'aoai-deployment'
params: {
openAiName: '${abbrs.cognitiveServicesAccounts}${resourceBaseNameFinal}'
location: location
llmModelName: llmModelName
llmModelDeploymentName: llmModelDeploymentName
llmModelVersion: llmModelVersion
llmTpmQuota: llmModelQuota
embeddingModelName: embeddingModelName
embeddingModelDeploymentName: embeddingModelDeploymentName
embeddingModelVersion: embeddingModelVersion
embeddingTpmQuota: embeddingModelQuota
}
}
module acr 'core/acr/acr.bicep' = if (deployAcr) {
name: 'acr-deployment'
params: {
registryName: '${abbrs.containerRegistryRegistries}${resourceBaseNameFinal}'
location: location
}
}
2024-06-26 15:45:06 -04:00
module aks 'core/aks/aks.bicep' = {
name: 'aks-deployment'
params: {
2024-06-26 15:45:06 -04:00
clusterName: '${abbrs.containerServiceManagedClusters}${resourceBaseNameFinal}'
location: location
graphragVMSize: 'standard_d8s_v5' // 8 vcpu, 32 GB memory
graphragIndexingVMSize: 'standard_e8s_v5' // 8 vcpus, 64 GB memory
clusterAdmins: [deployer().objectId]
2024-06-26 15:45:06 -04:00
logAnalyticsWorkspaceId: log.outputs.id
subnetId: vnet.outputs.aksSubnetId
privateDnsZoneName: privateDnsZone.outputs.name
2024-06-26 15:45:06 -04:00
}
}
module cosmosdb 'core/cosmosdb/cosmosdb.bicep' = {
name: 'cosmosdb-deployment'
2024-06-26 15:45:06 -04:00
params: {
cosmosDbName: '${abbrs.documentDBDatabaseAccounts}${resourceBaseNameFinal}'
2024-06-26 15:45:06 -04:00
location: location
publicNetworkAccess: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
}
}
module aiSearch 'core/ai-search/ai-search.bicep' = {
name: 'aisearch-deployment'
2024-06-26 15:45:06 -04:00
params: {
name: '${abbrs.searchSearchServices}${resourceBaseNameFinal}'
2024-06-26 15:45:06 -04:00
location: location
publicNetworkAccess: enablePrivateEndpoints ? 'disabled' : 'enabled'
}
}
module storage 'core/storage/storage.bicep' = {
name: 'storage-deployment'
2024-06-26 15:45:06 -04:00
params: {
name: '${abbrs.storageStorageAccounts}${replace(resourceBaseNameFinal, '-', '')}'
2024-06-26 15:45:06 -04:00
location: location
publicNetworkAccess: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
tags: tags
deleteRetentionPolicy: {
enabled: true
days: 5
}
defaultToOAuthAuthentication: true
}
}
module appInsights 'core/monitor/app-insights.bicep' = {
name: 'app-insights-deployment'
params: {
appInsightsName: '${abbrs.insightsComponents}${resourceBaseNameFinal}'
location: location
appInsightsPublicNetworkAccessForIngestion: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
logAnalyticsWorkspaceId: log.outputs.id
}
}
2024-06-26 15:45:06 -04:00
module apim 'core/apim/apim.bicep' = {
name: 'apim-deployment'
2024-06-26 15:45:06 -04:00
params: {
apiManagementName: !empty(apimName) ? apimName : '${abbrs.apiManagementService}${resourceBaseNameFinal}'
2024-08-09 22:22:49 -04:00
restoreAPIM: restoreAPIM
appInsightsId: appInsights.outputs.id
appInsightsInstrumentationKey: appInsights.outputs.instrumentationKey
2024-06-26 15:45:06 -04:00
publicIpName: '${abbrs.networkPublicIPAddresses}${resourceBaseNameFinal}'
location: location
sku: apimTier
2024-06-26 15:45:06 -04:00
skuCount: 1 // TODO expose in param for premium sku
availabilityZones: [] // TODO expose in param for premium sku
publisherEmail: apiPublisherEmail
publisherName: apiPublisherName
subnetId: vnet.outputs.apimSubnetId
2024-06-26 15:45:06 -04:00
}
}
module graphragDocsApi 'core/apim/apim.graphrag-docs-api.bicep' = {
name: 'graphrag-docs-api-deployment'
params: {
apiManagementName: apim.outputs.name
backendUrl: appUrl
}
}
module graphragApi 'core/apim/apim.graphrag-api.bicep' = if (!empty(managedAppStorageAccountName) && !empty(managedAppStorageAccountKey)) {
name: 'graphrag-api-deployment'
2024-06-26 15:45:06 -04:00
params: {
name: 'GraphRag'
apiManagementName: apim.outputs.name
backendUrl: appUrl
2024-06-26 15:45:06 -04:00
}
}
module workloadIdentity 'core/identity/identity.bicep' = {
name: 'workload-identity-deployment'
2024-06-26 15:45:06 -04:00
params: {
name: workloadIdentityName
location: location
federatedCredentials: {
'aks-workload-identity': {
issuer: aks.outputs.issuer
audiences: ['api://AzureADTokenExchange']
subject: workloadIdentitySubject
}
}
}
}
module privateDnsZone 'core/vnet/private-dns-zone.bicep' = {
name: 'private-dns-zone-deployment'
2024-06-26 15:45:06 -04:00
params: {
name: dnsDomain
vnetName: vnet.outputs.name
2024-06-26 15:45:06 -04:00
}
}
module privatelinkPrivateDns 'core/vnet/privatelink-private-dns-zones.bicep' = if (enablePrivateEndpoints) {
name: 'privatelink-private-dns-zones-deployment'
2024-06-26 15:45:06 -04:00
params: {
linkedVnetId: vnet.outputs.id
2024-06-26 15:45:06 -04:00
}
}
module azureMonitorPrivateLinkScope 'core/monitor/private-link-scope.bicep' = if (enablePrivateEndpoints) {
name: 'azure-monitor-privatelink-scope-deployment'
2024-06-26 15:45:06 -04:00
params: {
privateLinkScopeName: '${abbrs.networkPrivateLinkScope}${resourceBaseNameFinal}'
2024-06-26 15:45:06 -04:00
privateLinkScopedResources: [
log.outputs.id
appInsights.outputs.id
2024-06-26 15:45:06 -04:00
]
}
}
module cosmosDbPrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'cosmosDb-private-endpoint-deployment'
2024-06-26 15:45:06 -04:00
params: {
privateEndpointName: '${abbrs.privateEndpoint}cosmos-${cosmosdb.outputs.name}'
location: location
privateLinkServiceId: cosmosdb.outputs.id
subnetId: vnet.outputs.aksSubnetId
2024-06-26 15:45:06 -04:00
groupId: 'Sql'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.cosmosDbPrivateDnsZoneConfigs : []
}
}
module blobStoragePrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'blob-storage-private-endpoint-deployment'
2024-06-26 15:45:06 -04:00
params: {
privateEndpointName: '${abbrs.privateEndpoint}blob-${storage.outputs.name}'
location: location
privateLinkServiceId: storage.outputs.id
subnetId: vnet.outputs.aksSubnetId
2024-06-26 15:45:06 -04:00
groupId: 'blob'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.blobStoragePrivateDnsZoneConfigs : []
}
}
module aiSearchPrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'ai-search-private-endpoint-deployment'
2024-06-26 15:45:06 -04:00
params: {
privateEndpointName: '${abbrs.privateEndpoint}search-${aiSearch.outputs.name}'
location: location
privateLinkServiceId: aiSearch.outputs.id
subnetId: vnet.outputs.aksSubnetId
2024-06-26 15:45:06 -04:00
groupId: 'searchService'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.aiSearchPrivateDnsZoneConfigs : []
}
}
module privateLinkScopePrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'privatelink-scope-private-endpoint-deployment'
2024-06-26 15:45:06 -04:00
params: {
privateEndpointName: '${abbrs.privateEndpoint}pls-${resourceBaseNameFinal}'
location: location
privateLinkServiceId: enablePrivateEndpoints ? azureMonitorPrivateLinkScope.outputs.id : ''
subnetId: vnet.outputs.aksSubnetId
2024-06-26 15:45:06 -04:00
groupId: 'azuremonitor'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.azureMonitorPrivateDnsZoneConfigs : []
}
}
// The following deploymentScript is meant to only get deployed during a managed app deployment.
// Will not be deployed when performing a manual deployment with the deploy.sh script
module deploymentScript 'core/scripts/deployment-script.bicep' = if (!empty(managedAppStorageAccountName) && !empty(managedAppStorageAccountKey)) {
name: 'deployScript-deployment-${utcString}'
params: {
location: location
script_file: loadTextContent('managed-app/scripts/install-graphrag.sh')
public_storage_account_name: managedAppStorageAccountName
public_storage_account_key: managedAppStorageAccountKey
acr_login_server: existingAcrLoginServer
acr_token_name: acrTokenName
acr_token_password: acrTokenPassword
ai_search_name: aiSearch.name
aks_name: aks.outputs.name
aks_service_account_name: aksServiceAccountName
deployAoai: deployAoai
aoai_endpoint: aksWorkloadIdentityRBAC.outputs.aoaiEndpoint
aoai_llm_model: deployAoai ? aoai.outputs.llmModel : llmModelName
aoai_llm_model_deployment_name: deployAoai ? aoai.outputs.llmModelDeploymentName : llmModelDeploymentName
aoai_llm_model_version: deployAoai ? aoai.outputs.llmModelVersion : llmModelVersion
aoai_embedding_model: deployAoai ? aoai.outputs.embeddingModel : embeddingModelName
aoai_embedding_model_deployment_name: deployAoai
? aoai.outputs.embeddingModelDeploymentName
: embeddingModelDeploymentName
app_hostname: appHostname
app_insights_connection_string: appInsights.outputs.connectionString
cosmosdb_endpoint: cosmosdb.outputs.endpoint
image_name: graphragImageName
image_version: graphragImageVersion
storage_account_blob_url: storage.outputs.primaryEndpoints.blob
utcValue: utcString
workload_identity_client_id: workloadIdentity.outputs.clientId
}
}
output deployer_principal_id string = deployer().objectId
2024-06-26 15:45:06 -04:00
output azure_location string = location
output azure_tenant_id string = tenant().tenantId
output azure_ai_search_name string = aiSearch.outputs.name
output azure_acr_login_server string = deployAcr ? acr.outputs.loginServer : existingAcrLoginServer
output azure_acr_name string = deployAcr ? acr.outputs.name : ''
2024-06-26 15:45:06 -04:00
output azure_aks_name string = aks.outputs.name
output azure_aks_controlplanefqdn string = aks.outputs.controlPlaneFqdn
2024-06-26 15:45:06 -04:00
output azure_aks_managed_rg string = aks.outputs.managedResourceGroup
output azure_aks_service_account_name string = aksServiceAccountName
// conditionally output AOAI endpoint information if it was deployed
output azure_aoai_endpoint string = deployAoai ? aoai.outputs.endpoint : aksWorkloadIdentityRBAC.outputs.aoaiEndpoint
output azure_aoai_llm_model string = deployAoai ? aoai.outputs.llmModel : llmModelName
output azure_aoai_llm_model_deployment_name string = deployAoai
? aoai.outputs.llmModelDeploymentName
: llmModelDeploymentName
output azure_aoai_llm_model_quota int = deployAoai ? aoai.outputs.llmModelQuota : 0
output azure_aoai_llm_model_api_version string = deployAoai ? aoai.outputs.llmModelVersion : llmModelVersion
output azure_aoai_embedding_model string = deployAoai ? aoai.outputs.embeddingModel : embeddingModelName
output azure_aoai_embedding_model_deployment_name string = deployAoai
? aoai.outputs.embeddingModelDeploymentName
: embeddingModelDeploymentName
output azure_aoai_embedding_model_quota int = deployAoai ? aoai.outputs.embeddingModelQuota : 0
output azure_aoai_embedding_model_api_version string = deployAoai
? aoai.outputs.embeddingModelVersion
: embeddingModelVersion
output azure_apim_gateway_url string = apim.outputs.apimGatewayUrl
output azure_apim_name string = apim.outputs.name
output azure_app_hostname string = appHostname
output azure_app_url string = appUrl
output azure_app_insights_connection_string string = appInsights.outputs.connectionString
2024-06-26 15:45:06 -04:00
output azure_cosmosdb_endpoint string = cosmosdb.outputs.endpoint
output azure_cosmosdb_name string = cosmosdb.outputs.name
output azure_cosmosdb_id string = cosmosdb.outputs.id
output azure_dns_zone_name string = privateDnsZone.outputs.name
output azure_private_dns_zones array = enablePrivateEndpoints
? union(privatelinkPrivateDns.outputs.privateDnsZones, [privateDnsZone.outputs.name])
: []
output azure_storage_account string = storage.outputs.name
output azure_storage_account_blob_url string = storage.outputs.primaryEndpoints.blob
output azure_workload_identity_client_id string = workloadIdentity.outputs.clientId
output azure_workload_identity_principal_id string = workloadIdentity.outputs.principalId
output azure_workload_identity_name string = workloadIdentity.outputs.name