diff --git a/infra/README.md b/infra/README.md index ff9034ee5..be086f02e 100644 --- a/infra/README.md +++ b/infra/README.md @@ -3,6 +3,9 @@ You can deploy the Kernel Memory infrastructure to Azure by clicking the button below. This will create required resources. We recommend to create a new resource group for each deployment. +> [!WARNING] +> During the deployment you must select Models that your Azure Subscription is allowed to use. + [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmicrosoft%2Fkernel-memory%2Fmain%2Finfra%2Fmain.json)
@@ -33,8 +36,11 @@ After the deployment is complete, you will see the following resources in your r - Container App - Managed Identity - Storage account +- Public IP address +- Application Gateway +- Virtual network, Subnets, Network interfaces, Private Links and Private DNS Zones -You can start using Kernel Memory immediately after deployment. Use `Application Url` from Container App instance page as Kernel Memory's endpoint. Refer [to this screenshot](./images/ACA-ApplicationUrl.png) if you need help finding Application Url value. +You can start using Kernel Memory immediately after deployment. Navigate to `Public IP address` deployed in your Resource group and find `IP address`. Refer [to this screenshot](./images/Pip.png) (red marked used to hide private information) if you need help finding IP address value. Kernel Memory web service is deployed with `AuthenticationType` set to `APIKey` and default API keys are random GUIDs. Each request requires the `Authorization` HTTP header, passing one of the two keys. @@ -55,7 +61,7 @@ Here is an example of how to create a `MemoryWebClient` instance and start using ```csharp var memory = new MemoryWebClient( - "https://km-service-example.example.azurecontainerapps.io", + "http://111.111.111.111/", apiKey: "...your WebServiceAuthorizationKey1..."); ``` diff --git a/infra/images/Pip.png b/infra/images/Pip.png new file mode 100644 index 000000000..fc8f35507 Binary files /dev/null and b/infra/images/Pip.png differ diff --git a/infra/main.bicep b/infra/main.bicep index 96059d90a..ccd1f17c9 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -3,23 +3,71 @@ @maxLength(6) param suffix string = substring(newGuid(), 0, 6) +@description('The tags to apply to all resources. Refer to https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/naming-and-tagging for best practices.') +param tags object = { + Application: 'Kernel-Memory' + Environment: 'Demo' +} + @description(''' -gpt-35-turbo-16k deployment model\'s Tokens-Per-Minute (TPM) capacity, measured in thousands. +Kernel Memory Docker Image Tag. Check available tags at https://hub.docker.com/r/kernelmemory/service/tags +''') +@minLength(3) +@maxLength(16) +param KernelMemoryImageTag string = 'latest' + +///////////////////////////// AI Model Params /////////////////////////////// + +@description(''' +ATTENTION: USE MODELS THAT YOUR AZURE SUBSCRIPTION IS ALLOWED TO USE. + + +Azure OpenAI Inference Model. https://learn.microsoft.com/en-gb/azure/ai-services/openai/concepts/models + +Default model version will be assigned. The default version is different for different models and might change when there is new version available for a model. +''') +@allowed([ + 'gpt-35-turbo-16k' + 'gpt-4' + 'gpt-4-32k' + 'gpt-4o' + 'gpt-4o-mini' +]) +param InferenceModel string = 'gpt-35-turbo-16k' + +@description(''' +Inference deployment model\'s Tokens-Per-Minute (TPM) capacity, measured in thousands. The default capacity is 30,000 TPM. For model limits specific to your region, refer to the documentation at https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-quota. ''') @minValue(1) @maxValue(40) -param chatGptDeploymentCapacity int = 30 +param InferenceModelDeploymentCapacity int = 30 + +@description(''' +ATTENTION: USE MODELS THAT YOUR AZURE SUBSCRIPTION IS ALLOWED TO USE. + +Azure OpenAI Embedding Model. https://learn.microsoft.com/azure/ai-services/openai/concepts/models#embeddings + +Default model version will be assigned. The default version is different for different models and might change when there is new version available for a model. +''') +@allowed([ + 'text-embedding-ada-002' + 'text-embedding-3-small' + 'text-embedding-3-large' +]) +param EmbeddingModel string = 'text-embedding-ada-002' @description(''' -text-embedding-ada-002 deployment model\'s Tokens-Per-Minute (TPM) capacity, measured in thousands. +Embedding deployment model\'s Tokens-Per-Minute (TPM) capacity, measured in thousands. The default capacity is 30,000 TPM. For model limits specific to your region, refer to the documentation at https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-quota. ''') @minValue(1) @maxValue(40) -param embeddingDeploymentCapacity int = 30 +param EmbeddingModelDeploymentCapacity int = 30 + +///////////////////////////// App Keys /////////////////////////////// @description(''' PLEASE CHOOSE A SECURE AND SECRET KEY ! - @@ -41,50 +89,49 @@ The value is stored as an environment variable and is required by the web servic @secure() param WebServiceAuthorizationKey2 string +///////////////////////////// Networking Params /////////////////////////////// + +@description(''' +Define the address space of your virtual network. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices +''') +param VirtualNetworkAddressSpace string = '10.0.0.0/16' + +@description(''' +Select an address space and configure your subnet for Infrastructure. You can also customize a subnet later. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/virtual-network-vnet-plan-design-arm#subnets +''') +param InfrastructureSubnetAddressRange string = '10.0.0.0/23' + +@description(''' +Select an address space and configure your subnet for Application Gateway. You can also customize a subnet later. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/virtual-network-vnet-plan-design-arm#subnets +''') +param ApplicationGatewaySubnetAddressRange string = '10.0.2.0/24' + +@description(''' +Select an address space and configure your subnet for Private Endpoints. You can also customize a subnet later. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/virtual-network-vnet-plan-design-arm#subnets +''') +param PrivateEndpointSubnetAddressRange string = '10.0.3.0/24' + +///////////////////////////////////////////////////////////////////////////// + var rg = resourceGroup() var location = resourceGroup().location -var chatGpt = { - modelName: 'gpt-35-turbo-16k' - deploymentName: 'chat' - deploymentVersion: '0613' - deploymentCapacity: chatGptDeploymentCapacity -} +///////////////////////////////////////////////////////////////////////////// -var embedding = { - modelName: 'text-embedding-ada-002' - deploymentName: 'embedding' - deploymentVersion: '2' - deploymentCapacity: embeddingDeploymentCapacity -} +module module_vnet 'modules/network/virtual-network.bicep' = { + name: 'module-vnet-${suffix}' + params: { + location: location + tags: tags + vnetName: 'km-vnet-${suffix}' -var openAiDeployments = [ - { - name: chatGpt.deploymentName - model: { - format: 'OpenAI' - name: chatGpt.modelName - version: chatGpt.deploymentVersion - } - sku: { - name: 'Standard' - capacity: chatGpt.deploymentCapacity - } - } - { - name: embedding.deploymentName - model: { - format: 'OpenAI' - name: embedding.modelName - version: embedding.deploymentVersion - } - sku: { - name: 'Standard' - capacity: embedding.deploymentCapacity - } + VirtualNetworkAddressSpace: VirtualNetworkAddressSpace + InfrastructureSubnetAddressRange: InfrastructureSubnetAddressRange + ApplicationGatewaySubnetAddressRange: ApplicationGatewaySubnetAddressRange + PrivateEndpointSubnetAddressRange: PrivateEndpointSubnetAddressRange } -] +} /* Module to create a Managed Identity. @@ -92,8 +139,8 @@ var openAiDeployments = [ The managed identity is the main code-to-services and service-to-service authentication mechanism. */ -module managedidentity 'modules/managed-identity.bicep' = { - name: 'managedidentity-${suffix}' +module module_managedidentity 'modules/identity/managed-identity.bicep' = { + name: 'module-managedidentity-${suffix}' scope: rg params: { location: location @@ -108,13 +155,18 @@ module managedidentity 'modules/managed-identity.bicep' = { The storage account is used to store files (KM Document Storage) and to run asynchronous ingestion (KM Pipelines Orchestration). */ -module storage 'modules/storage.bicep' = { - name: 'storage-${suffix}' +module module_storage 'modules/storage.bicep' = { + name: 'module-storage-${suffix}' scope: rg params: { location: location + tags: tags suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId + + vnetId: module_vnet.outputs.vnetId + privateEndpointSubnetId: module_vnet.outputs.privateEndpointSubnetId + + managedIdentityPrincipalId: module_managedidentity.outputs.managedIdentityPrincipalId } } @@ -125,14 +177,18 @@ module storage 'modules/storage.bicep' = { Azure AI Search is used to store document chunks and LLM embeddings, and to search for relevant data when searching memories and asking questions. */ -module search 'modules/ai-search.bicep' = { - name: 'search-${suffix}' +module module_search 'modules/cognitive/ai-search.bicep' = { + name: 'module-search-${suffix}' scope: rg params: { location: location - name: 'km-search-${suffix}' + tags: tags suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId + + vnetId: module_vnet.outputs.vnetId + privateEndpointSubnetId: module_vnet.outputs.privateEndpointSubnetId + + managedIdentityPrincipalId: module_managedidentity.outputs.managedIdentityPrincipalId } } @@ -143,17 +199,53 @@ module search 'modules/ai-search.bicep' = { Azure OpenAI is used to generate text embeddings, and to generate text from memories (answers and summaries) */ -module openAi 'modules/cognitive-services-openAI.bicep' = { - name: 'openai-${suffix}' +var InferenceDeploymentName = 'chat' +var EmbeddingDeploymentName = 'embedding' + +var openAiDeployments = [ + { + name: InferenceDeploymentName + model: { + format: 'OpenAI' + name: InferenceModel + // version: chatGpt.deploymentVersion + } + sku: { + name: 'Standard' + capacity: InferenceModelDeploymentCapacity + } + } + { + name: EmbeddingDeploymentName + model: { + format: 'OpenAI' + name: EmbeddingModel + // version: embedding.deploymentVersion + } + sku: { + name: 'Standard' + capacity: EmbeddingModelDeploymentCapacity + } + } +] +module module_openAi 'modules/cognitive/openAI.bicep' = { + name: 'module-openai-${suffix}' scope: rg params: { suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId + tags: tags + + vnetId: module_vnet.outputs.vnetId + privateEndpointSubnetId: module_vnet.outputs.privateEndpointSubnetId + + managedIdentityPrincipalId: module_managedidentity.outputs.managedIdentityPrincipalId + name: 'km-openai-${suffix}' location: location sku: { name: 'S0' } + publicNetworkAccess: 'Disabled' deployments: openAiDeployments } } @@ -163,12 +255,19 @@ module openAi 'modules/cognitive-services-openAI.bicep' = { See https://azure.microsoft.com/products/ai-services/ai-document-intelligence Azure Document Intelligence is used to extract text from images */ -module docIntel 'modules/cognitive-services-docIntel.bicep' = { - name: 'docIntel-${suffix}' +module module_docIntel 'modules/cognitive/docIntel.bicep' = { + name: 'module-docIntel-${suffix}' scope: rg params: { suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId + tags: tags + + vnetId: module_vnet.outputs.vnetId + privateEndpointSubnetId: module_vnet.outputs.privateEndpointSubnetId + + managedIdentityPrincipalId: module_managedidentity.outputs.managedIdentityPrincipalId + + publicNetworkAccess: 'Disabled' name: 'km-docIntel-${suffix}' location: location sku: { @@ -177,17 +276,35 @@ module docIntel 'modules/cognitive-services-docIntel.bicep' = { } } +/* + Module to create monitoring resources +*/ +module module_insights 'modules/monitoring/insights.bicep' = { + name: 'module-insights-${suffix}' + scope: rg + params: { + suffix: suffix + location: location + tags: tags + } +} + /* Module to create an Azure Container Apps environment and a container app See https://learn.microsoft.com/en-us/azure/container-apps/environment and https://azure.github.io/aca-dotnet-workshop/aca/10-aca-iac-bicep/iac-bicep/#2-define-an-azure-container-apps-environment for more samples */ -module containerAppsEnvironment 'modules/container-apps-environment.bicep' = { - name: 'containerAppsEnvironment-${suffix}' +module module_containerAppsEnvironment 'modules/host/container-app-env.bicep' = { + name: 'module-containerAppsEnvironment-${suffix}' scope: rg params: { location: location suffix: suffix + tags: tags + // network + acaSubnetId: module_vnet.outputs.envInfraSubnetId + logAnalyticsWorkspaceName: module_insights.outputs.logAnalyticsWorkspaceName + applicationInsightsName: module_insights.outputs.applicationInsightsName } } @@ -197,30 +314,51 @@ module containerAppsEnvironment 'modules/container-apps-environment.bicep' = { The Azure Container app hosts the docker container containing KM web service. */ -module containerAppService 'modules/container-app.bicep' = { - name: 'containerAppService-${suffix}' +module module_containerApp 'modules/host/container-app.bicep' = { + name: 'module-containerAppService-${suffix}' scope: rg params: { location: location suffix: suffix - containerAppsEnvironmentId: containerAppsEnvironment.outputs.containerAppsEnvironmentId - appInsightsInstrumentationKey: containerAppsEnvironment.outputs.applicationInsightsInstrumentationKey - applicationInsightsConnectionString: containerAppsEnvironment.outputs.applicationInsightsConnectionString - managedIdentityId: managedidentity.outputs.managedIdentityId - managedIdentityClientId: managedidentity.outputs.managedIdentityClientId + tags: tags + containerAppsEnvironmentId: module_containerAppsEnvironment.outputs.containerAppsEnvironmentId + applicationInsightsName: module_insights.outputs.applicationInsightsName + managedIdentityId: module_managedidentity.outputs.managedIdentityId + managedIdentityClientId: module_managedidentity.outputs.managedIdentityClientId + + KernelMemoryImageTag: KernelMemoryImageTag KernelMemory__ServiceAuthorization__AccessKey1: WebServiceAuthorizationKey1 KernelMemory__ServiceAuthorization__AccessKey2: WebServiceAuthorizationKey2 - AzureAISearch_Endpoint: 'https://${search.outputs.searchName }.search.windows.net' - AzureBlobs_Account: storage.outputs.storageAccountName - AzureQueues_Account: storage.outputs.storageAccountName - AzureQueues_QueueName: storage.outputs.queueName - AzureOpenAIEmbedding_Deployment: embedding.deploymentName - AzureOpenAIEmbedding_Endpoint: openAi.outputs.endpoint - AzureOpenAIText_Deployment: chatGpt.deploymentName - AzureOpenAIText_Endpoint: openAi.outputs.endpoint - AzureAIDocIntel_Endpoint: docIntel.outputs.endpoint + AzureAISearch_Endpoint: 'https://${module_search.outputs.searchName }.search.windows.net' + AzureBlobs_Account: module_storage.outputs.storageAccountName + AzureQueues_Account: module_storage.outputs.storageAccountName + AzureQueues_QueueName: module_storage.outputs.queueName + AzureOpenAIEmbedding_Deployment: EmbeddingDeploymentName + AzureOpenAIEmbedding_Endpoint: module_openAi.outputs.endpoint + AzureOpenAIText_Deployment: InferenceDeploymentName + AzureOpenAIText_Endpoint: module_openAi.outputs.endpoint + AzureAIDocIntel_Endpoint: module_docIntel.outputs.endpoint + } +} + +/* + Module to expose Container App via Azure Application Gateway and Public IP +*/ +module module_appGateway 'modules/network/app-gateway.bicep' = { + name: 'module-appGateway-${suffix}' + params: { + location: location + suffix: suffix + tags: tags + + defaultDomain: module_containerAppsEnvironment.outputs.containerAppsEnvironmentDomain + staticIp: module_containerAppsEnvironment.outputs.containerAppsEnvironmentStaticIp + vnetId: module_vnet.outputs.vnetId + + containerAppFqdn: module_containerApp.outputs.kmServiceFQDN + subnetId: module_vnet.outputs.appGatewaySubnetId } } @@ -228,11 +366,11 @@ module containerAppService 'modules/container-app.bicep' = { Outputs */ -@description('The FQDN of the frontend web app service.') -output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN +@description('The public IP of the Kernel Memory service.') +output kmServiceEndpoint string = module_appGateway.outputs.ipAddress @description('Service Access Key 1.') -output kmServiceAccessKey1 string = containerAppService.outputs.kmServiceAccessKey1 +output kmServiceAccessKey1 string = module_containerApp.outputs.kmServiceAccessKey1 @description('Service Access Key 2.') -output kmServiceAccessKey2 string = containerAppService.outputs.kmServiceAccessKey2 +output kmServiceAccessKey2 string = module_containerApp.outputs.kmServiceAccessKey2 diff --git a/infra/main.json b/infra/main.json index e14f79f09..6fa24a120 100644 --- a/infra/main.json +++ b/infra/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "14745838150782572756" + "version": "0.29.47.4906", + "templateHash": "8926597517400072511" } }, "parameters": { @@ -18,22 +18,67 @@ "description": "Suffix to create unique resource names; 4-6 characters. Default is a random 6 characters." } }, - "chatGptDeploymentCapacity": { + "tags": { + "type": "object", + "defaultValue": { + "Application": "Kernel-Memory", + "Environment": "Demo" + }, + "metadata": { + "description": "The tags to apply to all resources. Refer to https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/naming-and-tagging for best practices." + } + }, + "KernelMemoryImageTag": { + "type": "string", + "defaultValue": "latest", + "minLength": 3, + "maxLength": 16, + "metadata": { + "description": "Kernel Memory Docker Image Tag. Check available tags at https://hub.docker.com/r/kernelmemory/service/tags\n" + } + }, + "InferenceModel": { + "type": "string", + "defaultValue": "gpt-35-turbo-16k", + "allowedValues": [ + "gpt-35-turbo-16k", + "gpt-4", + "gpt-4-32k", + "gpt-4o", + "gpt-4o-mini" + ], + "metadata": { + "description": "ATTENTION: USE MODELS THAT YOUR AZURE SUBSCRIPTION IS ALLOWED TO USE.\n\n\nAzure OpenAI Inference Model. https://learn.microsoft.com/en-gb/azure/ai-services/openai/concepts/models\n\nDefault model version will be assigned. The default version is different for different models and might change when there is new version available for a model.\n" + } + }, + "InferenceModelDeploymentCapacity": { "type": "int", "defaultValue": 30, "minValue": 1, "maxValue": 40, "metadata": { - "description": "gpt-35-turbo-16k deployment model\\'s Tokens-Per-Minute (TPM) capacity, measured in thousands.\nThe default capacity is 30,000 TPM. \nFor model limits specific to your region, refer to the documentation at https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-quota.\n" + "description": "Inference deployment model\\'s Tokens-Per-Minute (TPM) capacity, measured in thousands.\nThe default capacity is 30,000 TPM. \nFor model limits specific to your region, refer to the documentation at https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-quota.\n" + } + }, + "EmbeddingModel": { + "type": "string", + "defaultValue": "text-embedding-ada-002", + "allowedValues": [ + "text-embedding-ada-002", + "text-embedding-3-small", + "text-embedding-3-large" + ], + "metadata": { + "description": "ATTENTION: USE MODELS THAT YOUR AZURE SUBSCRIPTION IS ALLOWED TO USE.\n\nAzure OpenAI Embedding Model. https://learn.microsoft.com/azure/ai-services/openai/concepts/models#embeddings\n\nDefault model version will be assigned. The default version is different for different models and might change when there is new version available for a model.\n" } }, - "embeddingDeploymentCapacity": { + "EmbeddingModelDeploymentCapacity": { "type": "int", "defaultValue": 30, "minValue": 1, "maxValue": 40, "metadata": { - "description": "text-embedding-ada-002 deployment model\\'s Tokens-Per-Minute (TPM) capacity, measured in thousands.\nThe default capacity is 30,000 TPM.\nFor model limits specific to your region, refer to the documentation at https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-quota.\n" + "description": "Embedding deployment model\\'s Tokens-Per-Minute (TPM) capacity, measured in thousands.\nThe default capacity is 30,000 TPM.\nFor model limits specific to your region, refer to the documentation at https://learn.microsoft.com/azure/ai-services/openai/concepts/models#standard-deployment-model-quota.\n" } }, "WebServiceAuthorizationKey1": { @@ -51,46 +96,62 @@ "metadata": { "description": "PLEASE CHOOSE A SECURE AND SECRET KEY ! -\nKernel Memory Service Authorization AccessKey 2.\nThe value is stored as an environment variable and is required by the web service to authenticate HTTP requests.\n" } + }, + "VirtualNetworkAddressSpace": { + "type": "string", + "defaultValue": "10.0.0.0/16", + "metadata": { + "description": "Define the address space of your virtual network. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices\n" + } + }, + "InfrastructureSubnetAddressRange": { + "type": "string", + "defaultValue": "10.0.0.0/23", + "metadata": { + "description": "Select an address space and configure your subnet for Infrastructure. You can also customize a subnet later. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/virtual-network-vnet-plan-design-arm#subnets\n" + } + }, + "ApplicationGatewaySubnetAddressRange": { + "type": "string", + "defaultValue": "10.0.2.0/24", + "metadata": { + "description": "Select an address space and configure your subnet for Application Gateway. You can also customize a subnet later. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/virtual-network-vnet-plan-design-arm#subnets\n" + } + }, + "PrivateEndpointSubnetAddressRange": { + "type": "string", + "defaultValue": "10.0.3.0/24", + "metadata": { + "description": "Select an address space and configure your subnet for Private Endpoints. You can also customize a subnet later. Refer to the documentation at https://learn.microsoft.com/azure/virtual-network/virtual-network-vnet-plan-design-arm#subnets\n" + } } }, "variables": { "rg": "[resourceGroup()]", "location": "[resourceGroup().location]", - "chatGpt": { - "modelName": "gpt-35-turbo-16k", - "deploymentName": "chat", - "deploymentVersion": "0613", - "deploymentCapacity": "[parameters('chatGptDeploymentCapacity')]" - }, - "embedding": { - "modelName": "text-embedding-ada-002", - "deploymentName": "embedding", - "deploymentVersion": "2", - "deploymentCapacity": "[parameters('embeddingDeploymentCapacity')]" - }, + "InferenceDeploymentName": "chat", + "EmbeddingDeploymentName": "embedding", "openAiDeployments": [ { - "name": "[variables('chatGpt').deploymentName]", + "name": "[variables('InferenceDeploymentName')]", "model": { "format": "OpenAI", - "name": "[variables('chatGpt').modelName]", - "version": "[variables('chatGpt').deploymentVersion]" + "name": "[parameters('InferenceModel')]" }, "sku": { "name": "Standard", - "capacity": "[variables('chatGpt').deploymentCapacity]" + "capacity": "[parameters('InferenceModelDeploymentCapacity')]" } }, { - "name": "[variables('embedding').deploymentName]", + "name": "[variables('EmbeddingDeploymentName')]", "model": { "format": "OpenAI", - "name": "[variables('embedding').modelName]", - "version": "[variables('embedding').deploymentVersion]" + "name": "[parameters('EmbeddingModel')]" }, "sku": { "name": "Standard", - "capacity": "[variables('embedding').deploymentCapacity]" + "capacity": "[parameters('EmbeddingModelDeploymentCapacity')]" } } ] @@ -99,7 +160,153 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('managedidentity-{0}', parameters('suffix'))]", + "name": "[format('module-vnet-{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[variables('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "vnetName": { + "value": "[format('km-vnet-{0}', parameters('suffix'))]" + }, + "VirtualNetworkAddressSpace": { + "value": "[parameters('VirtualNetworkAddressSpace')]" + }, + "InfrastructureSubnetAddressRange": { + "value": "[parameters('InfrastructureSubnetAddressRange')]" + }, + "ApplicationGatewaySubnetAddressRange": { + "value": "[parameters('ApplicationGatewaySubnetAddressRange')]" + }, + "PrivateEndpointSubnetAddressRange": { + "value": "[parameters('PrivateEndpointSubnetAddressRange')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14942332175944303797" + } + }, + "parameters": { + "vnetName": { + "type": "string", + "metadata": { + "description": "The name of the virtual network" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Location of the Vnet" + } + }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags that will be applied to the VNet" + } + }, + "VirtualNetworkAddressSpace": { + "type": "string" + }, + "InfrastructureSubnetAddressRange": { + "type": "string" + }, + "ApplicationGatewaySubnetAddressRange": { + "type": "string" + }, + "PrivateEndpointSubnetAddressRange": { + "type": "string" + } + }, + "variables": { + "InfrastructureSubnetName": "infrastructure-subnet", + "ApplicationGatewaySubnetName": "app-gateway-subnet", + "PrivateEndpointSubnetName": "private-endpoint-subnet" + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2023-11-01", + "name": "[parameters('vnetName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('VirtualNetworkAddressSpace')]" + ] + }, + "subnets": [ + { + "name": "[variables('InfrastructureSubnetName')]", + "properties": { + "addressPrefix": "[parameters('InfrastructureSubnetAddressRange')]", + "privateEndpointNetworkPolicies": "Enabled", + "privateLinkServiceNetworkPolicies": "Disabled" + } + }, + { + "name": "[variables('ApplicationGatewaySubnetName')]", + "properties": { + "addressPrefix": "[parameters('ApplicationGatewaySubnetAddressRange')]", + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Disabled" + } + }, + { + "name": "[variables('PrivateEndpointSubnetName')]", + "properties": { + "addressPrefix": "[parameters('PrivateEndpointSubnetAddressRange')]", + "privateEndpointNetworkPolicies": "Enabled", + "privateLinkServiceNetworkPolicies": "Disabled" + } + } + ] + } + } + ], + "outputs": { + "vnetName": { + "type": "string", + "value": "[parameters('vnetName')]" + }, + "vnetId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + }, + "envInfraSubnetId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), variables('InfrastructureSubnetName'))]" + }, + "appGatewaySubnetId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), variables('ApplicationGatewaySubnetName'))]" + }, + "privateEndpointSubnetId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), variables('PrivateEndpointSubnetName'))]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-managedidentity-{0}', parameters('suffix'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -119,8 +326,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10309523882387137109" + "version": "0.29.47.4906", + "templateHash": "18084793981662397801" } }, "parameters": { @@ -186,7 +393,7 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('storage-{0}', parameters('suffix'))]", + "name": "[format('module-storage-{0}', parameters('suffix'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -196,11 +403,20 @@ "location": { "value": "[variables('location')]" }, + "tags": { + "value": "[parameters('tags')]" + }, "suffix": { "value": "[parameters('suffix')]" }, + "vnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.vnetId.value]" + }, + "privateEndpointSubnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.privateEndpointSubnetId.value]" + }, "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" } }, "template": { @@ -209,8 +425,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6577135357063255350" + "version": "0.29.47.4906", + "templateHash": "5679651508289915330" } }, "parameters": { @@ -224,9 +440,8 @@ }, "tags": { "type": "object", - "defaultValue": {}, "metadata": { - "description": "Optional. The tags to be assigned to the created resources." + "description": "The tags to be assigned to the created resources." } }, "storageAccountName": { @@ -250,6 +465,12 @@ "description": "The name of the Queue in Azure Storage." } }, + "vnetId": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, "managedIdentityPrincipalId": { "type": "string" } @@ -267,9 +488,46 @@ "kind": "StorageV2", "properties": { "allowBlobPublicAccess": false, - "allowSharedKeyAccess": false + "allowSharedKeyAccess": false, + "publicNetworkAccess": "Disabled" } }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('storageBlobContainerName'))]", + "properties": { + "publicAccess": "None" + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/queueServices/queues", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('externalTasksQueueName'))]", + "properties": {}, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), 'default')]" + ] + }, { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", @@ -330,7 +588,7 @@ "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(format('// Storage Queue Data Message Processor-{0}', parameters('suffix')))]", + "name": "[guid(format('Storage Queue Data Message Processor-{0}', parameters('suffix')))]", "properties": { "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", "principalId": "[parameters('managedIdentityPrincipalId')]", @@ -341,36 +599,429 @@ ] }, { - "type": "Microsoft.Storage/storageAccounts/blobServices", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module_blob_pe_{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "suffix": { + "value": "[parameters('suffix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "serviceName_Used_for_PE": { + "value": "[format('{0}-blob', parameters('storageAccountName'))]" + }, + "DNSZoneName": { + "value": "[format('privatelink.blob.{0}', environment().suffixes.storage)]" + }, + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateEndpointSubnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceId": { + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + }, + "privateLinkServiceConnections_GroupIds": { + "value": [ + "blob" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206819443057151020" + } + }, + "parameters": { + "suffix": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, + "serviceName_Used_for_PE": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, + "privateLinkServiceId": { + "type": "string" + }, + "privateLinkServiceConnections_GroupIds": { + "type": "array" + }, + "DNSZoneName": { + "type": "string" + }, + "vnetId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-01-01", + "name": "[format('{0}-pe', parameters('serviceName_Used_for_PE'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "subnet": { + "id": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "private-endpoint-connection", + "properties": { + "privateLinkServiceId": "[parameters('privateLinkServiceId')]", + "groupIds": "[parameters('privateLinkServiceConnections_GroupIds')]" + } + } + ] + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', format('{0}-pe', parameters('serviceName_Used_for_PE')), format('privateDnsZoneGroup-{0}', parameters('suffix')))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privateDnsZoneGroup-Config-{0}', parameters('suffix'))]", + "properties": { + "privateDnsZoneId": "[reference(resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))), '2022-09-01').outputs.dnsZoneId.value]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE')))]", + "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-pe', parameters('serviceName_Used_for_PE')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateDnsZoneName": { + "value": "[parameters('DNSZoneName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15260629403946340734" + } + }, + "parameters": { + "vnetId": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "privateDnsZoneName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]", + "location": "global", + "tags": "[parameters('tags')]", + "properties": {} + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), format('{0}-link', parameters('privateDnsZoneName')))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vnetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + ] + } + ], + "outputs": { + "dnsZoneName": { + "type": "string", + "value": "[parameters('privateDnsZoneName')]" + }, + "dnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + } + } + } + } + } + ] + } + }, "dependsOn": [ "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" ] }, { - "type": "Microsoft.Storage/storageAccounts/blobServices/containers", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('storageBlobContainerName'))]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), 'default')]" - ] - }, - { - "type": "Microsoft.Storage/storageAccounts/queueServices", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module_queue_pe_{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "suffix": { + "value": "[parameters('suffix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "serviceName_Used_for_PE": { + "value": "[format('{0}-queue', parameters('storageAccountName'))]" + }, + "DNSZoneName": { + "value": "[format('privatelink.queue.{0}', environment().suffixes.storage)]" + }, + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateEndpointSubnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceId": { + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" + }, + "privateLinkServiceConnections_GroupIds": { + "value": [ + "queue" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206819443057151020" + } + }, + "parameters": { + "suffix": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, + "serviceName_Used_for_PE": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, + "privateLinkServiceId": { + "type": "string" + }, + "privateLinkServiceConnections_GroupIds": { + "type": "array" + }, + "DNSZoneName": { + "type": "string" + }, + "vnetId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-01-01", + "name": "[format('{0}-pe', parameters('serviceName_Used_for_PE'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "subnet": { + "id": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "private-endpoint-connection", + "properties": { + "privateLinkServiceId": "[parameters('privateLinkServiceId')]", + "groupIds": "[parameters('privateLinkServiceConnections_GroupIds')]" + } + } + ] + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', format('{0}-pe', parameters('serviceName_Used_for_PE')), format('privateDnsZoneGroup-{0}', parameters('suffix')))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privateDnsZoneGroup-Config-{0}', parameters('suffix'))]", + "properties": { + "privateDnsZoneId": "[reference(resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))), '2022-09-01').outputs.dnsZoneId.value]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE')))]", + "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-pe', parameters('serviceName_Used_for_PE')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateDnsZoneName": { + "value": "[parameters('DNSZoneName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15260629403946340734" + } + }, + "parameters": { + "vnetId": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "privateDnsZoneName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]", + "location": "global", + "tags": "[parameters('tags')]", + "properties": {} + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), format('{0}-link', parameters('privateDnsZoneName')))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vnetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + ] + } + ], + "outputs": { + "dnsZoneName": { + "type": "string", + "value": "[parameters('privateDnsZoneName')]" + }, + "dnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + } + } + } + } + } + ] + } + }, "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module_blob_pe_{0}', parameters('suffix')))]", "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" ] - }, - { - "type": "Microsoft.Storage/storageAccounts/queueServices/queues", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('externalTasksQueueName'))]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), 'default')]" - ] } ], "outputs": { @@ -399,13 +1050,14 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" + "[resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix')))]" ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('search-{0}', parameters('suffix'))]", + "name": "[format('module-search-{0}', parameters('suffix'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -415,14 +1067,20 @@ "location": { "value": "[variables('location')]" }, - "name": { - "value": "[format('km-search-{0}', parameters('suffix'))]" + "tags": { + "value": "[parameters('tags')]" }, "suffix": { "value": "[parameters('suffix')]" }, + "vnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.vnetId.value]" + }, + "privateEndpointSubnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.privateEndpointSubnetId.value]" + }, "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" } }, "template": { @@ -431,8 +1089,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "16481486745226370443" + "version": "0.29.47.4906", + "templateHash": "5839044347575571066" } }, "parameters": { @@ -443,8 +1101,15 @@ "type": "string", "defaultValue": "[uniqueString(resourceGroup().id)]" }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, "name": { "type": "string", + "defaultValue": "[format('km-search-{0}', parameters('suffix'))]", "minLength": 2, "maxLength": 60, "metadata": { @@ -508,6 +1173,12 @@ "metadata": { "description": "Location for all resources." } + }, + "vnetId": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" } }, "resources": [ @@ -516,6 +1187,7 @@ "apiVersion": "2023-11-01", "name": "[parameters('name')]", "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "sku": { "name": "[parameters('sku')]" }, @@ -525,7 +1197,8 @@ "hostingMode": "[parameters('hostingMode')]", "authOptions": { "aadOrApiKey": {} - } + }, + "publicNetworkAccess": "disabled" } }, { @@ -555,6 +1228,218 @@ "dependsOn": [ "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module_search_pe_{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "suffix": { + "value": "[parameters('suffix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "serviceName_Used_for_PE": { + "value": "[parameters('name')]" + }, + "DNSZoneName": { + "value": "privatelink.search.windows.net" + }, + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateEndpointSubnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceId": { + "value": "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" + }, + "privateLinkServiceConnections_GroupIds": { + "value": [ + "searchService" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206819443057151020" + } + }, + "parameters": { + "suffix": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, + "serviceName_Used_for_PE": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, + "privateLinkServiceId": { + "type": "string" + }, + "privateLinkServiceConnections_GroupIds": { + "type": "array" + }, + "DNSZoneName": { + "type": "string" + }, + "vnetId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-01-01", + "name": "[format('{0}-pe', parameters('serviceName_Used_for_PE'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "subnet": { + "id": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "private-endpoint-connection", + "properties": { + "privateLinkServiceId": "[parameters('privateLinkServiceId')]", + "groupIds": "[parameters('privateLinkServiceConnections_GroupIds')]" + } + } + ] + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', format('{0}-pe', parameters('serviceName_Used_for_PE')), format('privateDnsZoneGroup-{0}', parameters('suffix')))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privateDnsZoneGroup-Config-{0}', parameters('suffix'))]", + "properties": { + "privateDnsZoneId": "[reference(resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))), '2022-09-01').outputs.dnsZoneId.value]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE')))]", + "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-pe', parameters('serviceName_Used_for_PE')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateDnsZoneName": { + "value": "[parameters('DNSZoneName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15260629403946340734" + } + }, + "parameters": { + "vnetId": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "privateDnsZoneName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]", + "location": "global", + "tags": "[parameters('tags')]", + "properties": {} + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), format('{0}-link', parameters('privateDnsZoneName')))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vnetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + ] + } + ], + "outputs": { + "dnsZoneName": { + "type": "string", + "value": "[parameters('privateDnsZoneName')]" + }, + "dnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" + ] } ], "outputs": { @@ -566,13 +1451,14 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" + "[resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix')))]" ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('openai-{0}', parameters('suffix'))]", + "name": "[format('module-openai-{0}', parameters('suffix'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -582,8 +1468,17 @@ "suffix": { "value": "[parameters('suffix')]" }, - "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" + "tags": { + "value": "[parameters('tags')]" + }, + "vnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.vnetId.value]" + }, + "privateEndpointSubnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.privateEndpointSubnetId.value]" + }, + "managedIdentityPrincipalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" }, "name": { "value": "[format('km-openai-{0}', parameters('suffix'))]" @@ -596,6 +1491,9 @@ "name": "S0" } }, + "publicNetworkAccess": { + "value": "Disabled" + }, "deployments": { "value": "[variables('openAiDeployments')]" } @@ -606,8 +1504,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8195898199308451447" + "version": "0.29.47.4906", + "templateHash": "74450014706483266" }, "description": "Creates an Azure Cognitive Services instance." }, @@ -616,9 +1514,6 @@ "type": "string", "defaultValue": "[uniqueString(resourceGroup().id)]" }, - "managedIdentityPrincipalId": { - "type": "string" - }, "name": { "type": "string" }, @@ -628,7 +1523,9 @@ }, "tags": { "type": "object", - "defaultValue": {} + "metadata": { + "description": "The tags to be assigned to the created resources." + } }, "customSubDomainName": { "type": "string", @@ -663,6 +1560,15 @@ "type": "array", "defaultValue": [] }, + "vnetId": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, + "managedIdentityPrincipalId": { + "type": "string" + }, "networkAcls": { "type": "object", "defaultValue": "[if(empty(parameters('allowedIpRules')), createObject('defaultAction', 'Allow'), createObject('ipRules', parameters('allowedIpRules'), 'defaultAction', 'Deny'))]" @@ -696,9 +1602,9 @@ "name": "[format('{0}/{1}', parameters('name'), parameters('deployments')[copyIndex()].name)]", "properties": { "model": "[parameters('deployments')[copyIndex()].model]", - "raiPolicyName": "[if(contains(parameters('deployments')[copyIndex()], 'raiPolicyName'), parameters('deployments')[copyIndex()].raiPolicyName, null())]" + "raiPolicyName": "[coalesce(tryGet(parameters('deployments')[copyIndex()], 'raiPolicyName'), null())]" }, - "sku": "[if(contains(parameters('deployments')[copyIndex()], 'sku'), parameters('deployments')[copyIndex()].sku, createObject('name', 'Standard', 'capacity', 1))]", + "sku": "[coalesce(tryGet(parameters('deployments')[copyIndex()], 'sku'), createObject('name', 'Standard', 'capacity', 1))]", "dependsOn": [ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" ] @@ -730,6 +1636,218 @@ "dependsOn": [ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module_openai_pe_{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "suffix": { + "value": "[parameters('suffix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "serviceName_Used_for_PE": { + "value": "[parameters('name')]" + }, + "DNSZoneName": { + "value": "privatelink.openai.azure.com" + }, + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateEndpointSubnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceId": { + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "privateLinkServiceConnections_GroupIds": { + "value": [ + "account" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206819443057151020" + } + }, + "parameters": { + "suffix": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, + "serviceName_Used_for_PE": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, + "privateLinkServiceId": { + "type": "string" + }, + "privateLinkServiceConnections_GroupIds": { + "type": "array" + }, + "DNSZoneName": { + "type": "string" + }, + "vnetId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-01-01", + "name": "[format('{0}-pe', parameters('serviceName_Used_for_PE'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "subnet": { + "id": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "private-endpoint-connection", + "properties": { + "privateLinkServiceId": "[parameters('privateLinkServiceId')]", + "groupIds": "[parameters('privateLinkServiceConnections_GroupIds')]" + } + } + ] + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', format('{0}-pe', parameters('serviceName_Used_for_PE')), format('privateDnsZoneGroup-{0}', parameters('suffix')))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privateDnsZoneGroup-Config-{0}', parameters('suffix'))]", + "properties": { + "privateDnsZoneId": "[reference(resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))), '2022-09-01').outputs.dnsZoneId.value]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE')))]", + "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-pe', parameters('serviceName_Used_for_PE')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateDnsZoneName": { + "value": "[parameters('DNSZoneName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15260629403946340734" + } + }, + "parameters": { + "vnetId": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "privateDnsZoneName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]", + "location": "global", + "tags": "[parameters('tags')]", + "properties": {} + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), format('{0}-link', parameters('privateDnsZoneName')))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vnetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + ] + } + ], + "outputs": { + "dnsZoneName": { + "type": "string", + "value": "[parameters('privateDnsZoneName')]" + }, + "dnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + ] } ], "outputs": { @@ -749,13 +1867,14 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" + "[resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix')))]" ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('docIntel-{0}', parameters('suffix'))]", + "name": "[format('module-docIntel-{0}', parameters('suffix'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -765,8 +1884,20 @@ "suffix": { "value": "[parameters('suffix')]" }, + "tags": { + "value": "[parameters('tags')]" + }, + "vnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.vnetId.value]" + }, + "privateEndpointSubnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.privateEndpointSubnetId.value]" + }, "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" + }, + "publicNetworkAccess": { + "value": "Disabled" }, "name": { "value": "[format('km-docIntel-{0}', parameters('suffix'))]" @@ -786,8 +1917,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3584343383825170510" + "version": "0.29.47.4906", + "templateHash": "11877878921179671125" }, "description": "Creates an Azure Document Intelligence (form recognizer) instance." }, @@ -796,6 +1927,12 @@ "type": "string", "defaultValue": "[uniqueString(resourceGroup().id)]" }, + "vnetId": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, "managedIdentityPrincipalId": { "type": "string" }, @@ -806,6 +1943,12 @@ "type": "string", "defaultValue": "[resourceGroup().location]" }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, "customSubDomainName": { "type": "string", "defaultValue": "[parameters('name')]", @@ -847,6 +1990,7 @@ "name": "[parameters('name')]", "location": "[parameters('location')]", "kind": "[parameters('kind')]", + "tags": "[parameters('tags')]", "properties": { "customSubDomainName": "[parameters('customSubDomainName')]", "publicNetworkAccess": "[parameters('publicNetworkAccess')]", @@ -868,6 +2012,218 @@ "dependsOn": [ "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module_DocIntel_pe{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "suffix": { + "value": "[parameters('suffix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "serviceName_Used_for_PE": { + "value": "[parameters('name')]" + }, + "DNSZoneName": { + "value": "privatelink.cognitiveservices.azure.com" + }, + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateEndpointSubnetId": { + "value": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceId": { + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "privateLinkServiceConnections_GroupIds": { + "value": [ + "account" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206819443057151020" + } + }, + "parameters": { + "suffix": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, + "serviceName_Used_for_PE": { + "type": "string" + }, + "privateEndpointSubnetId": { + "type": "string" + }, + "privateLinkServiceId": { + "type": "string" + }, + "privateLinkServiceConnections_GroupIds": { + "type": "array" + }, + "DNSZoneName": { + "type": "string" + }, + "vnetId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-01-01", + "name": "[format('{0}-pe', parameters('serviceName_Used_for_PE'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "subnet": { + "id": "[parameters('privateEndpointSubnetId')]" + }, + "privateLinkServiceConnections": [ + { + "name": "private-endpoint-connection", + "properties": { + "privateLinkServiceId": "[parameters('privateLinkServiceId')]", + "groupIds": "[parameters('privateLinkServiceConnections_GroupIds')]" + } + } + ] + } + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', format('{0}-pe', parameters('serviceName_Used_for_PE')), format('privateDnsZoneGroup-{0}', parameters('suffix')))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[format('privateDnsZoneGroup-Config-{0}', parameters('suffix'))]", + "properties": { + "privateDnsZoneId": "[reference(resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))), '2022-09-01').outputs.dnsZoneId.value]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE')))]", + "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-pe', parameters('serviceName_Used_for_PE')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-dns-{0}-pe', parameters('serviceName_Used_for_PE'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetId": { + "value": "[parameters('vnetId')]" + }, + "privateDnsZoneName": { + "value": "[parameters('DNSZoneName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15260629403946340734" + } + }, + "parameters": { + "vnetId": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + }, + "privateDnsZoneName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]", + "location": "global", + "tags": "[parameters('tags')]", + "properties": {} + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), format('{0}-link', parameters('privateDnsZoneName')))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vnetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + ] + } + ], + "outputs": { + "dnsZoneName": { + "type": "string", + "value": "[parameters('privateDnsZoneName')]" + }, + "dnsZoneId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('privateDnsZoneName'))]" + } + } + } + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + ] } ], "outputs": { @@ -879,24 +2235,28 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" + "[resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix')))]" ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('containerAppsEnvironment-{0}', parameters('suffix'))]", + "name": "[format('module-insights-{0}', parameters('suffix'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { + "suffix": { + "value": "[parameters('suffix')]" + }, "location": { "value": "[variables('location')]" }, - "suffix": { - "value": "[parameters('suffix')]" + "tags": { + "value": "[parameters('tags')]" } }, "template": { @@ -905,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15906627962917124587" + "version": "0.29.47.4906", + "templateHash": "14091880884769556995" } }, "parameters": { @@ -923,16 +2283,8 @@ }, "tags": { "type": "object", - "defaultValue": {}, "metadata": { - "description": "Optional. The tags to be assigned to the created resources." - } - }, - "containerAppsEnvironmentName": { - "type": "string", - "defaultValue": "[format('km-cae-{0}', parameters('suffix'))]", - "metadata": { - "description": "The name of the container apps environment. If set, it overrides the name generated by the template." + "description": "The tags to be assigned to the created resources." } }, "logAnalyticsWorkspaceName": { @@ -981,16 +2333,114 @@ "dependsOn": [ "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" ] + } + ], + "outputs": { + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[parameters('logAnalyticsWorkspaceName')]" + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "The name of the application insights." + }, + "value": "[parameters('applicationInsightsName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-containerAppsEnvironment-{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[variables('location')]" + }, + "suffix": { + "value": "[parameters('suffix')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "acaSubnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.envInfraSubnetId.value]" + }, + "logAnalyticsWorkspaceName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-insights-{0}', parameters('suffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceName.value]" + }, + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-insights-{0}', parameters('suffix'))), '2022-09-01').outputs.applicationInsightsName.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9308445102453052293" + } + }, + "parameters": { + "suffix": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location where the resources will be created." + } }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, + "containerAppsEnvironmentName": { + "type": "string", + "defaultValue": "[format('km-cae-{0}', parameters('suffix'))]", + "metadata": { + "description": "The name of the container apps environment. If set, it overrides the name generated by the template." + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "The name of the log analytics workspace resource create in another module." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "The name of the application insights resource create in another module." + } + }, + "acaSubnetId": { + "type": "string", + "metadata": { + "description": "The subnet id of the subnet where the container apps environment will be deployed." + } + } + }, + "resources": [ { "type": "Microsoft.App/managedEnvironments", "apiVersion": "2022-10-01", "name": "[parameters('containerAppsEnvironmentName')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", - "sku": { - "name": "Consumption" - }, "properties": { "daprAIInstrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]", "appLogsConfiguration": { @@ -999,12 +2449,13 @@ "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2021-06-01').customerId]", "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2021-06-01').primarySharedKey]" } + }, + "zoneRedundant": true, + "vnetConfiguration": { + "infrastructureSubnetId": "[parameters('acaSubnetId')]", + "internal": true } - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]", - "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" - ] + } } ], "outputs": { @@ -1016,33 +2467,26 @@ "type": "string", "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]" }, - "logAnalyticsWorkspaceName": { - "type": "string", - "value": "[parameters('logAnalyticsWorkspaceName')]" - }, - "applicationInsightsName": { + "containerAppsEnvironmentDomain": { "type": "string", - "metadata": { - "description": "The name of the application insights." - }, - "value": "[parameters('applicationInsightsName')]" + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2022-10-01').defaultDomain]" }, - "applicationInsightsInstrumentationKey": { + "containerAppsEnvironmentStaticIp": { "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" - }, - "applicationInsightsConnectionString": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" + "value": "[reference(resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName')), '2022-10-01').staticIp]" } } } - } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module-insights-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix')))]" + ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('containerAppService-{0}', parameters('suffix'))]", + "name": "[format('module-containerAppService-{0}', parameters('suffix'))]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -1055,20 +2499,23 @@ "suffix": { "value": "[parameters('suffix')]" }, - "containerAppsEnvironmentId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.containerAppsEnvironmentId.value]" + "tags": { + "value": "[parameters('tags')]" }, - "appInsightsInstrumentationKey": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.applicationInsightsInstrumentationKey.value]" + "containerAppsEnvironmentId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.containerAppsEnvironmentId.value]" }, - "applicationInsightsConnectionString": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.applicationInsightsConnectionString.value]" + "applicationInsightsName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-insights-{0}', parameters('suffix'))), '2022-09-01').outputs.applicationInsightsName.value]" }, "managedIdentityId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityId.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityId.value]" }, "managedIdentityClientId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityClientId.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityClientId.value]" + }, + "KernelMemoryImageTag": { + "value": "[parameters('KernelMemoryImageTag')]" }, "KernelMemory__ServiceAuthorization__AccessKey1": { "value": "[parameters('WebServiceAuthorizationKey1')]" @@ -1077,31 +2524,31 @@ "value": "[parameters('WebServiceAuthorizationKey2')]" }, "AzureAISearch_Endpoint": { - "value": "[format('https://{0}.search.windows.net', reference(resourceId('Microsoft.Resources/deployments', format('search-{0}', parameters('suffix'))), '2022-09-01').outputs.searchName.value)]" + "value": "[format('https://{0}.search.windows.net', reference(resourceId('Microsoft.Resources/deployments', format('module-search-{0}', parameters('suffix'))), '2022-09-01').outputs.searchName.value)]" }, "AzureBlobs_Account": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('storage-{0}', parameters('suffix'))), '2022-09-01').outputs.storageAccountName.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-storage-{0}', parameters('suffix'))), '2022-09-01').outputs.storageAccountName.value]" }, "AzureQueues_Account": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('storage-{0}', parameters('suffix'))), '2022-09-01').outputs.storageAccountName.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-storage-{0}', parameters('suffix'))), '2022-09-01').outputs.storageAccountName.value]" }, "AzureQueues_QueueName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('storage-{0}', parameters('suffix'))), '2022-09-01').outputs.queueName.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-storage-{0}', parameters('suffix'))), '2022-09-01').outputs.queueName.value]" }, "AzureOpenAIEmbedding_Deployment": { - "value": "[variables('embedding').deploymentName]" + "value": "[variables('EmbeddingDeploymentName')]" }, "AzureOpenAIEmbedding_Endpoint": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('openai-{0}', parameters('suffix'))), '2022-09-01').outputs.endpoint.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-openai-{0}', parameters('suffix'))), '2022-09-01').outputs.endpoint.value]" }, "AzureOpenAIText_Deployment": { - "value": "[variables('chatGpt').deploymentName]" + "value": "[variables('InferenceDeploymentName')]" }, "AzureOpenAIText_Endpoint": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('openai-{0}', parameters('suffix'))), '2022-09-01').outputs.endpoint.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-openai-{0}', parameters('suffix'))), '2022-09-01').outputs.endpoint.value]" }, "AzureAIDocIntel_Endpoint": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('docIntel-{0}', parameters('suffix'))), '2022-09-01').outputs.endpoint.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-docIntel-{0}', parameters('suffix'))), '2022-09-01').outputs.endpoint.value]" } }, "template": { @@ -1110,8 +2557,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1087801445927599681" + "version": "0.29.47.4906", + "templateHash": "11520573448908281320" } }, "parameters": { @@ -1123,12 +2570,22 @@ "type": "string", "defaultValue": "[resourceGroup().location]" }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags to be assigned to the created resources." + } + }, "managedIdentityId": { "type": "string" }, "managedIdentityClientId": { "type": "string" }, + "KernelMemoryImageTag": { + "type": "string", + "defaultValue": "latest" + }, "kmServiceName": { "type": "string", "defaultValue": "[format('km-service-{0}', parameters('suffix'))]" @@ -1136,11 +2593,11 @@ "containerAppsEnvironmentId": { "type": "string" }, - "appInsightsInstrumentationKey": { - "type": "string" - }, - "applicationInsightsConnectionString": { - "type": "string" + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "The name of the application insights resource create in another module." + } }, "AzureBlobs_Account": { "type": "string" @@ -1182,13 +2639,14 @@ "apiVersion": "2023-05-01", "name": "[parameters('kmServiceName')]", "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { "environmentId": "[parameters('containerAppsEnvironmentId')]", "configuration": { "secrets": [ { "name": "appinsights-key", - "value": "[parameters('appInsightsInstrumentationKey')]" + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]" } ], "registries": [], @@ -1207,7 +2665,7 @@ "containers": [ { "name": "kernelmemory-service", - "image": "docker.io/kernelmemory/service:latest", + "image": "[format('docker.io/kernelmemory/service:{0}', parameters('KernelMemoryImageTag'))]", "command": [], "resources": { "cpu": "[json('0.25')]", @@ -1220,7 +2678,7 @@ }, { "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[parameters('applicationInsightsConnectionString')]" + "value": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').ConnectionString]" }, { "name": "AZURE_CLIENT_ID", @@ -1367,12 +2825,340 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('containerAppsEnvironment-{0}', parameters('suffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('docIntel-{0}', parameters('suffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('openai-{0}', parameters('suffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('search-{0}', parameters('suffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('storage-{0}', parameters('suffix')))]" + "[resourceId('Microsoft.Resources/deployments', format('module-containerAppsEnvironment-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-docIntel-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-insights-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-managedidentity-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-openai-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-search-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-storage-{0}', parameters('suffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('module-appGateway-{0}', parameters('suffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[variables('location')]" + }, + "suffix": { + "value": "[parameters('suffix')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "defaultDomain": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.containerAppsEnvironmentDomain.value]" + }, + "staticIp": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.containerAppsEnvironmentStaticIp.value]" + }, + "vnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.vnetId.value]" + }, + "containerAppFqdn": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceFQDN.value]" + }, + "subnetId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix'))), '2022-09-01').outputs.appGatewaySubnetId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14131115677795191231" + } + }, + "parameters": { + "suffix": { + "type": "string", + "defaultValue": "[uniqueString(resourceGroup().id)]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location where the App Gateway will be deployed" + } + }, + "tags": { + "type": "object", + "metadata": { + "description": "The tags that will be applied to the App Gateway" + } + }, + "staticIp": { + "type": "string" + }, + "defaultDomain": { + "type": "string" + }, + "vnetId": { + "type": "string" + }, + "subnetId": { + "type": "string", + "metadata": { + "description": "The subnet ID that will be used for the App Gateway configuration" + } + }, + "containerAppFqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the Container App" + } + } + }, + "variables": { + "appGatewayName": "[format('km-appG-{0}', parameters('suffix'))]", + "ipAddressName": "[format('km-appG-pip-{0}', parameters('suffix'))]" + }, + "resources": [ + { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('defaultDomain')]", + "location": "global", + "tags": "[parameters('tags')]" + }, + { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('defaultDomain'), '*')]", + "properties": { + "ttl": 3600, + "aRecords": [ + { + "ipv4Address": "[parameters('staticIp')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('defaultDomain'))]" + ] + }, + { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('defaultDomain'), '@')]", + "properties": { + "ttl": 3600, + "aRecords": [ + { + "ipv4Address": "[parameters('staticIp')]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('defaultDomain'))]" + ] + }, + { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('defaultDomain'), 'pdns-link')]", + "tags": "[parameters('tags')]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[parameters('vnetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateDnsZones', parameters('defaultDomain'))]" + ] + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-11-01", + "name": "[variables('ipAddressName')]", + "location": "[parameters('location')]", + "sku": { + "name": "Standard" + }, + "zones": [ + "1" + ], + "properties": { + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Static" + } + }, + { + "type": "Microsoft.Network/applicationGateways", + "apiVersion": "2023-11-01", + "name": "[variables('appGatewayName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "zones": [ + "1" + ], + "properties": { + "sku": { + "tier": "Standard_v2", + "capacity": 1, + "name": "Standard_v2" + }, + "gatewayIPConfigurations": [ + { + "name": "appgateway-subnet", + "properties": { + "subnet": { + "id": "[parameters('subnetId')]" + } + } + } + ], + "frontendIPConfigurations": [ + { + "name": "my-frontend", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('ipAddressName'))]" + } + } + } + ], + "frontendPorts": [ + { + "name": "port_80", + "properties": { + "port": 80 + } + } + ], + "backendAddressPools": [ + { + "name": "my-agw-backend-pool", + "properties": { + "backendAddresses": [ + { + "fqdn": "[parameters('containerAppFqdn')]" + } + ] + } + } + ], + "probes": [ + { + "name": "health-http", + "properties": { + "protocol": "Http", + "path": "/health", + "interval": 30, + "timeout": 30, + "unhealthyThreshold": 3, + "pickHostNameFromBackendHttpSettings": true, + "minServers": 0, + "match": {} + } + }, + { + "name": "health-https", + "properties": { + "protocol": "Https", + "path": "/health", + "interval": 30, + "timeout": 30, + "unhealthyThreshold": 3, + "pickHostNameFromBackendHttpSettings": true, + "minServers": 0, + "match": {} + } + } + ], + "backendHttpSettingsCollection": [ + { + "name": "backend-setting-https", + "properties": { + "protocol": "Https", + "port": 443, + "cookieBasedAffinity": "Disabled", + "requestTimeout": 20, + "pickHostNameFromBackendAddress": true, + "probe": { + "id": "[resourceId('Microsoft.Network/applicationGateways/probes', variables('appGatewayName'), 'health-https')]" + } + } + }, + { + "name": "backend-setting-http", + "properties": { + "protocol": "Http", + "port": 80, + "cookieBasedAffinity": "Disabled", + "requestTimeout": 20, + "pickHostNameFromBackendAddress": true, + "probe": { + "id": "[resourceId('Microsoft.Network/applicationGateways/probes', variables('appGatewayName'), 'health-http')]" + } + } + } + ], + "httpListeners": [ + { + "name": "my-agw-listener", + "properties": { + "frontendIPConfiguration": { + "id": "[resourceId('Microsoft.Network/applicationGateways/frontendIPConfigurations', variables('appGatewayName'), 'my-frontend')]" + }, + "frontendPort": { + "id": "[resourceId('Microsoft.Network/applicationGateways/frontendPorts', variables('appGatewayName'), 'port_80')]" + }, + "protocol": "Http" + } + } + ], + "requestRoutingRules": [ + { + "name": "my-agw-routing-rule", + "properties": { + "priority": 1, + "ruleType": "Basic", + "httpListener": { + "id": "[resourceId('Microsoft.Network/applicationGateways/httpListeners', variables('appGatewayName'), 'my-agw-listener')]" + }, + "backendAddressPool": { + "id": "[resourceId('Microsoft.Network/applicationGateways/backendAddressPools', variables('appGatewayName'), 'my-agw-backend-pool')]" + }, + "backendHttpSettings": { + "id": "[resourceId('Microsoft.Network/applicationGateways/backendHttpSettingsCollection', variables('appGatewayName'), 'backend-setting-https')]" + } + } + } + ], + "enableHttp2": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses', variables('ipAddressName'))]" + ] + } + ], + "outputs": { + "ipAddress": { + "type": "string", + "metadata": { + "description": "Public IP" + }, + "value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', variables('ipAddressName')), '2023-11-01').ipAddress]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('module-containerAppService-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-containerAppsEnvironment-{0}', parameters('suffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('module-vnet-{0}', parameters('suffix')))]" ] } ], @@ -1380,23 +3166,23 @@ "kmServiceEndpoint": { "type": "string", "metadata": { - "description": "The FQDN of the frontend web app service." + "description": "The public IP of the Kernel Memory service." }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceFQDN.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-appGateway-{0}', parameters('suffix'))), '2022-09-01').outputs.ipAddress.value]" }, "kmServiceAccessKey1": { "type": "string", "metadata": { "description": "Service Access Key 1." }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceAccessKey1.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceAccessKey1.value]" }, "kmServiceAccessKey2": { "type": "string", "metadata": { "description": "Service Access Key 2." }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceAccessKey2.value]" + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('module-containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceAccessKey2.value]" } } } \ No newline at end of file diff --git a/infra/modules/ai-search.bicep b/infra/modules/cognitive/ai-search.bicep similarity index 70% rename from infra/modules/ai-search.bicep rename to infra/modules/cognitive/ai-search.bicep index 55128ed39..6d96f694a 100644 --- a/infra/modules/ai-search.bicep +++ b/infra/modules/cognitive/ai-search.bicep @@ -2,10 +2,13 @@ param managedIdentityPrincipalId string param suffix string = uniqueString(resourceGroup().id) +@description('The tags to be assigned to the created resources.') +param tags object + @description('Service name must only contain lowercase letters, digits or dashes, cannot use dash as the first two or last one characters, cannot contain consecutive dashes, and is limited between 2 and 60 characters in length.') @minLength(2) @maxLength(60) -param name string +param name string = 'km-search-${suffix}' @allowed([ 'free' @@ -45,9 +48,13 @@ param hostingMode string = 'default' @description('Location for all resources.') param location string = resourceGroup().location +param vnetId string +param privateEndpointSubnetId string + resource search 'Microsoft.Search/searchServices@2023-11-01' = { name: name location: location + tags: tags sku: { name: sku } @@ -55,12 +62,38 @@ resource search 'Microsoft.Search/searchServices@2023-11-01' = { replicaCount: replicaCount partitionCount: partitionCount hostingMode: hostingMode + + // bohdan Check `disableLocalAuth: true` authOptions: { aadOrApiKey: {} } + + publicNetworkAccess: 'disabled' + } +} + +////////////////////////// Private endpoint + +module module_search_pe '../network/private-endpoint.bicep' = { + name: 'module_search_pe_${suffix}' + params: { + suffix: suffix + location: location + tags: tags + + serviceName_Used_for_PE: name + + DNSZoneName: 'privatelink.search.windows.net' // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns + vnetId: vnetId + privateEndpointSubnetId: privateEndpointSubnetId + + privateLinkServiceId: search.id + privateLinkServiceConnections_GroupIds: ['searchService'] // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource } } +////////////////////////// RBAC + // Search Index Data Contributor resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid('Search Index Data Contributor-${suffix}') @@ -89,4 +122,9 @@ resource roleAssignment2 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } +////////////////////////// Output + output searchName string = search.name + +// output searchObj object = search +// output searchPrivateEndpointObj object = privateEndpoint diff --git a/infra/modules/cognitive-services-docIntel.bicep b/infra/modules/cognitive/docIntel.bicep similarity index 63% rename from infra/modules/cognitive-services-docIntel.bicep rename to infra/modules/cognitive/docIntel.bicep index 8eded9c85..9cd40d980 100644 --- a/infra/modules/cognitive-services-docIntel.bicep +++ b/infra/modules/cognitive/docIntel.bicep @@ -1,4 +1,8 @@ param suffix string = uniqueString(resourceGroup().id) + +param vnetId string +param privateEndpointSubnetId string + param managedIdentityPrincipalId string metadata description = 'Creates an Azure Document Intelligence (form recognizer) instance.' @@ -6,6 +10,9 @@ metadata description = 'Creates an Azure Document Intelligence (form recognizer) param name string param location string = resourceGroup().location +@description('The tags to be assigned to the created resources.') +param tags object + @description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') param customSubDomainName string = name param kind string = 'FormRecognizer' @@ -30,6 +37,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { name: name location: location kind: kind + tags: tags properties: { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess @@ -39,6 +47,28 @@ resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { sku: sku } +////////////////////////// Private endpoint + +module module_DocIntel_pe '../network/private-endpoint.bicep' = { + name: 'module_DocIntel_pe${suffix}' + params: { + suffix: suffix + location: location + tags: tags + + serviceName_Used_for_PE: name + + DNSZoneName: 'privatelink.cognitiveservices.azure.com' // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns + vnetId: vnetId + privateEndpointSubnetId: privateEndpointSubnetId + + privateLinkServiceId: account.id + privateLinkServiceConnections_GroupIds: ['account'] // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource + } +} + +////////////////////////// RBAC + // Cognitive Services User resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid('Cognitive Services User-${suffix}') @@ -53,4 +83,6 @@ resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } +////////////////////////// Output + output endpoint string = account.properties.endpoint diff --git a/infra/modules/cognitive-services-openAI.bicep b/infra/modules/cognitive/openAI.bicep similarity index 69% rename from infra/modules/cognitive-services-openAI.bicep rename to infra/modules/cognitive/openAI.bicep index 6bccecef1..c925329a9 100644 --- a/infra/modules/cognitive-services-openAI.bicep +++ b/infra/modules/cognitive/openAI.bicep @@ -1,10 +1,12 @@ param suffix string = uniqueString(resourceGroup().id) -param managedIdentityPrincipalId string metadata description = 'Creates an Azure Cognitive Services instance.' param name string param location string = resourceGroup().location -param tags object = {} + +@description('The tags to be assigned to the created resources.') +param tags object + @description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') param customSubDomainName string = name param deployments array = [] @@ -17,6 +19,12 @@ param sku object = { } param allowedIpRules array = [] + +param vnetId string +param privateEndpointSubnetId string + +param managedIdentityPrincipalId string + param networkAcls object = empty(allowedIpRules) ? { defaultAction: 'Allow' @@ -47,17 +55,37 @@ resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01 name: deployment.name properties: { model: deployment.model - raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + raiPolicyName: deployment.?raiPolicyName ?? null + } + sku: deployment.?sku ?? { + name: 'Standard' + capacity: 1 } - sku: contains(deployment, 'sku') - ? deployment.sku - : { - name: 'Standard' - capacity: 1 - } } ] +////////////////////////// Private endpoint + +module module_openai_pe '../network/private-endpoint.bicep' = { + name: 'module_openai_pe_${suffix}' + params: { + suffix: suffix + location: location + tags: tags + + serviceName_Used_for_PE: name + + DNSZoneName: 'privatelink.openai.azure.com' // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns + vnetId: vnetId + privateEndpointSubnetId: privateEndpointSubnetId + + privateLinkServiceId: account.id + privateLinkServiceConnections_GroupIds: ['account'] // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource + } +} + +////////////////////////// RBAC + // Cognitive Services OpenAI Contributor resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid('Cognitive Services OpenAI Contributor-${suffix}') @@ -86,6 +114,8 @@ resource roleAssignment2 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } +////////////////////////// Output + output endpoint string = account.properties.endpoint output id string = account.id output name string = account.name diff --git a/infra/modules/host/container-app-env.bicep b/infra/modules/host/container-app-env.bicep new file mode 100644 index 000000000..a870eb9d8 --- /dev/null +++ b/infra/modules/host/container-app-env.bicep @@ -0,0 +1,64 @@ +targetScope = 'resourceGroup' + +param suffix string = uniqueString(resourceGroup().id) + +@description('The location where the resources will be created.') +param location string = resourceGroup().location + +@description('The tags to be assigned to the created resources.') +param tags object + +@description('The name of the container apps environment. If set, it overrides the name generated by the template.') +param containerAppsEnvironmentName string = 'km-cae-${suffix}' + +@description('The name of the log analytics workspace resource create in another module.') +param logAnalyticsWorkspaceName string + +@description('The name of the application insights resource create in another module.') +param applicationInsightsName string + +@description('The subnet id of the subnet where the container apps environment will be deployed.') +param acaSubnetId string + +//////// Previously created resources + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +//////// + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-10-01' = { + name: containerAppsEnvironmentName + location: location + tags: tags + // sku: { + // name: 'Consumption' + // } + properties: { + daprAIInstrumentationKey: applicationInsights.properties.InstrumentationKey + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + + // network + zoneRedundant: true + vnetConfiguration: { + infrastructureSubnetId: acaSubnetId + internal: true + } + } +} + +output containerAppsEnvironmentName string = containerAppsEnvironment.name +output containerAppsEnvironmentId string = containerAppsEnvironment.id +output containerAppsEnvironmentDomain string = containerAppsEnvironment.properties.defaultDomain +output containerAppsEnvironmentStaticIp string = containerAppsEnvironment.properties.staticIp diff --git a/infra/modules/container-app.bicep b/infra/modules/host/container-app.bicep similarity index 89% rename from infra/modules/container-app.bicep rename to infra/modules/host/container-app.bicep index 015c21053..5a9b494bc 100644 --- a/infra/modules/container-app.bicep +++ b/infra/modules/host/container-app.bicep @@ -4,14 +4,18 @@ param suffix string = uniqueString(resourceGroup().id) param location string = resourceGroup().location +@description('The tags to be assigned to the created resources.') +param tags object + param managedIdentityId string param managedIdentityClientId string +param KernelMemoryImageTag string = 'latest' param kmServiceName string = 'km-service-${suffix}' param containerAppsEnvironmentId string -param appInsightsInstrumentationKey string -param applicationInsightsConnectionString string +@description('The name of the application insights resource create in another module.') +param applicationInsightsName string param AzureBlobs_Account string param AzureQueues_Account string @@ -26,16 +30,25 @@ param AzureAIDocIntel_Endpoint string param KernelMemory__ServiceAuthorization__AccessKey1 string param KernelMemory__ServiceAuthorization__AccessKey2 string +//////// Previously created resources + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} + +//////// + resource kmService 'Microsoft.App/containerApps@2023-05-01' = { name: kmServiceName location: location + tags: tags properties: { environmentId: containerAppsEnvironmentId configuration: { secrets: [ { name: 'appinsights-key' - value: appInsightsInstrumentationKey + value: applicationInsights.properties.InstrumentationKey } ] registries: [] @@ -56,7 +69,7 @@ resource kmService 'Microsoft.App/containerApps@2023-05-01' = { containers: [ { name: 'kernelmemory-service' - image: 'docker.io/kernelmemory/service:latest' + image: 'docker.io/kernelmemory/service:${KernelMemoryImageTag}' command: [] resources: { cpu: json('0.25') @@ -69,7 +82,7 @@ resource kmService 'Microsoft.App/containerApps@2023-05-01' = { } { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: applicationInsightsConnectionString + value: applicationInsights.properties.ConnectionString } { diff --git a/infra/modules/managed-identity.bicep b/infra/modules/identity/managed-identity.bicep similarity index 100% rename from infra/modules/managed-identity.bicep rename to infra/modules/identity/managed-identity.bicep diff --git a/infra/modules/container-apps-environment.bicep b/infra/modules/monitoring/insights.bicep similarity index 52% rename from infra/modules/container-apps-environment.bicep rename to infra/modules/monitoring/insights.bicep index d2cb2303d..b5830818f 100644 --- a/infra/modules/container-apps-environment.bicep +++ b/infra/modules/monitoring/insights.bicep @@ -5,11 +5,8 @@ param suffix string = uniqueString(resourceGroup().id) @description('The location where the resources will be created.') param location string = resourceGroup().location -@description('Optional. The tags to be assigned to the created resources.') -param tags object = {} - -@description('The name of the container apps environment. If set, it overrides the name generated by the template.') -param containerAppsEnvironmentName string = 'km-cae-${suffix}' +@description('The tags to be assigned to the created resources.') +param tags object @description('The name of the log analytics workspace. If set, it overrides the name generated by the template.') param logAnalyticsWorkspaceName string = 'km-log-${suffix}' @@ -43,32 +40,10 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { } } -resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-10-01' = { - name: containerAppsEnvironmentName - location: location - tags: tags - sku: { - name: 'Consumption' - } - properties: { - daprAIInstrumentationKey: applicationInsights.properties.InstrumentationKey - appLogsConfiguration: { - destination: 'log-analytics' - logAnalyticsConfiguration: { - customerId: logAnalyticsWorkspace.properties.customerId - sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey - } - } - } -} - -output containerAppsEnvironmentName string = containerAppsEnvironment.name -output containerAppsEnvironmentId string = containerAppsEnvironment.id - output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name @description('The name of the application insights.') output applicationInsightsName string = applicationInsights.name -output applicationInsightsInstrumentationKey string = applicationInsights.properties.InstrumentationKey -output applicationInsightsConnectionString string = applicationInsights.properties.ConnectionString +// output applicationInsightsInstrumentationKey string = applicationInsights.properties.InstrumentationKey +// output applicationInsightsConnectionString string = applicationInsights.properties.ConnectionString diff --git a/infra/modules/network/app-gateway.bicep b/infra/modules/network/app-gateway.bicep new file mode 100644 index 000000000..a36237c88 --- /dev/null +++ b/infra/modules/network/app-gateway.bicep @@ -0,0 +1,247 @@ +param suffix string = uniqueString(resourceGroup().id) + +@description('The location where the App Gateway will be deployed') +param location string + +@description('The tags that will be applied to the App Gateway') +param tags object + +param staticIp string +param defaultDomain string +param vnetId string + +@description('The subnet ID that will be used for the App Gateway configuration') +param subnetId string + +@description('The FQDN of the Container App') +param containerAppFqdn string + +//////////////////////////////////////////////////////// + +var appGatewayName = 'km-appG-${suffix}' + +var ipAddressName = 'km-appG-pip-${suffix}' + +//////////////////////////////////////////////////////// Private DNS Zone + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: defaultDomain + location: 'global' + tags: tags +} + +resource starRecordSet 'Microsoft.Network/privateDnsZones/A@2020-06-01' = { + name: '*' + parent: privateDnsZone + properties: { + ttl: 3600 + aRecords: [ + { + ipv4Address: staticIp + } + ] + } +} + +resource atRecordSet 'Microsoft.Network/privateDnsZones/A@2020-06-01' = { + name: '@' + parent: privateDnsZone + properties: { + ttl: 3600 + aRecords: [ + { + ipv4Address: staticIp + } + ] + } +} + +resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: 'pdns-link' + parent: privateDnsZone + tags: tags + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: vnetId + } + } +} + +//////////////////////////////////////////////////////// PiP and App Gateway + +resource publicIp 'Microsoft.Network/publicIPAddresses@2023-11-01' = { + name: ipAddressName + location: location + sku: { + name: 'Standard' + } + zones: [ + '1' + ] + properties: { + publicIPAddressVersion: 'IPv4' + publicIPAllocationMethod: 'Static' + } +} + +resource appGateway 'Microsoft.Network/applicationGateways@2023-11-01' = { + name: appGatewayName + location: location + tags: tags + zones: [ + '1' + ] + properties: { + sku: { + tier: 'Standard_v2' + capacity: 1 + name: 'Standard_v2' + } + gatewayIPConfigurations: [ + { + name: 'appgateway-subnet' + properties: { + subnet: { + id: subnetId + } + } + } + ] + frontendIPConfigurations: [ + { + name: 'my-frontend' + properties: { + publicIPAddress: { + id: publicIp.id + } + } + } + ] + frontendPorts: [ + { + name: 'port_80' + properties: { + port: 80 + } + } + ] + backendAddressPools: [ + { + name: 'my-agw-backend-pool' + properties: { + backendAddresses: [ + { + fqdn: containerAppFqdn + } + ] + } + } + ] + probes: [ + { + name: 'health-http' + properties: { + protocol: 'Http' + path: '/health' + interval: 30 + timeout: 30 + unhealthyThreshold: 3 + pickHostNameFromBackendHttpSettings: true + minServers: 0 + match: {} + } + } + { + name: 'health-https' + properties: { + protocol: 'Https' + path: '/health' + interval: 30 + timeout: 30 + unhealthyThreshold: 3 + pickHostNameFromBackendHttpSettings: true + minServers: 0 + match: {} + } + } + ] + backendHttpSettingsCollection: [ + { + name: 'backend-setting-https' + properties: { + protocol: 'Https' + port: 443 + cookieBasedAffinity: 'Disabled' + requestTimeout: 20 + pickHostNameFromBackendAddress: true + probe: { + id: resourceId('Microsoft.Network/applicationGateways/probes', appGatewayName, 'health-https') + } + } + } + { + name: 'backend-setting-http' + properties: { + protocol: 'Http' + port: 80 + cookieBasedAffinity: 'Disabled' + requestTimeout: 20 + pickHostNameFromBackendAddress: true + probe: { + id: resourceId('Microsoft.Network/applicationGateways/probes', appGatewayName, 'health-http') + } + } + } + ] + httpListeners: [ + { + name: 'my-agw-listener' + properties: { + frontendIPConfiguration: { + id: resourceId( + 'Microsoft.Network/applicationGateways/frontendIPConfigurations', + appGatewayName, + 'my-frontend' + ) + } + frontendPort: { + id: resourceId('Microsoft.Network/applicationGateways/frontendPorts', appGatewayName, 'port_80') + } + protocol: 'Http' + } + } + ] + requestRoutingRules: [ + { + name: 'my-agw-routing-rule' + properties: { + priority: 1 + ruleType: 'Basic' + httpListener: { + id: resourceId('Microsoft.Network/applicationGateways/httpListeners', appGatewayName, 'my-agw-listener') + } + backendAddressPool: { + id: resourceId( + 'Microsoft.Network/applicationGateways/backendAddressPools', + appGatewayName, + 'my-agw-backend-pool' + ) + } + backendHttpSettings: { + id: resourceId( + 'Microsoft.Network/applicationGateways/backendHttpSettingsCollection', + appGatewayName, + 'backend-setting-https' + ) + } + } + } + ] + enableHttp2: true + } +} + +@description('Public IP') +output ipAddress string = publicIp.properties.ipAddress diff --git a/infra/modules/network/dns.bicep b/infra/modules/network/dns.bicep new file mode 100644 index 000000000..f88f9e398 --- /dev/null +++ b/infra/modules/network/dns.bicep @@ -0,0 +1,26 @@ +param vnetId string +param tags object = {} + +param privateDnsZoneName string + +resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: privateDnsZoneName + location: 'global' + tags: tags + properties: {} +} + +resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + parent: privateDnsZone + name: '${privateDnsZoneName}-link' + location: 'global' + properties: { + registrationEnabled: false + virtualNetwork: { + id: vnetId + } + } +} + +output dnsZoneName string = privateDnsZoneName +output dnsZoneId string = privateDnsZone.id diff --git a/infra/modules/network/private-endpoint.bicep b/infra/modules/network/private-endpoint.bicep new file mode 100644 index 000000000..afeb55312 --- /dev/null +++ b/infra/modules/network/private-endpoint.bicep @@ -0,0 +1,66 @@ +param suffix string = uniqueString(resourceGroup().id) + +param location string = resourceGroup().location + +@description('The tags to be assigned to the created resources.') +param tags object + +////////////////////////// + +param serviceName_Used_for_PE string + +param privateEndpointSubnetId string + +param privateLinkServiceId string + +param privateLinkServiceConnections_GroupIds array + +param DNSZoneName string + +param vnetId string + +////////////////////////// + +module module_dns_2 '../network/dns.bicep' = { + name: 'module-dns-${serviceName_Used_for_PE}-pe' + params: { + vnetId: vnetId + privateDnsZoneName: DNSZoneName + tags: tags + } +} + +resource privateEndpoint 'Microsoft.Network/privateEndpoints@2024-01-01' = { + name: '${serviceName_Used_for_PE}-pe' + location: location + tags: tags + properties: { + subnet: { + id: privateEndpointSubnetId + } + privateLinkServiceConnections: [ + { + name: 'private-endpoint-connection' + properties: { + privateLinkServiceId: privateLinkServiceId + groupIds: privateLinkServiceConnections_GroupIds + } + } + ] + } +} + +resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-01-01' = { + name: 'privateDnsZoneGroup-${suffix}' + parent: privateEndpoint + properties: { + privateDnsZoneConfigs: [ + { + name: 'privateDnsZoneGroup-Config-${suffix}' + properties: { + privateDnsZoneId: module_dns_2.outputs.dnsZoneId + } + } + ] + } +} diff --git a/infra/modules/network/virtual-network.bicep b/infra/modules/network/virtual-network.bicep new file mode 100644 index 000000000..b974a5304 --- /dev/null +++ b/infra/modules/network/virtual-network.bicep @@ -0,0 +1,74 @@ +@description('The name of the virtual network') +param vnetName string + +@description('Location of the Vnet') +param location string + +@description('The tags that will be applied to the VNet') +param tags object + +var InfrastructureSubnetName = 'infrastructure-subnet' +var ApplicationGatewaySubnetName = 'app-gateway-subnet' +var PrivateEndpointSubnetName = 'private-endpoint-subnet' + +param VirtualNetworkAddressSpace string +param InfrastructureSubnetAddressRange string +param ApplicationGatewaySubnetAddressRange string +param PrivateEndpointSubnetAddressRange string + +resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = { + name: vnetName + location: location + tags: tags + properties: { + addressSpace: { + addressPrefixes: [ + VirtualNetworkAddressSpace + ] + } + subnets: [ + { + name: InfrastructureSubnetName + properties: { + addressPrefix: InfrastructureSubnetAddressRange + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + { + name: ApplicationGatewaySubnetName + properties: { + addressPrefix: ApplicationGatewaySubnetAddressRange + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + { + name: PrivateEndpointSubnetName + properties: { + addressPrefix: PrivateEndpointSubnetAddressRange + privateEndpointNetworkPolicies: 'Enabled' + privateLinkServiceNetworkPolicies: 'Disabled' + } + } + ] + } + + resource envInfraSubnet 'subnets' existing = { + name: InfrastructureSubnetName + } + + resource appGatewaySubnet 'subnets' existing = { + name: ApplicationGatewaySubnetName + } + + resource privateEndpointSubnet 'subnets' existing = { + name: PrivateEndpointSubnetName + } +} + +output vnetName string = vnet.name +output vnetId string = vnet.id +output envInfraSubnetId string = vnet::envInfraSubnet.id +output appGatewaySubnetId string = vnet::appGatewaySubnet.id +output privateEndpointSubnetId string = vnet::privateEndpointSubnet.id diff --git a/infra/modules/storage.bicep b/infra/modules/storage.bicep index a3f814c69..b25232f14 100644 --- a/infra/modules/storage.bicep +++ b/infra/modules/storage.bicep @@ -4,8 +4,8 @@ param suffix string = uniqueString(resourceGroup().id) param location string = resourceGroup().location -@description('Optional. The tags to be assigned to the created resources.') -param tags object = {} +@description('The tags to be assigned to the created resources.') +param tags object @description('The name of the Azure Storage Account.') param storageAccountName string = 'kmstorage${suffix}' //'storage${uniqueString(resourceGroup().id)}' @@ -16,6 +16,9 @@ param storageBlobContainerName string = 'smemory' @description('The name of the Queue in Azure Storage.') param externalTasksQueueName string = 'km-queue-${suffix}' +param vnetId string +param privateEndpointSubnetId string + param managedIdentityPrincipalId string resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { @@ -29,9 +32,80 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { properties: { allowBlobPublicAccess: false allowSharedKeyAccess: false + + publicNetworkAccess: 'Disabled' + } +} + +////////////////////////// Seeding + +resource storageBlobService 'Microsoft.Storage/storageAccounts/blobServices@2021-09-01' = { + name: 'default' + parent: storageAccount +} + +resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01' = { + parent: storageBlobService + name: storageBlobContainerName + properties: { + publicAccess: 'None' + } +} + +resource storageQueuesService 'Microsoft.Storage/storageAccounts/queueServices@2021-09-01' = { + name: 'default' + parent: storageAccount +} + +resource queue 'Microsoft.Storage/storageAccounts/queueServices/queues@2021-09-01' = { + name: externalTasksQueueName + parent: storageQueuesService + properties: {} +} + +////////////////////////// Private endpoint + +module module_blob_pe 'network/private-endpoint.bicep' = { + name: 'module_blob_pe_${suffix}' + params: { + suffix: suffix + location: location + tags: tags + + serviceName_Used_for_PE: '${storageAccountName}-blob' + + DNSZoneName: 'privatelink.blob.${environment().suffixes.storage}' // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns + vnetId: vnetId + privateEndpointSubnetId: privateEndpointSubnetId + + privateLinkServiceId: storageAccount.id + privateLinkServiceConnections_GroupIds: ['blob'] // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource } } +module module_queue_pe 'network/private-endpoint.bicep' = { + dependsOn: [ + module_blob_pe + ] + name: 'module_queue_pe_${suffix}' + params: { + suffix: suffix + location: location + tags: tags + + serviceName_Used_for_PE: '${storageAccountName}-queue' + + DNSZoneName: 'privatelink.queue.${environment().suffixes.storage}' // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-dns + vnetId: vnetId + privateEndpointSubnetId: privateEndpointSubnetId + + privateLinkServiceId: storageAccount.id + privateLinkServiceConnections_GroupIds: ['queue'] // https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource + } +} + +////////////////////////// RBAC + // Storage Queue Data Contributor resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid('Storage Queue Data Contributor-${suffix}') @@ -90,7 +164,7 @@ resource roleAssignment3 'Microsoft.Authorization/roleAssignments@2022-04-01' = // Storage Queue Data Message Processor resource roleAssignment4 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('// Storage Queue Data Message Processor-${suffix}') + name: guid('Storage Queue Data Message Processor-${suffix}') scope: storageAccount properties: { roleDefinitionId: subscriptionResourceId( @@ -102,25 +176,7 @@ resource roleAssignment4 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } -resource storageBlobService 'Microsoft.Storage/storageAccounts/blobServices@2021-09-01' = { - name: 'default' - parent: storageAccount -} - -resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01' = { - parent: storageBlobService - name: storageBlobContainerName -} - -resource storageQueuesService 'Microsoft.Storage/storageAccounts/queueServices@2021-09-01' = { - name: 'default' - parent: storageAccount -} - -resource queue 'Microsoft.Storage/storageAccounts/queueServices/queues@2021-09-01' = { - name: externalTasksQueueName - parent: storageQueuesService -} +////////////////////////// Output @description('The storage account name.') output storageAccountName string = storageAccount.name