2024-07-15 19:42:22 -04:00

346 lines
12 KiB
Bicep

// 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 group deployment using Azure CLI (az deployment group ...)
The script will deploy the following resources in a specified resource group:
AI Search
CosmosDB
Blob Storage
AKS
API Management
Log Analytics
Private Endpoints
Managed Identity
*/
@description('Unique name to append to each resource')
param resourceBaseName string = ''
var resourceBaseNameFinal = !empty(resourceBaseName) ? resourceBaseName : toLower(uniqueString('${subscription().id}/resourceGroups/${graphRagName}'))
@minLength(1)
@maxLength(64)
@description('Name of the resource group that GraphRAG will be deployed in.')
param graphRagName string
@description('Cloud location for all resources')
param location string = resourceGroup().location
@minLength(1)
@description('Name of the publisher of the API Management instance')
param publisherName string
@minLength(1)
@description('Email address of the publisher of the API Management instance')
param publisherEmail string
@description('The AKS namespace the workload identity service account will be created in.')
param aksNamespace string = 'graphrag'
@description('Public key to allow access to AKS Linux nodes.')
param aksSshRsaPublicKey string
@description('Whether to enable private endpoints.')
param enablePrivateEndpoints bool = true
param apimName string = ''
param storageAccountName string = ''
param cosmosDbName string = ''
param aiSearchName string = ''
var graphRagDnsLabel = 'graphrag'
var dnsDomain = 'graphrag.io'
var graphRagHostname = '${graphRagDnsLabel}.${dnsDomain}'
var graphRagUrl = 'http://${graphRagHostname}'
var abbrs = loadJsonContent('abbreviations.json')
var tags = { 'azd-env-name': graphRagName }
var workloadIdentityName = '${abbrs.managedIdentityUserAssignedIdentities}${resourceBaseNameFinal}'
var aksServiceAccountName = '${aksNamespace}-workload-sa'
var workloadIdentitySubject = 'system:serviceaccount:${aksNamespace}:${aksServiceAccountName}'
var roles = {
storageBlobDataContributor: resourceId(
'Microsoft.Authorization/roleDefinitions',
'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
)
storageQueueDataContributor: resourceId(
'Microsoft.Authorization/roleDefinitions',
'974c5e8b-45b9-4653-ba55-5f855dd0fb88'
)
aiSearchContributor: resourceId(
'Microsoft.Authorization/roleDefinitions',
'b24988ac-6180-42a0-ab88-20f7382dd24c' // AI Search Contributor Role
)
aiSearchIndexDataContributor: resourceId(
'Microsoft.Authorization/roleDefinitions',
'8ebe5a00-799e-43f5-93ac-243d3dce84a7' // AI Search Index Data Contributor Role
)
aiSearchIndexDataReader: resourceId (
'Microsoft.Authorization/roleDefinitions',
'1407120a-92aa-4202-b7e9-c0e197c71c8f' // AI Search Index Data Reader Role
)
}
module log 'core/log-analytics/log.bicep' = {
name: 'log'
scope: resourceGroup()
params:{
name: '${abbrs.operationalInsightsWorkspaces}${resourceBaseNameFinal}'
location: location
publicNetworkAccessForIngestion: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
}
}
module aks 'core/aks/aks.bicep' = {
name: 'aks'
scope: resourceGroup()
params:{
clusterName: '${abbrs.containerServiceManagedClusters}${resourceBaseNameFinal}'
location: location
sshRSAPublicKey: aksSshRsaPublicKey
logAnalyticsWorkspaceId: log.outputs.id
}
}
module cosmosdb 'core/cosmosdb/cosmosdb.bicep' = {
name: 'cosmosdb'
scope: resourceGroup()
params: {
cosmosDbName: !empty(cosmosDbName) ? cosmosDbName : '${abbrs.documentDBDatabaseAccounts}${resourceBaseNameFinal}'
location: location
publicNetworkAccess: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
principalId: workloadIdentity.outputs.principal_id
}
}
module aiSearch 'core/ai-search/ai-search.bicep' = {
name: 'aisearch'
scope: resourceGroup()
params: {
name: !empty(aiSearchName) ? aiSearchName : '${abbrs.searchSearchServices}${resourceBaseNameFinal}'
location: location
publicNetworkAccess: enablePrivateEndpoints ? 'disabled' : 'enabled'
roleAssignments: [
{
principalId: workloadIdentity.outputs.principal_id
principalType: 'ServicePrincipal'
roleDefinitionId: roles.aiSearchContributor
}
{
principalId: workloadIdentity.outputs.principal_id
principalType: 'ServicePrincipal'
roleDefinitionId: roles.aiSearchIndexDataContributor
}
{
principalId: workloadIdentity.outputs.principal_id
principalType: 'ServicePrincipal'
roleDefinitionId: roles.aiSearchIndexDataReader
}
]
}
}
module storage 'core/blob/storage.bicep' = {
name: 'storage'
scope: resourceGroup()
params: {
name: !empty(storageAccountName) ? storageAccountName : '${abbrs.storageStorageAccounts}${replace(resourceBaseNameFinal, '-', '')}'
location: location
publicNetworkAccess: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
tags: tags
roleAssignments: [
{
principalId: workloadIdentity.outputs.principal_id
principalType: 'ServicePrincipal'
roleDefinitionId: roles.storageBlobDataContributor
}
{
principalId: workloadIdentity.outputs.principal_id
principalType: 'ServicePrincipal'
roleDefinitionId: roles.storageQueueDataContributor
}
]
deleteRetentionPolicy: {
enabled: true
days: 5
}
defaultToOAuthAuthentication: true
}
}
module apim 'core/apim/apim.bicep' = {
name: 'apim'
scope: resourceGroup()
params: {
apiManagementName: !empty(apimName) ? apimName : '${abbrs.apiManagementService}${resourceBaseNameFinal}'
appInsightsName: '${abbrs.insightsComponents}${resourceBaseNameFinal}'
appInsightsPublicNetworkAccessForIngestion: enablePrivateEndpoints ? 'Disabled' : 'Enabled'
nsgName: '${abbrs.networkNetworkSecurityGroups}${resourceBaseNameFinal}'
publicIpName: '${abbrs.networkPublicIPAddresses}${resourceBaseNameFinal}'
virtualNetworkName: '${abbrs.networkVirtualNetworks}${resourceBaseNameFinal}'
location: location
sku: 'Developer'
skuCount: 1 // TODO expose in param for premium sku
availabilityZones: [] // TODO expose in param for premium sku
publisherEmail: publisherEmail
publisherName: publisherName
logAnalyticsWorkspaceId: log.outputs.id
}
}
module graphragApi 'core/apim/apim.graphrag-documentation.bicep' = {
name: 'apimservice'
scope: resourceGroup()
params: {
apimname: apim.outputs.name
backendUrl: graphRagUrl
}
}
module workloadIdentity 'core/identity/identity.bicep' = {
name: 'workloadIdentity'
scope: resourceGroup()
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'
scope: resourceGroup()
params: {
name: dnsDomain
vnetNames: [
apim.outputs.vnetName
]
}
}
module privatelinkPrivateDns 'core/vnet/privatelink-private-dns-zones.bicep' = if (enablePrivateEndpoints) {
name: 'privatelink-private-dns-zones'
scope: resourceGroup()
params: {
linkedVnetResourceIds: [
apim.outputs.vnetId
]
}
}
module azureMonitorPrivateLinkScope 'core/monitor/private-link-scope.bicep' = if (enablePrivateEndpoints) {
name: 'azureMonitorPrivateLinkScope'
scope: resourceGroup()
params: {
privateLinkScopeName: 'pls-${resourceBaseNameFinal}'
privateLinkScopedResources: [
log.outputs.id
apim.outputs.appInsightsId
]
}
}
module cosmosDbPrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'cosmosDbPrivateEndpoint'
scope: resourceGroup()
params: {
privateEndpointName: '${abbrs.privateEndpoint}cosmos-${cosmosdb.outputs.name}'
location: location
privateLinkServiceId: cosmosdb.outputs.id
subnetId: apim.outputs.defaultSubnetId
groupId: 'Sql'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.cosmosDbPrivateDnsZoneConfigs : []
}
}
module blobStoragePrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'blobStoragePrivateEndpoint'
scope: resourceGroup()
params: {
privateEndpointName: '${abbrs.privateEndpoint}blob-${storage.outputs.name}'
location: location
privateLinkServiceId: storage.outputs.id
subnetId: apim.outputs.defaultSubnetId
groupId: 'blob'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.blobStoragePrivateDnsZoneConfigs : []
}
}
module queueStoragePrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'queueStoragePrivateEndpoint'
scope: resourceGroup()
params: {
privateEndpointName: '${abbrs.privateEndpoint}queue-${storage.outputs.name}'
location: location
privateLinkServiceId: storage.outputs.id
subnetId: apim.outputs.defaultSubnetId
groupId: 'queue'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.queueStoragePrivateDnsZoneConfigs : []
}
}
module aiSearchPrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'aiSearchPrivateEndpoint'
scope: resourceGroup()
params: {
privateEndpointName: '${abbrs.privateEndpoint}search-${aiSearch.outputs.name}'
location: location
privateLinkServiceId: aiSearch.outputs.id
subnetId: apim.outputs.defaultSubnetId
groupId: 'searchService'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.aiSearchPrivateDnsZoneConfigs : []
}
}
module privateLinkScopePrivateEndpoint 'core/vnet/private-endpoint.bicep' = if (enablePrivateEndpoints) {
name: 'privateLinkScopePrivateEndpoint'
scope: resourceGroup()
params: {
privateEndpointName: '${abbrs.privateEndpoint}pls-${resourceBaseNameFinal}'
location: location
privateLinkServiceId: enablePrivateEndpoints ? azureMonitorPrivateLinkScope.outputs.privateLinkScopeId : ''
subnetId: apim.outputs.defaultSubnetId
groupId: 'azuremonitor'
privateDnsZoneConfigs: enablePrivateEndpoints ? privatelinkPrivateDns.outputs.azureMonitorPrivateDnsZoneConfigs : []
}
}
output azure_location string = location
output azure_tenant_id string = tenant().tenantId
output azure_ai_search_name string = aiSearch.outputs.name
output azure_aks_name string = aks.outputs.name
output azure_aks_controlplanefqdn string = aks.outputs.controlPlaneFQDN
output azure_aks_managed_rg string = aks.outputs.managedResourceGroup
output azure_aks_service_account_name string = aksServiceAccountName
output azure_storage_account string = storage.outputs.name
output azure_storage_account_blob_url string = storage.outputs.primaryEndpoints.blob
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_app_insights_connection_string string = apim.outputs.appInsightsConnectionString
output azure_apim_name string = apim.outputs.name
output azure_apim_url string = apim.outputs.apimGatewayUrl
output azure_apim_vnet_name string = apim.outputs.vnetName
output azure_apim_vnet_id string = apim.outputs.vnetId
output azure_dns_zone_name string = privateDnsZone.outputs.dns_zone_name
output azure_graphrag_hostname string = graphRagHostname
output azure_graphrag_url string = graphRagUrl
output azure_workload_identity_client_id string = workloadIdentity.outputs.client_id
output azure_workload_identity_principal_id string = workloadIdentity.outputs.principal_id
output azure_workload_identity_name string = workloadIdentity.outputs.name
output azure_private_dns_zones array = enablePrivateEndpoints ? union(
privatelinkPrivateDns.outputs.privateDnsZones,
[privateDnsZone.outputs.dns_zone_name]
) : []