From dd2d5048b06608ed33c1c87f02c5c702aaaa929c Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Tue, 26 Aug 2025 17:45:08 +0530 Subject: [PATCH 01/29] Bicep Standard Changes --- App/kernel-memory/infra/main.bicep | 6 +++ .../infra/modules/ai-search.bicep | 8 ++++ .../modules/cognitive-services-docIntel.bicep | 14 ++++++ .../modules/cognitive-services-openAI.bicep | 22 +++++++++ .../infra/modules/container-app.bicep | 45 +++++++++++++++++++ .../modules/container-apps-environment.bicep | 20 ++++++--- .../infra/modules/managed-identity.bicep | 8 ++++ App/kernel-memory/infra/modules/storage.bicep | 5 +++ 8 files changed, 123 insertions(+), 5 deletions(-) diff --git a/App/kernel-memory/infra/main.bicep b/App/kernel-memory/infra/main.bicep index 96059d90..f8965f52 100644 --- a/App/kernel-memory/infra/main.bicep +++ b/App/kernel-memory/infra/main.bicep @@ -41,6 +41,9 @@ The value is stored as an environment variable and is required by the web servic @secure() param WebServiceAuthorizationKey2 string +@description('Optional. The tags to apply to all deployed Azure resources.') +param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} + var rg = resourceGroup() var location = resourceGroup().location @@ -155,6 +158,7 @@ module openAi 'modules/cognitive-services-openAI.bicep' = { name: 'S0' } deployments: openAiDeployments + tags : tags } } @@ -174,6 +178,7 @@ module docIntel 'modules/cognitive-services-docIntel.bicep' = { sku: { name: 'S0' } + tags : tags } } @@ -221,6 +226,7 @@ module containerAppService 'modules/container-app.bicep' = { AzureOpenAIText_Deployment: chatGpt.deploymentName AzureOpenAIText_Endpoint: openAi.outputs.endpoint AzureAIDocIntel_Endpoint: docIntel.outputs.endpoint + tags : tags } } diff --git a/App/kernel-memory/infra/modules/ai-search.bicep b/App/kernel-memory/infra/modules/ai-search.bicep index 55128ed3..19330182 100644 --- a/App/kernel-memory/infra/modules/ai-search.bicep +++ b/App/kernel-memory/infra/modules/ai-search.bicep @@ -1,5 +1,9 @@ +@description('Required. Contains ManagedIdentity Principal ID.') param managedIdentityPrincipalId string +@description('Required. Suffix to create unique resource names; 4-15 characters.') +@minLength(4) +@maxLength(15) param suffix string = uniqueString(resourceGroup().id) @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.') @@ -45,6 +49,9 @@ param hostingMode string = 'default' @description('Location for all resources.') param location string = resourceGroup().location +@description('Optional. Tags to be applied to the resources.') +param tags object = {} + resource search 'Microsoft.Search/searchServices@2023-11-01' = { name: name location: location @@ -89,4 +96,5 @@ resource roleAssignment2 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } +@description('Contains ManagedIdentity Principal ID.') output searchName string = search.name diff --git a/App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep b/App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep index 8eded9c8..63d73b86 100644 --- a/App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep +++ b/App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep @@ -1,9 +1,17 @@ +@description('Required. Suffix to create unique resource names; 4-15 characters.') +@minLength(4) +@maxLength(15) param suffix string = uniqueString(resourceGroup().id) + +@description('Required. Contains ManagedIdentity Principal ID.') param managedIdentityPrincipalId string metadata description = 'Creates an Azure Document Intelligence (form recognizer) instance.' +@description('Required. Contains Name.') param name string + +@description('Required. Contains Resource Group Location.') param location string = resourceGroup().location @description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') @@ -16,6 +24,7 @@ param sku object = { name: 'S0' } +@description('Required. Contains IP Rules.') param allowedIpRules array = [] param networkAcls object = empty(allowedIpRules) ? { @@ -26,6 +35,10 @@ param networkAcls object = empty(allowedIpRules) defaultAction: 'Deny' } +@description('Optional. Tags to be applied to the resources.') +param tags object = {} + + resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { name: name location: location @@ -53,4 +66,5 @@ resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } +@description('Contains Endpoint.') output endpoint string = account.properties.endpoint diff --git a/App/kernel-memory/infra/modules/cognitive-services-openAI.bicep b/App/kernel-memory/infra/modules/cognitive-services-openAI.bicep index 6bccecef..22f8455f 100644 --- a/App/kernel-memory/infra/modules/cognitive-services-openAI.bicep +++ b/App/kernel-memory/infra/modules/cognitive-services-openAI.bicep @@ -1,13 +1,29 @@ +@description('Required. Suffix to create unique resource names; 4-15 characters.') +@minLength(4) +@maxLength(15) param suffix string = uniqueString(resourceGroup().id) + +@description('Required. Contains ManagedIdentity Principal ID.') param managedIdentityPrincipalId string metadata description = 'Creates an Azure Cognitive Services instance.' + +@description('Required. Contains Name.') param name string + +@description('Required. Contains Resource Group Location.') param location string = resourceGroup().location + +@description('Required. Contains Tags.') 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 + +@description('Required. Contains Deployments.') param deployments array = [] + +@description('Optional. Contains the type.') param kind string = 'OpenAI' @allowed(['Enabled', 'Disabled']) @@ -16,6 +32,7 @@ param sku object = { name: 'S0' } +@description('Required. Contains the IP rules which are allowed.') param allowedIpRules array = [] param networkAcls object = empty(allowedIpRules) ? { @@ -86,6 +103,11 @@ resource roleAssignment2 'Microsoft.Authorization/roleAssignments@2022-04-01' = } } +@description('Contains the Endpoint.') output endpoint string = account.properties.endpoint + +@description('Contains the ID.') output id string = account.id + +@description('Contains Account Name.') output name string = account.name diff --git a/App/kernel-memory/infra/modules/container-app.bicep b/App/kernel-memory/infra/modules/container-app.bicep index 015c2105..0aeff12a 100644 --- a/App/kernel-memory/infra/modules/container-app.bicep +++ b/App/kernel-memory/infra/modules/container-app.bicep @@ -1,31 +1,67 @@ targetScope = 'resourceGroup' +@description('Required. Suffix to create unique resource names; 4-15 characters.') +@minLength(4) +@maxLength(15) param suffix string = uniqueString(resourceGroup().id) +@description('Required. Contains Resource Group Location.') param location string = resourceGroup().location +@description('Required. Contains Managed Identity ID.') param managedIdentityId string + +@description('Required. Contains Managed Identity Client ID.') param managedIdentityClientId string +@description('Required. Contains KM Service Name.') param kmServiceName string = 'km-service-${suffix}' +@description('Required. Contains Container App Environment ID.') param containerAppsEnvironmentId string + +@description('Required. Contains AppInsights Instrumentation Key.') param appInsightsInstrumentationKey string + +@description('Required. Contains App Insights Connection String.') param applicationInsightsConnectionString string +@description('Required. Contains Azure Blobs Account.') param AzureBlobs_Account string + +@description('Required. Contains Azure Queues Account.') param AzureQueues_Account string + +@description('Required. Contains Azure Queues Queue Name.') param AzureQueues_QueueName string + +@description('Required. Contains Azure AI Search Endpoint.') param AzureAISearch_Endpoint string + +@description('Required. Contains Azure OpenAI Text Endpoint.') param AzureOpenAIText_Endpoint string + +@description('Required. Contains Azure OpenAI Text Deployment.') param AzureOpenAIText_Deployment string + +@description('Required. Contains Azure OpenAI Embedding Endpoint.') param AzureOpenAIEmbedding_Endpoint string + +@description('Required. Contains Azure OpenAI Embedding Deployment.') param AzureOpenAIEmbedding_Deployment string + +@description('Required. Contains Azure AI DocIntel Endpoint.') param AzureAIDocIntel_Endpoint string +@description('Required. Contains Kernel Memory AccessKey 1.') param KernelMemory__ServiceAuthorization__AccessKey1 string + +@description('Required. Contains Kernel Memory AccessKey 2.') param KernelMemory__ServiceAuthorization__AccessKey2 string +@description('Optional. Tags to be applied to the resources.') +param tags object = {} + resource kmService 'Microsoft.App/containerApps@2023-05-01' = { name: kmServiceName location: location @@ -181,6 +217,7 @@ resource kmService 'Microsoft.App/containerApps@2023-05-01' = { } } } + tags : tags identity: { type: 'UserAssigned' userAssignedIdentities: { @@ -189,9 +226,17 @@ resource kmService 'Microsoft.App/containerApps@2023-05-01' = { } } +@description('Contains KM Service Name.') output kmServiceName string = kmService.name + +@description('Contains KM Service ID.') output kmServiceId string = kmService.id + +@description('Contains KM Service AccessKey 1.') output kmServiceAccessKey1 string = KernelMemory__ServiceAuthorization__AccessKey1 + +@description('Contains KM Service AccessKey 2.') output kmServiceAccessKey2 string = KernelMemory__ServiceAuthorization__AccessKey2 + @description('The FQDN of the frontend web app service.') output kmServiceFQDN string = kmService.properties.configuration.ingress.fqdn diff --git a/App/kernel-memory/infra/modules/container-apps-environment.bicep b/App/kernel-memory/infra/modules/container-apps-environment.bicep index d2cb2303..437338db 100644 --- a/App/kernel-memory/infra/modules/container-apps-environment.bicep +++ b/App/kernel-memory/infra/modules/container-apps-environment.bicep @@ -1,20 +1,23 @@ targetScope = 'resourceGroup' +@description('Required. Suffix to create unique resource names; 4-15 characters.') +@minLength(4) +@maxLength(15) param suffix string = uniqueString(resourceGroup().id) -@description('The location where the resources will be created.') +@description('Required. 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.') +@description('Required. 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. If set, it overrides the name generated by the template.') +@description('Required. The name of the log analytics workspace. If set, it overrides the name generated by the template.') param logAnalyticsWorkspaceName string = 'km-log-${suffix}' -@description(' The name of the application insights. If set, it overrides the name generated by the template.') +@description('Required. The name of the application insights. If set, it overrides the name generated by the template.') param applicationInsightsName string = 'km-appins-${suffix}' resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { @@ -62,13 +65,20 @@ resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2022-10-01' } } +@description('Contains App Environment Name.') output containerAppsEnvironmentName string = containerAppsEnvironment.name + +@description('Contains App Environment ID.') output containerAppsEnvironmentId string = containerAppsEnvironment.id +@description('Contains LogAnalytics Workspace Name.') output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name -@description('The name of the application insights.') +@description('Contains App Insights Name.') output applicationInsightsName string = applicationInsights.name +@description('Contains App Insights Instrumentation Key.') output applicationInsightsInstrumentationKey string = applicationInsights.properties.InstrumentationKey + +@description('Contains App Insights Connection String.') output applicationInsightsConnectionString string = applicationInsights.properties.ConnectionString diff --git a/App/kernel-memory/infra/modules/managed-identity.bicep b/App/kernel-memory/infra/modules/managed-identity.bicep index 917c5eb0..66c9ff6c 100644 --- a/App/kernel-memory/infra/modules/managed-identity.bicep +++ b/App/kernel-memory/infra/modules/managed-identity.bicep @@ -1,3 +1,6 @@ +@description('Required. Suffix to create unique resource names; 4-15 characters.') +@minLength(4) +@maxLength(15) param suffix string = uniqueString(resourceGroup().id) @description('Managed Identity name.') @@ -26,6 +29,11 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { } } +@description('Contains Managed Identity ID.') output managedIdentityId string = managedIdentity.id + +@description('Contains Managed Identity Principal ID.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('Contains Managed Identity Client ID.') output managedIdentityClientId string = managedIdentity.properties.clientId diff --git a/App/kernel-memory/infra/modules/storage.bicep b/App/kernel-memory/infra/modules/storage.bicep index a3f814c6..d499ecc7 100644 --- a/App/kernel-memory/infra/modules/storage.bicep +++ b/App/kernel-memory/infra/modules/storage.bicep @@ -1,7 +1,11 @@ targetScope = 'resourceGroup' +@description('Required. Suffix to create unique resource names; 4-15 characters.') +@minLength(4) +@maxLength(15) param suffix string = uniqueString(resourceGroup().id) +@description('Required. Contains Resource Group Location.') param location string = resourceGroup().location @description('Optional. The tags to be assigned to the created resources.') @@ -16,6 +20,7 @@ param storageBlobContainerName string = 'smemory' @description('The name of the Queue in Azure Storage.') param externalTasksQueueName string = 'km-queue-${suffix}' +@description('Required. Contains ManagedIdentity Principal ID.') param managedIdentityPrincipalId string resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { From 510a47ac008a347388c3faf0f1d526af857c88c1 Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Fri, 5 Sep 2025 15:55:33 +0530 Subject: [PATCH 02/29] avm changes 1 --- App/kernel-memory/infra/main.bicep | 648 ++++++++++++++++-- App/kernel-memory/infra/modules/network.bicep | 251 +++++++ .../infra/modules/network/bastionHost.bicep | 104 +++ .../infra/modules/network/jumpbox.bicep | 155 +++++ .../infra/modules/network/main.bicep | 103 +++ .../modules/network/virtualNetwork.bicep | 157 +++++ 6 files changed, 1359 insertions(+), 59 deletions(-) create mode 100644 App/kernel-memory/infra/modules/network.bicep create mode 100644 App/kernel-memory/infra/modules/network/bastionHost.bicep create mode 100644 App/kernel-memory/infra/modules/network/jumpbox.bicep create mode 100644 App/kernel-memory/infra/modules/network/main.bicep create mode 100644 App/kernel-memory/infra/modules/network/virtualNetwork.bicep diff --git a/App/kernel-memory/infra/main.bicep b/App/kernel-memory/infra/main.bicep index f8965f52..3c4970bb 100644 --- a/App/kernel-memory/infra/main.bicep +++ b/App/kernel-memory/infra/main.bicep @@ -44,6 +44,70 @@ param WebServiceAuthorizationKey2 string @description('Optional. The tags to apply to all deployed Azure resources.') param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Enable private networking for applicable resources, aligned with the WAF recommendations. Defaults to false.') +param enablePrivateNetworking bool = false + +@description('Optional: Existing Log Analytics Workspace Resource ID') +param existingLogAnalyticsWorkspaceId string = '' + +@description('Optional. Admin username for the Jumpbox Virtual Machine. Set to custom value if enablePrivateNetworking is true.') +@secure() +param vmAdminUsername string? + +@description('Optional. Admin password for the Jumpbox Virtual Machine. Set to custom value if enablePrivateNetworking is true.') +@secure() +param vmAdminPassword string? + +@description('Optional. Size of the Jumpbox Virtual Machine when created. Set to custom value if enablePrivateNetworking is true.') +param vmSize string = 'Standard_DS2_v2' + +@description('Optional. Enable monitoring applicable resources, aligned with the Well Architected Framework recommendations. This setting enables Application Insights and Log Analytics and configures all the resources applicable resources to send logs. Defaults to false.') +param enableMonitoring bool = false + +@description('Optional. Enable redundancy for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false.') +param enableRedundancy bool = false + +@description('Optional. Enable scalability for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false.') +param enableScalability bool = false + +@description('Optional. Enable purge protection for the Key Vault') +param enablePurgeProtection bool = false + +// @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') +// param secretsExportConfiguration secretsExportConfigurationType? +// Replica regions list based on article in [Azure regions list](https://learn.microsoft.com/azure/reliability/regions-list) and [Enhance resilience by replicating your Log Analytics workspace across regions](https://learn.microsoft.com/azure/azure-monitor/logs/workspace-replication#supported-regions) for supported regions for Log Analytics Workspace. +var replicaRegionPairs = { + australiaeast: 'australiasoutheast' + centralus: 'westus' + eastasia: 'japaneast' + eastus: 'centralus' + eastus2: 'centralus' + japaneast: 'eastasia' + northeurope: 'westeurope' + southeastasia: 'eastasia' + uksouth: 'westeurope' + westeurope: 'northeurope' +} +var replicaLocation = replicaRegionPairs[resourceGroup().location] + +@description('Optional. The Container Registry hostname where the docker images for the container app are located.') +param containerRegistryHostname string = 'biabcontainerreg.azurecr.io' + +@description('Optional. The Container Image Name to deploy on the container app.') +param containerImageName string = 'macaebackend' + +@description('Optional. The Container Image Tag to deploy on the container app.') +param containerImageTag string = 'latest_2025-07-22_895' + +// Extracts subscription, resource group, and workspace name from the resource ID when using an existing Log Analytics workspace +var useExistingLogAnalytics = !empty(existingLogAnalyticsWorkspaceId) +var logAnalyticsWorkspaceResourceId = useExistingLogAnalytics + ? existingLogAnalyticsWorkspaceId + : logAnalyticsWorkspace!.outputs.resourceId + var rg = resourceGroup() var location = resourceGroup().location @@ -89,18 +153,172 @@ var openAiDeployments = [ } ] +// ========== Private DNS Zones ========== // +var privateDnsZones = [ + 'privatelink.cognitiveservices.azure.com' + 'privatelink.openai.azure.com' + 'privatelink.services.ai.azure.com' + 'privatelink.contentunderstanding.ai.azure.com' + 'privatelink.blob.${environment().suffixes.storage}' + 'privatelink.queue.${environment().suffixes.storage}' + 'privatelink.file.${environment().suffixes.storage}' + 'privatelink.api.azureml.ms' + 'privatelink.notebooks.azure.net' + 'privatelink.mongo.cosmos.azure.com' + 'privatelink.azconfig.io' + 'privatelink.vaultcore.azure.net' + 'privatelink.azurecr.io' + 'privatelink${environment().suffixes.sqlServerHostname}' + 'privatelink.azurewebsites.net' + 'privatelink.search.windows.net' +] +// DNS Zone Index Constants +var dnsZoneIndex = { + cognitiveServices: 0 + openAI: 1 + aiServices: 2 + contentUnderstanding: 3 + storageBlob: 4 + storageQueue: 5 + storageFile: 6 + aiFoundry: 7 + notebooks: 8 + cosmosDB: 9 + appConfig: 10 + keyVault: 11 + containerRegistry: 12 + sqlServer: 13 + appService: 14 + search: 15 +} +@batchSize(5) +module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.7.1' = [ + for (zone, i) in privateDnsZones: if (enablePrivateNetworking) { + name: 'dns-zone-${i}' + params: { + name: zone + tags: tags + enableTelemetry: enableTelemetry + virtualNetworkLinks: [{ virtualNetworkResourceId: network.outputs.subnetPrivateEndpointsResourceId }] + } + } +] + +@metadata({ + azd: { + type: 'location' + usageName: [ + 'OpenAI.GlobalStandard.gpt-4o-mini,150' + 'OpenAI.GlobalStandard.text-embedding-ada-002,80' + ] + } +}) +@description('Required. Location for AI Foundry deployment. This is the location where the AI Foundry resources will be deployed.') +param aiDeploymentsLocation string + +// ========== Log Analytics Workspace ========== // +// WAF best practices for Log Analytics: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-log-analytics +// WAF PSRules for Log Analytics: https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/#azure-monitor-logs +var logAnalyticsWorkspaceResourceName = 'log-${suffix}' +module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.12.0' = if (enableMonitoring) { + name: take('avm.res.operational-insights.workspace.${logAnalyticsWorkspaceResourceName}', 64) + params: { + name: logAnalyticsWorkspaceResourceName + tags: tags + location: location + enableTelemetry: enableTelemetry + skuName: 'PerGB2018' + dataRetention: 365 + features: { enableLogAccessUsingOnlyResourcePermissions: true } + diagnosticSettings: [{ useThisWorkspace: true }] + // WAF aligned configuration for Redundancy + dailyQuotaGb: enableRedundancy ? 10 : null //WAF recommendation: 10 GB per day is a good starting point for most workloads + replication: enableRedundancy + ? { + enabled: true + location: replicaLocation + } + : null + // WAF aligned configuration for Private Networking + publicNetworkAccessForIngestion: enablePrivateNetworking ? 'Disabled' : 'Enabled' + publicNetworkAccessForQuery: enablePrivateNetworking ? 'Disabled' : 'Enabled' + dataSources: enablePrivateNetworking + ? [ + { + tags: tags + eventLogName: 'Application' + eventTypes: [ + { + eventType: 'Error' + } + { + eventType: 'Warning' + } + { + eventType: 'Information' + } + ] + kind: 'WindowsEvent' + name: 'applicationEvent' + } + { + counterName: '% Processor Time' + instanceName: '*' + intervalSeconds: 60 + kind: 'WindowsPerformanceCounter' + name: 'windowsPerfCounter1' + objectName: 'Processor' + } + { + kind: 'IISLogs' + name: 'sampleIISLog1' + state: 'OnPremiseEnabled' + } + ] + : null + } +} + +module network 'modules/network.bicep' = if (enablePrivateNetworking) { + name: take('network-${suffix}-deployment', 64) + params: { + resourcesName: suffix + logAnalyticsWorkSpaceResourceId: logAnalyticsWorkspaceResourceId + vmAdminUsername: vmAdminUsername ?? 'JumpboxAdminUser' + vmAdminPassword: vmAdminPassword ?? 'JumpboxAdminP@ssw0rd1234!' + vmSize: vmSize ?? 'Standard_DS2_v2' // Default VM size + location: location + tags: tags + enableTelemetry: enableTelemetry + } +} + /* Module to create a Managed Identity. See https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview The managed identity is the main code-to-services and service-to-service authentication mechanism. */ -module managedidentity 'modules/managed-identity.bicep' = { - name: 'managedidentity-${suffix}' - scope: rg +// module managedidentity 'modules/managed-identity.bicep' = { +// name: 'managedidentity-${suffix}' +// scope: rg +// params: { +// location: location +// suffix: suffix +// } +// } + +// ========== AVM WAF ========== // +// ========== User Assigned Identity ========== // +// WAF best practices for identity and access management: https://learn.microsoft.com/en-us/azure/well-architected/security/identity-access +var userAssignedIdentityResourceName = 'id-${suffix}' +module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = { + name: take('avm.res.managed-identity.user-assigned-identity.${userAssignedIdentityResourceName}', 64) params: { + name: userAssignedIdentityResourceName location: location - suffix: suffix + tags: tags + enableTelemetry: enableTelemetry } } @@ -111,13 +329,89 @@ 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}' - scope: rg - params: { +// module storage 'modules/storage.bicep' = { +// name: 'storage-${suffix}' +// scope: rg +// params: { +// location: location +// suffix: suffix +// managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId +// } +// } + +// ========== AVM WAF ========== // +// ========== Storage account module ========== // + +var storageAccountName = 'storage-${suffix}' +module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { + name: take('avm.res.storage.storage-account.${storageAccountName}', 64) + params : { + name: storageAccountName location: location - suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId + managedIdentities: { systemAssigned: true } + minimumTlsVersion: 'TLS1_2' + enableTelemetry: enableTelemetry + tags: tags + accessTier: 'Hot' + supportsHttpsTrafficOnly: true + + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + principalType: 'ServicePrincipal' + } + ] + + // WAF aligned networking + networkAcls: { + bypass: 'AzureServices' + defaultAction: enablePrivateNetworking ? 'Deny' : 'Allow' + } + allowBlobPublicAccess: enablePrivateNetworking ? true : false + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' + + privateEndpoints: enablePrivateNetworking + ? [ + { + name: 'pep-blob-${suffix}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'storage-dns-zone-group-blob' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.storageBlob]!.outputs.resourceId + } + ] + } + subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + service: 'blob' + } + { + name: 'pep-queue-${suffix}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'storage-dns-zone-group-queue' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.storageQueue]!.outputs.resourceId + } + ] + } + subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + service: 'queue' + } + ] + : [] + + blobServices: { + corsRules: [] + deleteRetentionPolicyEnabled: false + containers: [ + { + name: 'data' + publicAccess: 'None' + } + ] + } } } @@ -128,14 +422,69 @@ 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}' - scope: rg +// module search 'modules/ai-search.bicep' = { +// name: 'search-${suffix}' +// scope: rg +// params: { +// location: location +// name: 'km-search-${suffix}' +// suffix: suffix +// managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId +// } +// } + +// ========== AI Foundry: AI Search ========== // +var aiSearchName = 'srch-${suffix}' +// var aiSearchConnectionName = 'myCon-${suffix}' +// var varKvSecretNameAzureSearchKey = 'AZURE-SEARCH-KEY' +// AI Foundry: AI Search +module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = { + name: take('avm.res.cognitive-search-services.${aiSearchName}', 64) params: { - location: location - name: 'km-search-${suffix}' - suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId + name: aiSearchName + tags: tags + location: aiDeploymentsLocation + enableTelemetry: enableTelemetry + diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null + sku: 'standard3' + managedIdentities: { userAssignedResourceIds: [userAssignedIdentity!.outputs.resourceId] } + replicaCount: 1 + partitionCount: 1 + + roleAssignments: [ + { + roleDefinitionIdOrName: 'Cognitive Services Contributor' // Cognitive Search Contributor + principalId: userAssignedIdentity.outputs.principalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'Cognitive Services OpenAI User'//'5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'// Cognitive Services OpenAI User + principalId: userAssignedIdentity.outputs.principalId + principalType: 'ServicePrincipal' + } + ] + disableLocalAuth: false + semanticSearch: 'free' + // secretsExportConfiguration: { + // keyVaultResourceId: keyvault.outputs.resourceId + // primaryAdminKeyName: varKvSecretNameAzureSearchKey + // } + // WAF aligned configuration for Private Networking + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' + privateEndpoints: enablePrivateNetworking + ? [ + { + name: 'pep-${aiSearchName}' + customNetworkInterfaceName: 'nic-${aiSearchName}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.search]!.outputs.resourceId } + ] + } + subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + } + ] + : [] } } @@ -146,19 +495,84 @@ 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}' - scope: rg +// module openAi 'modules/cognitive-services-openAI.bicep' = { +// name: 'openai-${suffix}' +// scope: rg +// params: { +// suffix: suffix +// managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId +// name: 'km-openai-${suffix}' +// location: location +// sku: { +// name: 'S0' +// } +// deployments: openAiDeployments +// tags : tags +// } +// } + +// ========== AVM WAF ========== // +// ========== Cognitive Services - OpenAI module ========== // + +var openAiAccountName = 'openai-${suffix}' + +module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { + name: take('avm.res.cognitiveservices.account.${openAiAccountName}', 64) params: { - suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId - name: 'km-openai-${suffix}' + name: openAiAccountName location: location + kind: 'OpenAI' sku: { name: 'S0' } + tags: tags + enableTelemetry: enableTelemetry + + managedIdentities: { + systemAssigned: true + } + + // WAF baseline + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' + networkAcls: { + defaultAction: enablePrivateNetworking ? 'Deny' : 'Allow' + bypass: 'AzureServices' + } + + privateEndpoints: enablePrivateNetworking + ? [ + { + name: 'pep-openai-${suffix}' + subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + service: 'account' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'openai-dns-zone-group' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAi]!.outputs.resourceId + } + ] + } + } + ] + : [] + + // Role assignments + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'Cognitive Services OpenAI Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'Cognitive Services OpenAI User' + principalType: 'ServicePrincipal' + } + ] + + // OpenAI deployments (pass array from main) deployments: openAiDeployments - tags : tags } } @@ -167,21 +581,80 @@ 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 docIntel 'modules/cognitive-services-docIntel.bicep' = { +// name: 'docIntel-${suffix}' +// scope: rg +// params: { +// suffix: suffix +// managedIdentityPrincipalId: userAssignedIdentity.outputs.managedIdentityPrincipalId +// name: 'km-docIntel-${suffix}' +// location: location +// sku: { +// name: 'S0' +// } +// tags : tags +// } +// } + +// ========== AVM WAF ========== // +// ========== Cognitive Services - docIntel module ========== // + +// Document Intelligence (Form Recognizer) +var docIntelAccountName = 'docIntel-${suffix}' + +module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { + name: take('avm.res.cognitiveservices.account.${docIntelAccountName}', 64) scope: rg params: { - suffix: suffix - managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId - name: 'km-docIntel-${suffix}' + name: docIntelAccountName location: location + kind: 'FormRecognizer' + tags: tags sku: { name: 'S0' } - tags : tags + managedIdentities: { + systemAssigned: true + } + + // Networking aligned to WAF + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' + networkAcls: { + bypass: 'AzureServices' + defaultAction: enablePrivateNetworking ? 'Deny' : 'Allow' + } + + // Private Endpoint for Form Recognizer + privateEndpoints: enablePrivateNetworking + ? [ + { + name: 'pep-docintel-${suffix}' + subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + service: 'account' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'docintel-dns-zone-group' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.formRecognizer]!.outputs.resourceId + } + ] + } + } + ] + : [] + + // Role Assignments + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'Cognitive Services User' + principalType: 'ServicePrincipal' + } + ] } } + /* Module to create an Azure Container Apps environment and a container app See https://learn.microsoft.com/en-us/azure/container-apps/environment @@ -202,31 +675,88 @@ 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}' - scope: rg +// module containerAppService 'modules/container-app.bicep' = { +// name: 'containerAppService-${suffix}' +// scope: rg +// params: { +// location: location +// suffix: suffix +// containerAppsEnvironmentId: containerAppsEnvironment.outputs.containerAppsEnvironmentId +// appInsightsInstrumentationKey: containerAppsEnvironment.outputs.applicationInsightsInstrumentationKey +// applicationInsightsConnectionString: containerAppsEnvironment.outputs.applicationInsightsConnectionString +// managedIdentityId: us.outputs.managedIdentityId +// managedIdentityClientId: managedidentity.outputs.managedIdentityClientId + +// 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 +// tags : tags +// } +// } + + +// ========== AVM WAF ========== // +// ========== Container App module ========== // + +var containerAppResourceName = 'ca-${suffix}' +module containerApp 'br/public:avm/res/app/container-app:0.18.1' = { + name: take('avm.res.app.container-app.${containerAppResourceName}', 64) params: { + name: containerAppResourceName + tags: tags location: location - suffix: suffix - containerAppsEnvironmentId: containerAppsEnvironment.outputs.containerAppsEnvironmentId - appInsightsInstrumentationKey: containerAppsEnvironment.outputs.applicationInsightsInstrumentationKey - applicationInsightsConnectionString: containerAppsEnvironment.outputs.applicationInsightsConnectionString - managedIdentityId: managedidentity.outputs.managedIdentityId - managedIdentityClientId: managedidentity.outputs.managedIdentityClientId - - 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 - tags : tags + enableTelemetry: enableTelemetry + environmentResourceId: containerAppsEnvironment.outputs.resourceId + managedIdentities: { systemAssigned: true } + ingressTargetPort: 8000 + ingressExternal: true + activeRevisionsMode: 'Single' + corsPolicy: { + allowedOrigins: [ + 'https://${webSiteName}.azurewebsites.net' + 'http://${webSiteName}.azurewebsites.net' + ] + } + // WAF aligned configuration for Scalability + scaleSettings: { + maxReplicas: enableScalability ? 3 : 1 + minReplicas: enableScalability ? 2 : 1 + rules: [ + { + name: 'http-scaler' + http: { + metadata: { + concurrentRequests: '100' + } + } + } + ] + } + containers: [ + { + name: 'backend' + image: '${containerRegistryHostname}/${containerImageName}:${containerImageTag}' + resources: { + cpu: '2.0' + memory: '4.0Gi' + } + env: [ + { + name: '{ENVIRONMENT_VARIABLE_NAME}' + value: '{ENVIRONMENT_VARIABLE_VALUE}' + } + ] + } + ] } } @@ -234,11 +764,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 FQDN of the frontend web app service.') +// output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN -@description('Service Access Key 1.') -output kmServiceAccessKey1 string = containerAppService.outputs.kmServiceAccessKey1 +// @description('Service Access Key 1.') +// output kmServiceAccessKey1 string = containerAppService.outputs.kmServiceAccessKey1 -@description('Service Access Key 2.') -output kmServiceAccessKey2 string = containerAppService.outputs.kmServiceAccessKey2 +// @description('Service Access Key 2.') +// output kmServiceAccessKey2 string = containerAppService.outputs.kmServiceAccessKey2 diff --git a/App/kernel-memory/infra/modules/network.bicep b/App/kernel-memory/infra/modules/network.bicep new file mode 100644 index 00000000..55a161fb --- /dev/null +++ b/App/kernel-memory/infra/modules/network.bicep @@ -0,0 +1,251 @@ +@description('Required. Named used for all resource naming.') +param resourcesName string + +@description('Required. Resource ID of the Log Analytics Workspace for monitoring and diagnostics.') +param logAnalyticsWorkSpaceResourceId string + +@minLength(3) +@description('Required. Azure region for all services.') +param location string + +@description('Optional. Tags to be applied to the resources.') +param tags object = {} + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Required. Admin username for the VM.') +@secure() +param vmAdminUsername string + +@description('Required. Admin password for the VM.') +@secure() +param vmAdminPassword string + +@description('Required. VM size for the Jumpbox VM.') +param vmSize string + + +// VM Size Notes: +// 1 B-series VMs (like Standard_B2ms) do not support accelerated networking. +// 2 Pick a VM size that does support accelerated networking (the usual jump-box candidates): +// Standard_DS2_v2 (2 vCPU, 7 GiB RAM, Premium SSD) // The most broadly available (it’s a legacy SKU supported in virtually every region). +// Standard_D2s_v3 (2 vCPU, 8 GiB RAM, Premium SSD) // next most common +// Standard_D2s_v4 (2 vCPU, 8 GiB RAM, Premium SSD) // Newest, so fewer regions availabl + + +// Subnet Classless Inter-Doman Routing (CIDR) Sizing Reference Table (Best Practices) +// | CIDR | # of Addresses | # of /24s | Notes | +// |-----------|---------------|-----------|----------------------------------------| +// | /24 | 256 | 1 | Smallest recommended for Azure subnets | +// | /23 | 512 | 2 | Good for 1-2 workloads per subnet | +// | /22 | 1024 | 4 | Good for 2-4 workloads per subnet | +// | /21 | 2048 | 8 | | +// | /20 | 4096 | 16 | Used for default VNet in this solution | +// | /19 | 8192 | 32 | | +// | /18 | 16384 | 64 | | +// | /17 | 32768 | 128 | | +// | /16 | 65536 | 256 | | +// | /15 | 131072 | 512 | | +// | /14 | 262144 | 1024 | | +// | /13 | 524288 | 2048 | | +// | /12 | 1048576 | 4096 | | +// | /11 | 2097152 | 8192 | | +// | /10 | 4194304 | 16384 | | +// | /9 | 8388608 | 32768 | | +// | /8 | 16777216 | 65536 | | +// +// Best Practice Notes: +// - Use /24 as the minimum subnet size for Azure (smaller subnets are not supported for most services). +// - Plan for future growth: allocate larger address spaces (e.g., /20 or /21 for VNets) to allow for new subnets. +// - Avoid overlapping address spaces with on-premises or other VNets. +// - Use contiguous, non-overlapping ranges for subnets. +// - Document subnet usage and purpose in code comments. +// - For AVM modules, ensure only one delegation per subnet and leave delegations empty if not required. + +module network 'network/main.bicep' = { + name: take('network-${resourcesName}-create', 64) + params: { + resourcesName: resourcesName + location: location + logAnalyticsWorkSpaceResourceId: logAnalyticsWorkSpaceResourceId + tags: tags + addressPrefixes: ['10.0.0.0/20'] // 4096 addresses (enough for 8 /23 subnets or 16 /24) + subnets: [ + // Only one delegation per subnet is supported by the AVM module as of June 2025. + // For subnets that do not require delegation, leave the value empty. + { + name: 'web' + addressPrefixes: ['10.0.0.0/23'] // /23 (10.0.0.0 - 10.0.1.255), 512 addresses + networkSecurityGroup: { + name: 'nsg-web' + securityRules: [ + { + name: 'AllowHttpsInbound' + properties: { + access: 'Allow' + direction: 'Inbound' + priority: 100 + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefixes: ['0.0.0.0/0'] + destinationAddressPrefixes: ['10.0.0.0/23'] + } + } + { + name: 'AllowIntraSubnetTraffic' + properties: { + access: 'Allow' + direction: 'Inbound' + priority: 200 + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefixes: ['10.0.0.0/23'] // From same subnet + destinationAddressPrefixes: ['10.0.0.0/23'] // To same subnet + } + } + { + name: 'AllowAzureLoadBalancer' + properties: { + access: 'Allow' + direction: 'Inbound' + priority: 300 + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: 'AzureLoadBalancer' + destinationAddressPrefix: '10.0.0.0/23' + } + } + ] + } + delegation: 'Microsoft.App/environments' + } + { + name: 'peps' + addressPrefixes: ['10.0.2.0/23'] // /23 (10.0.2.0 - 10.0.3.255), 512 addresses + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Disabled' + networkSecurityGroup: { + name: 'nsg-peps' + securityRules: [] + } + } + ] + bastionConfiguration: { + name: 'bas-${resourcesName}' + subnet: { + name: 'AzureBastionSubnet' + addressPrefixes: ['10.0.10.0/26'] + networkSecurityGroup: { + name: 'nsg-AzureBastionSubnet' + securityRules: [ + { + name: 'AllowGatewayManager' + properties: { + access: 'Allow' + direction: 'Inbound' + priority: 2702 + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'GatewayManager' + destinationAddressPrefix: '*' + } + } + { + name: 'AllowHttpsInBound' + properties: { + access: 'Allow' + direction: 'Inbound' + priority: 2703 + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'Internet' + destinationAddressPrefix: '*' + } + } + { + name: 'AllowSshRdpOutbound' + properties: { + access: 'Allow' + direction: 'Outbound' + priority: 100 + protocol: '*' + sourcePortRange: '*' + destinationPortRanges: ['22', '3389'] + sourceAddressPrefix: '*' + destinationAddressPrefix: 'VirtualNetwork' + } + } + { + name: 'AllowAzureCloudOutbound' + properties: { + access: 'Allow' + direction: 'Outbound' + priority: 110 + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: '*' + destinationAddressPrefix: 'AzureCloud' + } + } + ] + } + } + } + jumpboxConfiguration: { + name: 'vm-jumpbox-${resourcesName}' + size: vmSize + username: vmAdminUsername + password: vmAdminPassword + subnet: { + name: 'jumpbox' + addressPrefixes: ['10.0.12.0/23'] // /23 (10.0.12.0 - 10.0.13.255), 512 addresses + networkSecurityGroup: { + name: 'nsg-jumbox' + securityRules: [ + { + name: 'AllowRdpFromBastion' + properties: { + access: 'Allow' + direction: 'Inbound' + priority: 100 + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '3389' + sourceAddressPrefixes: [ + '10.0.10.0/26' // Azure Bastion subnet + ] + destinationAddressPrefixes: ['10.0.12.0/23'] + } + } + ] + } + } + } + enableTelemetry: enableTelemetry + } +} + +@description('Name of the Virtual Network resource.') +output vnetName string = network.outputs.vnetName + +@description('Resource ID of the Virtual Network.') +output vnetResourceId string = network.outputs.vnetResourceId + +@description('Resource ID of the "web" subnet.') +output subnetWebResourceId string = first(filter(network.outputs.subnets, s => s.name == 'web')).?resourceId ?? '' + +@description('Resource ID of the "peps" subnet for Private Endpoints.') +output subnetPrivateEndpointsResourceId string = first(filter(network.outputs.subnets, s => s.name == 'peps')).?resourceId ?? '' + +@description('Resource ID of the Bastion Host.') +output bastionResourceId string = network.outputs.bastionHostId + +@description('Resource ID of the Jumpbox VM.') +output jumpboxResourceId string = network.outputs.jumpboxResourceId diff --git a/App/kernel-memory/infra/modules/network/bastionHost.bicep b/App/kernel-memory/infra/modules/network/bastionHost.bicep new file mode 100644 index 00000000..cc1987e5 --- /dev/null +++ b/App/kernel-memory/infra/modules/network/bastionHost.bicep @@ -0,0 +1,104 @@ +// /****************************************************************************************************************************/ +// Create Azure Bastion Subnet and Azure Bastion Host +// /****************************************************************************************************************************/ + +@description('Name of the Azure Bastion Host resource.') +param name string + +@description('Azure region to deploy resources.') +param location string = resourceGroup().location + +@description('Resource ID of the Virtual Network where the Azure Bastion Host will be deployed.') +param vnetId string + +@description('Name of the Virtual Network where the Azure Bastion Host will be deployed.') +param vnetName string + +@description('Resource ID of the Log Analytics Workspace for monitoring and diagnostics.') +param logAnalyticsWorkspaceId string + +@description('Optional. Tags to apply to the resources.') +param tags object = {} + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +import { subnetType } from 'virtualNetwork.bicep' +@description('Optional. Subnet configuration for the Jumpbox VM.') +param subnet subnetType? + +// 1. Create AzureBastionSubnet NSG +// using AVM Network Security Group module +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/network-security-group +module nsg 'br/public:avm/res/network/network-security-group:0.5.1' = if (!empty(subnet)) { + name: '${vnetName}-${subnet.?networkSecurityGroup.name}' + params: { + name: '${subnet.?networkSecurityGroup.name}-${vnetName}' + location: location + securityRules: subnet.?networkSecurityGroup.securityRules + tags: tags + enableTelemetry: enableTelemetry + } +} + +// 2. Create Azure Bastion Host using AVM Subnet Module with special config for Azure Bastion Subnet +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/virtual-network/subnet +module bastionSubnet 'br/public:avm/res/network/virtual-network/subnet:0.1.2' = if (!empty(subnet)) { + name: take('bastionSubnet-${vnetName}', 64) + params: { + virtualNetworkName: vnetName + name: 'AzureBastionSubnet' // this name required as is for Azure Bastion Host subnet + addressPrefixes: subnet.?addressPrefixes + networkSecurityGroupResourceId: nsg.outputs.resourceId + enableTelemetry: enableTelemetry + } +} + +// 3. Create Azure Bastion Host in AzureBastionsubnetSubnet using AVM Bastion Host module +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/bastion-host + +module bastionHost 'br/public:avm/res/network/bastion-host:0.6.1' = { + name: take('bastionHost-${vnetName}-${name}', 64) + params: { + name: name + skuName: 'Standard' + location: location + virtualNetworkResourceId: vnetId + diagnosticSettings: [ + { + name: 'bastionDiagnostics' + workspaceResourceId: logAnalyticsWorkspaceId + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + enabled: true + } + ] + } + ] + tags: tags + enableTelemetry: enableTelemetry + publicIPAddressObject: { + name: 'pip-${name}' + zones: [] + } + } + dependsOn: [ + bastionSubnet + ] +} + +output resourceId string = bastionHost.outputs.resourceId +output name string = bastionHost.outputs.name +output subnetId string = bastionSubnet.outputs.resourceId +output subnetName string = bastionSubnet.outputs.name + +@export() +@description('Custom type definition for establishing Bastion Host for remote connection.') +type bastionHostConfigurationType = { + @description('The name of the Bastion Host resource.') + name: string + + @description('Optional. Subnet configuration for the Jumpbox VM.') + subnet: subnetType? +} diff --git a/App/kernel-memory/infra/modules/network/jumpbox.bicep b/App/kernel-memory/infra/modules/network/jumpbox.bicep new file mode 100644 index 00000000..9cdd1d7e --- /dev/null +++ b/App/kernel-memory/infra/modules/network/jumpbox.bicep @@ -0,0 +1,155 @@ +// /****************************************************************************************************************************/ +// Create Jumpbox NSG and Jumpbox Subnet, then create Jumpbox VM +// /****************************************************************************************************************************/ + +@description('Name of the Jumpbox Virtual Machine.') +param name string + +@description('Azure region to deploy resources.') +param location string = resourceGroup().location + +@description('Name of the Virtual Network where the Jumpbox VM will be deployed.') +param vnetName string + +@description('Size of the Jumpbox Virtual Machine.') +param size string + +import { subnetType } from 'virtualNetwork.bicep' +@description('Optional. Subnet configuration for the Jumpbox VM.') +param subnet subnetType? + +@description('Username to access the Jumpbox VM.') +param username string + +@secure() +@description('Password to access the Jumpbox VM.') +param password string + +@description('Optional. Tags to apply to the resources.') +param tags object = {} + +@description('Log Analytics Workspace Resource ID for VM diagnostics.') +param logAnalyticsWorkspaceId string + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +// 1. Create Jumpbox NSG +// using AVM Network Security Group module +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/network-security-group +module nsg 'br/public:avm/res/network/network-security-group:0.5.1' = if (!empty(subnet)) { + name: '${vnetName}-${subnet.?networkSecurityGroup.name}' + params: { + name: '${subnet.?networkSecurityGroup.name}-${vnetName}' + location: location + securityRules: subnet.?networkSecurityGroup.securityRules + tags: tags + enableTelemetry: enableTelemetry + } +} + +// 2. Create Jumpbox subnet as part of the existing VNet +// using AVM Virtual Network Subnet module +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/virtual-network/subnet +module subnetResource 'br/public:avm/res/network/virtual-network/subnet:0.1.2' = if (!empty(subnet)) { + name: subnet.?name ?? '${vnetName}-jumpbox-subnet' + params: { + virtualNetworkName: vnetName + name: subnet.?name ?? '' + addressPrefixes: subnet.?addressPrefixes + networkSecurityGroupResourceId: nsg!.outputs.resourceId + enableTelemetry: enableTelemetry + } +} + +// 3. Create Jumpbox VM +// using AVM Virtual Machine module +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/compute/virtual-machine +var vmName = take(name, 15) // Shorten VM name to 15 characters to avoid Azure limits + +module vm 'br/public:avm/res/compute/virtual-machine:0.15.0' = { + name: take('${vmName}-jumpbox', 64) + params: { + name: vmName + vmSize: size + location: location + adminUsername: username + adminPassword: password + tags: tags + zone: 0 + imageReference: { + offer: 'WindowsServer' + publisher: 'MicrosoftWindowsServer' + sku: '2019-datacenter' + version: 'latest' + } + osType: 'Windows' + osDisk: { + name: 'osdisk-${vmName}' + managedDisk: { + storageAccountType: 'Standard_LRS' + } + } + encryptionAtHost: false // Some Azure subscriptions do not support encryption at host + nicConfigurations: [ + { + name: 'nic-${vmName}' + ipConfigurations: [ + { + name: 'ipconfig1' + subnetResourceId: subnetResource!.outputs.resourceId + } + ] + networkSecurityGroupResourceId: nsg!.outputs.resourceId + diagnosticSettings: [ + { + name: 'jumpboxDiagnostics' + workspaceResourceId: logAnalyticsWorkspaceId + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + enabled: true + } + ] + metricCategories: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } + ] + } + ] + enableTelemetry: enableTelemetry + } +} + +output resourceId string = vm.outputs.resourceId +output name string = vm.outputs.name +output location string = vm.outputs.location + +output subnetId string = subnetResource.outputs.resourceId +output subnetName string = subnetResource.outputs.name +output nsgId string = nsg.outputs.resourceId +output nsgName string = nsg.outputs.name + +@export() +@description('Custom type definition for establishing Jumpbox Virtual Machine and its associated resources.') +type jumpBoxConfigurationType = { + @description('The name of the Virtual Machine.') + name: string + + @description('The size of the VM.') + size: string? + + @description('Username to access VM.') + username: string + + @secure() + @description('Password to access VM.') + password: string + + @description('Optional. Subnet configuration for the Jumpbox VM.') + subnet: subnetType? +} diff --git a/App/kernel-memory/infra/modules/network/main.bicep b/App/kernel-memory/infra/modules/network/main.bicep new file mode 100644 index 00000000..3d54481e --- /dev/null +++ b/App/kernel-memory/infra/modules/network/main.bicep @@ -0,0 +1,103 @@ +@minLength(6) +@maxLength(25) +@description('Name used for naming all network resources.') +param resourcesName string + +@minLength(3) +@description('Azure region for all services.') +param location string + +@description('Resource ID of the Log Analytics Workspace for monitoring and diagnostics.') +param logAnalyticsWorkSpaceResourceId string + +@description('Networking address prefix for the VNET.') +param addressPrefixes array + +import { subnetType } from 'virtualNetwork.bicep' +@description('Array of subnets to be created within the VNET.') +param subnets subnetType[] + +import { jumpBoxConfigurationType } from 'jumpbox.bicep' +@description('Optional. Configuration for the Jumpbox VM. Leave null to omit Jumpbox creation.') +param jumpboxConfiguration jumpBoxConfigurationType? + +import { bastionHostConfigurationType } from 'bastionHost.bicep' +@description('Optional. Configuration for the Azure Bastion Host. Leave null to omit Bastion creation.') +param bastionConfiguration bastionHostConfigurationType? + +@description('Optional. Tags to be applied to the resources.') +param tags object = {} + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +// /****************************************************************************************************************************/ +// Networking - NSGs, VNET and Subnets. Each subnet has its own NSG +// /****************************************************************************************************************************/ + +module virtualNetwork 'virtualNetwork.bicep' = { + name: '${resourcesName}-virtualNetwork' + params: { + name: 'vnet-${resourcesName}' + addressPrefixes: addressPrefixes + subnets: subnets + location: location + tags: tags + logAnalyticsWorkspaceId: logAnalyticsWorkSpaceResourceId + enableTelemetry: enableTelemetry + } +} + +// /****************************************************************************************************************************/ +// // Create Azure Bastion Subnet and Azure Bastion Host +// /****************************************************************************************************************************/ + +module bastionHost 'bastionHost.bicep' = if (!empty(bastionConfiguration)) { + name: '${resourcesName}-bastionHost' + params: { + name: bastionConfiguration.?name ?? 'bas-${resourcesName}' + vnetId: virtualNetwork.outputs.resourceId + vnetName: virtualNetwork.outputs.name + location: location + logAnalyticsWorkspaceId: logAnalyticsWorkSpaceResourceId + subnet: bastionConfiguration.?subnet + tags: tags + enableTelemetry: enableTelemetry + } +} + +// /****************************************************************************************************************************/ +// // create Jumpbox NSG and Jumpbox Subnet, then create Jumpbox VM +// /****************************************************************************************************************************/ + +module jumpbox 'jumpbox.bicep' = if (!empty(jumpboxConfiguration)) { + name: '${resourcesName}-jumpbox' + params: { + name: jumpboxConfiguration.?name ?? 'vm-jumpbox-${resourcesName}' + vnetName: virtualNetwork.outputs.name + size: jumpboxConfiguration.?size ?? 'Standard_D2s_v3' + logAnalyticsWorkspaceId: logAnalyticsWorkSpaceResourceId + location: location + subnet: jumpboxConfiguration.?subnet + username: jumpboxConfiguration.?username ?? '' // required + password: jumpboxConfiguration.?password ?? '' // required + enableTelemetry: enableTelemetry + tags: tags + } +} + +output vnetName string = virtualNetwork.outputs.name +output vnetResourceId string = virtualNetwork.outputs.resourceId + +import { subnetOutputType } from 'virtualNetwork.bicep' +output subnets subnetOutputType[] = virtualNetwork.outputs.subnets // This one holds critical info for subnets, including NSGs + +output bastionSubnetId string = bastionHost.outputs.subnetId +output bastionSubnetName string = bastionHost.outputs.subnetName +output bastionHostId string = bastionHost.outputs.resourceId +output bastionHostName string = bastionHost.outputs.name + +output jumpboxSubnetName string = jumpbox.outputs.subnetName +output jumpboxSubnetId string = jumpbox.outputs.subnetId +output jumpboxName string = jumpbox.outputs.name +output jumpboxResourceId string = jumpbox.outputs.resourceId diff --git a/App/kernel-memory/infra/modules/network/virtualNetwork.bicep b/App/kernel-memory/infra/modules/network/virtualNetwork.bicep new file mode 100644 index 00000000..2400dfe1 --- /dev/null +++ b/App/kernel-memory/infra/modules/network/virtualNetwork.bicep @@ -0,0 +1,157 @@ +/****************************************************************************************************************************/ +// Networking - NSGs, VNET and Subnets. Each subnet has its own NSG +/****************************************************************************************************************************/ +@description('Name of the virtual network.') +param name string + +@description('Azure region to deploy resources.') +param location string = resourceGroup().location + +@description('Required. An Array of 1 or more IP Address Prefixes OR the resource ID of the IPAM pool to be used for the Virtual Network. When specifying an IPAM pool resource ID you must also set a value for the parameter called `ipamPoolNumberOfIpAddresses`.') +param addressPrefixes array + +@description('An array of subnets to be created within the virtual network. Each subnet can have its own configuration and associated Network Security Group (NSG).') +param subnets subnetType[] + +@description('Optional. Tags to be applied to the resources.') +param tags object = {} + +@description('Optional. The resource ID of the Log Analytics Workspace to send diagnostic logs to.') +param logAnalyticsWorkspaceId string + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +// 1. Create NSGs for subnets +// using AVM Network Security Group module +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/network-security-group + +@batchSize(1) +module nsgs 'br/public:avm/res/network/network-security-group:0.5.1' = [ + for (subnet, i) in subnets: if (!empty(subnet.?networkSecurityGroup)) { + name: take('${name}-${subnet.?networkSecurityGroup.name}-networksecuritygroup', 64) + params: { + name: '${subnet.?networkSecurityGroup.name}-${name}' + location: location + securityRules: subnet.?networkSecurityGroup.securityRules + tags: tags + enableTelemetry: enableTelemetry + } + } +] + +// 2. Create VNet and subnets, with subnets associated with corresponding NSGs +// using AVM Virtual Network module +// https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/virtual-network + +module virtualNetwork 'br/public:avm/res/network/virtual-network:0.7.0' = { + name: take('${name}-virtualNetwork', 64) + params: { + name: name + location: location + addressPrefixes: addressPrefixes + subnets: [ + for (subnet, i) in subnets: { + name: subnet.name + addressPrefixes: subnet.?addressPrefixes + networkSecurityGroupResourceId: !empty(subnet.?networkSecurityGroup) ? nsgs[i]!.outputs.resourceId : null + privateEndpointNetworkPolicies: subnet.?privateEndpointNetworkPolicies + privateLinkServiceNetworkPolicies: subnet.?privateLinkServiceNetworkPolicies + delegation: subnet.?delegation + } + ] + diagnosticSettings: [ + { + name: 'vnetDiagnostics' + workspaceResourceId: logAnalyticsWorkspaceId + logCategoriesAndGroups: [ + { + categoryGroup: 'allLogs' + enabled: true + } + ] + metricCategories: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } + ] + tags: tags + enableTelemetry: enableTelemetry + } +} + +output name string = virtualNetwork.outputs.name +output resourceId string = virtualNetwork.outputs.resourceId + +// combined output array that holds subnet details along with NSG information +output subnets subnetOutputType[] = [ + for (subnet, i) in subnets: { + name: subnet.name + resourceId: virtualNetwork.outputs.subnetResourceIds[i] + nsgName: !empty(subnet.?networkSecurityGroup) ? subnet.?networkSecurityGroup.name : null + nsgResourceId: !empty(subnet.?networkSecurityGroup) ? nsgs[i].outputs.resourceId : null + } +] + +@export() +@description('Custom type definition for subnet resource information as output') +type subnetOutputType = { + @description('The name of the subnet.') + name: string + + @description('The resource ID of the subnet.') + resourceId: string + + @description('The name of the associated network security group, if any.') + nsgName: string? + + @description('The resource ID of the associated network security group, if any.') + nsgResourceId: string? +} + +@export() +@description('Custom type definition for subnet configuration') +type subnetType = { + @description('Required. The Name of the subnet resource.') + name: string + + @description('Required. Prefixes for the subnet.') // Required to ensure at least one prefix is provided + addressPrefixes: string[] + + @description('Optional. The delegation to enable on the subnet.') + delegation: string? + + @description('Optional. enable or disable apply network policies on private endpoint in the subnet.') + privateEndpointNetworkPolicies: ('Disabled' | 'Enabled' | 'NetworkSecurityGroupEnabled' | 'RouteTableEnabled')? + + @description('Optional. Enable or disable apply network policies on private link service in the subnet.') + privateLinkServiceNetworkPolicies: ('Disabled' | 'Enabled')? + + @description('Optional. Network Security Group configuration for the subnet.') + networkSecurityGroup: networkSecurityGroupType? + + @description('Optional. The resource ID of the route table to assign to the subnet.') + routeTableResourceId: string? + + @description('Optional. An array of service endpoint policies.') + serviceEndpointPolicies: object[]? + + @description('Optional. The service endpoints to enable on the subnet.') + serviceEndpoints: string[]? + + @description('Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet.') + defaultOutboundAccess: bool? +} + +@export() +@description('Custom type definition for network security group configuration') +type networkSecurityGroupType = { + @description('Required. The name of the network security group.') + name: string + + @description('Required. The security rules for the network security group.') + securityRules: object[] +} From c9eb2396af24c483183be17da71d445fa1b5e238 Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Tue, 9 Sep 2025 12:30:45 +0530 Subject: [PATCH 03/29] bicep changes --- .../infra/modules/ai-search.bicep | 100 -------- .../modules/cognitive-services-docIntel.bicep | 70 ----- .../modules/cognitive-services-openAI.bicep | 113 -------- .../infra/modules/container-app.bicep | 242 ------------------ .../modules/container-apps-environment.bicep | 84 ------ .../infra/modules/managed-identity.bicep | 39 --- App/kernel-memory/infra/modules/storage.bicep | 137 ---------- {App/kernel-memory/infra => infra}/README.md | 0 .../infra => infra}/build-main.json.sh | 0 .../images/ACA-ApplicationUrl.png | Bin .../infra => infra}/images/ACA-EnvVar.png | Bin {App/kernel-memory/infra => infra}/main.bicep | 237 ++++++----------- {App/kernel-memory/infra => infra}/main.json | 0 .../infra => infra}/modules/network.bicep | 0 .../modules/network/bastionHost.bicep | 0 .../modules/network/jumpbox.bicep | 0 .../modules/network/main.bicep | 0 .../modules/network/virtualNetwork.bicep | 0 18 files changed, 83 insertions(+), 939 deletions(-) delete mode 100644 App/kernel-memory/infra/modules/ai-search.bicep delete mode 100644 App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep delete mode 100644 App/kernel-memory/infra/modules/cognitive-services-openAI.bicep delete mode 100644 App/kernel-memory/infra/modules/container-app.bicep delete mode 100644 App/kernel-memory/infra/modules/container-apps-environment.bicep delete mode 100644 App/kernel-memory/infra/modules/managed-identity.bicep delete mode 100644 App/kernel-memory/infra/modules/storage.bicep rename {App/kernel-memory/infra => infra}/README.md (100%) rename {App/kernel-memory/infra => infra}/build-main.json.sh (100%) rename {App/kernel-memory/infra => infra}/images/ACA-ApplicationUrl.png (100%) rename {App/kernel-memory/infra => infra}/images/ACA-EnvVar.png (100%) rename {App/kernel-memory/infra => infra}/main.bicep (79%) rename {App/kernel-memory/infra => infra}/main.json (100%) rename {App/kernel-memory/infra => infra}/modules/network.bicep (100%) rename {App/kernel-memory/infra => infra}/modules/network/bastionHost.bicep (100%) rename {App/kernel-memory/infra => infra}/modules/network/jumpbox.bicep (100%) rename {App/kernel-memory/infra => infra}/modules/network/main.bicep (100%) rename {App/kernel-memory/infra => infra}/modules/network/virtualNetwork.bicep (100%) diff --git a/App/kernel-memory/infra/modules/ai-search.bicep b/App/kernel-memory/infra/modules/ai-search.bicep deleted file mode 100644 index 19330182..00000000 --- a/App/kernel-memory/infra/modules/ai-search.bicep +++ /dev/null @@ -1,100 +0,0 @@ -@description('Required. Contains ManagedIdentity Principal ID.') -param managedIdentityPrincipalId string - -@description('Required. Suffix to create unique resource names; 4-15 characters.') -@minLength(4) -@maxLength(15) -param suffix string = uniqueString(resourceGroup().id) - -@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 - -@allowed([ - 'free' - 'basic' - 'standard' - 'standard2' - 'standard3' - 'storage_optimized_l1' - 'storage_optimized_l2' -]) -@description('The pricing tier of the search service you want to create (for example, basic or standard).') -param sku string = 'standard' - -@description('Replicas distribute search workloads across the service. You need at least two replicas to support high availability of query workloads (not applicable to the free tier).') -@minValue(1) -@maxValue(12) -param replicaCount int = 1 - -@description('Partitions allow for scaling of document count as well as faster indexing by sharding your index over multiple search units.') -@allowed([ - 1 - 2 - 3 - 4 - 6 - 12 -]) -param partitionCount int = 1 - -@description('Applicable only for SKUs set to standard3. You can set this property to enable a single, high density partition that allows up to 1000 indexes, which is much higher than the maximum indexes allowed for any other SKU.') -@allowed([ - 'default' - 'highDensity' -]) -param hostingMode string = 'default' - -@description('Location for all resources.') -param location string = resourceGroup().location - -@description('Optional. Tags to be applied to the resources.') -param tags object = {} - -resource search 'Microsoft.Search/searchServices@2023-11-01' = { - name: name - location: location - sku: { - name: sku - } - properties: { - replicaCount: replicaCount - partitionCount: partitionCount - hostingMode: hostingMode - authOptions: { - aadOrApiKey: {} - } - } -} - -// Search Index Data Contributor -resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Search Index Data Contributor-${suffix}') - scope: search - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - '8ebe5a00-799e-43f5-93ac-243d3dce84a7' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -// Search Service Contributor -resource roleAssignment2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Search Service Contributor-${suffix}') - scope: search - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - '7ca78c08-252a-4471-8644-bb5ff32d4ba0' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -@description('Contains ManagedIdentity Principal ID.') -output searchName string = search.name diff --git a/App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep b/App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep deleted file mode 100644 index 63d73b86..00000000 --- a/App/kernel-memory/infra/modules/cognitive-services-docIntel.bicep +++ /dev/null @@ -1,70 +0,0 @@ -@description('Required. Suffix to create unique resource names; 4-15 characters.') -@minLength(4) -@maxLength(15) -param suffix string = uniqueString(resourceGroup().id) - -@description('Required. Contains ManagedIdentity Principal ID.') -param managedIdentityPrincipalId string - -metadata description = 'Creates an Azure Document Intelligence (form recognizer) instance.' - -@description('Required. Contains Name.') -param name string - -@description('Required. Contains Resource Group Location.') -param location string = resourceGroup().location - -@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' - -@allowed(['Enabled', 'Disabled']) -param publicNetworkAccess string = 'Enabled' -param sku object = { - name: 'S0' -} - -@description('Required. Contains IP Rules.') -param allowedIpRules array = [] -param networkAcls object = empty(allowedIpRules) - ? { - defaultAction: 'Allow' - } - : { - ipRules: allowedIpRules - defaultAction: 'Deny' - } - -@description('Optional. Tags to be applied to the resources.') -param tags object = {} - - -resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { - name: name - location: location - kind: kind - properties: { - customSubDomainName: customSubDomainName - publicNetworkAccess: publicNetworkAccess - networkAcls: networkAcls - disableLocalAuth: true - } - sku: sku -} - -// Cognitive Services User -resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Cognitive Services User-${suffix}') - scope: account - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - 'a97b65f3-24c7-4388-baec-2e87135dc908' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -@description('Contains Endpoint.') -output endpoint string = account.properties.endpoint diff --git a/App/kernel-memory/infra/modules/cognitive-services-openAI.bicep b/App/kernel-memory/infra/modules/cognitive-services-openAI.bicep deleted file mode 100644 index 22f8455f..00000000 --- a/App/kernel-memory/infra/modules/cognitive-services-openAI.bicep +++ /dev/null @@ -1,113 +0,0 @@ -@description('Required. Suffix to create unique resource names; 4-15 characters.') -@minLength(4) -@maxLength(15) -param suffix string = uniqueString(resourceGroup().id) - -@description('Required. Contains ManagedIdentity Principal ID.') -param managedIdentityPrincipalId string - -metadata description = 'Creates an Azure Cognitive Services instance.' - -@description('Required. Contains Name.') -param name string - -@description('Required. Contains Resource Group Location.') -param location string = resourceGroup().location - -@description('Required. Contains Tags.') -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 - -@description('Required. Contains Deployments.') -param deployments array = [] - -@description('Optional. Contains the type.') -param kind string = 'OpenAI' - -@allowed(['Enabled', 'Disabled']) -param publicNetworkAccess string = 'Enabled' -param sku object = { - name: 'S0' -} - -@description('Required. Contains the IP rules which are allowed.') -param allowedIpRules array = [] -param networkAcls object = empty(allowedIpRules) - ? { - defaultAction: 'Allow' - } - : { - ipRules: allowedIpRules - defaultAction: 'Deny' - } - -resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { - name: name - location: location - tags: tags - kind: kind - properties: { - customSubDomainName: customSubDomainName - publicNetworkAccess: publicNetworkAccess - networkAcls: networkAcls - disableLocalAuth: true - } - sku: sku -} - -@batchSize(1) -resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [ - for deployment in deployments: { - parent: account - name: deployment.name - properties: { - model: deployment.model - raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null - } - sku: contains(deployment, 'sku') - ? deployment.sku - : { - name: 'Standard' - capacity: 1 - } - } -] - -// Cognitive Services OpenAI Contributor -resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Cognitive Services OpenAI Contributor-${suffix}') - scope: account - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - 'a001fd3d-188f-4b5d-821b-7da978bf7442' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -// Cognitive Services OpenAI User -resource roleAssignment2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Cognitive Services OpenAI User-${suffix}') - scope: account - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -@description('Contains the Endpoint.') -output endpoint string = account.properties.endpoint - -@description('Contains the ID.') -output id string = account.id - -@description('Contains Account Name.') -output name string = account.name diff --git a/App/kernel-memory/infra/modules/container-app.bicep b/App/kernel-memory/infra/modules/container-app.bicep deleted file mode 100644 index 0aeff12a..00000000 --- a/App/kernel-memory/infra/modules/container-app.bicep +++ /dev/null @@ -1,242 +0,0 @@ -targetScope = 'resourceGroup' - -@description('Required. Suffix to create unique resource names; 4-15 characters.') -@minLength(4) -@maxLength(15) -param suffix string = uniqueString(resourceGroup().id) - -@description('Required. Contains Resource Group Location.') -param location string = resourceGroup().location - -@description('Required. Contains Managed Identity ID.') -param managedIdentityId string - -@description('Required. Contains Managed Identity Client ID.') -param managedIdentityClientId string - -@description('Required. Contains KM Service Name.') -param kmServiceName string = 'km-service-${suffix}' - -@description('Required. Contains Container App Environment ID.') -param containerAppsEnvironmentId string - -@description('Required. Contains AppInsights Instrumentation Key.') -param appInsightsInstrumentationKey string - -@description('Required. Contains App Insights Connection String.') -param applicationInsightsConnectionString string - -@description('Required. Contains Azure Blobs Account.') -param AzureBlobs_Account string - -@description('Required. Contains Azure Queues Account.') -param AzureQueues_Account string - -@description('Required. Contains Azure Queues Queue Name.') -param AzureQueues_QueueName string - -@description('Required. Contains Azure AI Search Endpoint.') -param AzureAISearch_Endpoint string - -@description('Required. Contains Azure OpenAI Text Endpoint.') -param AzureOpenAIText_Endpoint string - -@description('Required. Contains Azure OpenAI Text Deployment.') -param AzureOpenAIText_Deployment string - -@description('Required. Contains Azure OpenAI Embedding Endpoint.') -param AzureOpenAIEmbedding_Endpoint string - -@description('Required. Contains Azure OpenAI Embedding Deployment.') -param AzureOpenAIEmbedding_Deployment string - -@description('Required. Contains Azure AI DocIntel Endpoint.') -param AzureAIDocIntel_Endpoint string - -@description('Required. Contains Kernel Memory AccessKey 1.') -param KernelMemory__ServiceAuthorization__AccessKey1 string - -@description('Required. Contains Kernel Memory AccessKey 2.') -param KernelMemory__ServiceAuthorization__AccessKey2 string - -@description('Optional. Tags to be applied to the resources.') -param tags object = {} - -resource kmService 'Microsoft.App/containerApps@2023-05-01' = { - name: kmServiceName - location: location - properties: { - environmentId: containerAppsEnvironmentId - configuration: { - secrets: [ - { - name: 'appinsights-key' - value: appInsightsInstrumentationKey - } - ] - registries: [] - activeRevisionsMode: 'Single' - ingress: { - external: true - transport: 'Auto' - allowInsecure: false - targetPort: 9001 - stickySessions: { - affinity: 'none' - } - // additionalPortMappings: [] - } - } - - template: { - containers: [ - { - name: 'kernelmemory-service' - image: 'docker.io/kernelmemory/service:latest' - command: [] - resources: { - cpu: json('0.25') - memory: '0.5Gi' - } - env: [ - { - name: 'ASPNETCORE_ENVIRONMENT' - value: 'Production' - } - { - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: applicationInsightsConnectionString - } - - { - name: 'AZURE_CLIENT_ID' - value: managedIdentityClientId - } - { - name: 'KernelMemory__Service__OpenApiEnabled' - value: 'true' - } - { - name: 'KernelMemory__DocumentStorageType' - value: 'AzureBlobs' - } - { - name: 'KernelMemory__TextGeneratorType' - value: 'AzureOpenAIText' - } - { - name: 'KernelMemory__DefaultIndexName' - value: 'default' - } - { - name: 'KernelMemory__ServiceAuthorization__Enabled' - value: 'true' - } - { - name: 'KernelMemory__ServiceAuthorization__AuthenticationType' - value: 'APIKey' - } - { - name: 'KernelMemory__ServiceAuthorization__HttpHeaderName' - value: 'Authorization' - } - { - name: 'KernelMemory__ServiceAuthorization__AccessKey1' - value: KernelMemory__ServiceAuthorization__AccessKey1 - } - { - name: 'KernelMemory__ServiceAuthorization__AccessKey2' - value: KernelMemory__ServiceAuthorization__AccessKey2 - } - { - name: 'KernelMemory__DataIngestion__DistributedOrchestration__QueueType' - value: 'AzureQueues' - } - { - name: 'KernelMemory__DataIngestion__EmbeddingGeneratorTypes__0' - value: 'AzureOpenAIEmbedding' - } - { - name: 'KernelMemory__DataIngestion__MemoryDbTypes__0' - value: 'AzureAISearch' - } - { - name: 'KernelMemory__DataIngestion__ImageOcrType' - value: 'AzureAIDocIntel' - } - { - name: 'KernelMemory__Retrieval__EmbeddingGeneratorType' - value: 'AzureOpenAIEmbedding' - } - { - name: 'KernelMemory__Retrieval__MemoryDbType' - value: 'AzureAISearch' - } - { - name: 'KernelMemory__Services__AzureBlobs__Account' - value: AzureBlobs_Account - } - { - name: 'KernelMemory__Services__AzureQueues__Account' - value: AzureQueues_Account - } - { - name: 'KernelMemory__Services__AzureQueues__QueueName' - value: AzureQueues_QueueName - } - { - name: 'KernelMemory__Services__AzureAISearch__Endpoint' - value: AzureAISearch_Endpoint - } - { - name: 'KernelMemory__Services__AzureOpenAIText__Endpoint' - value: AzureOpenAIText_Endpoint - } - { - name: 'KernelMemory__Services__AzureOpenAIText__Deployment' - value: AzureOpenAIText_Deployment - } - { - name: 'KernelMemory__Services__AzureOpenAIEmbedding__Endpoint' - value: AzureOpenAIEmbedding_Endpoint - } - { - name: 'KernelMemory__Services__AzureOpenAIEmbedding__Deployment' - value: AzureOpenAIEmbedding_Deployment - } - { - name: 'KernelMemory__Services__AzureAIDocIntel__Endpoint' - value: AzureAIDocIntel_Endpoint - } - ] - } - ] - scale: { - minReplicas: 1 - maxReplicas: 1 - } - } - } - tags : tags - identity: { - type: 'UserAssigned' - userAssignedIdentities: { - '${managedIdentityId}': {} - } - } -} - -@description('Contains KM Service Name.') -output kmServiceName string = kmService.name - -@description('Contains KM Service ID.') -output kmServiceId string = kmService.id - -@description('Contains KM Service AccessKey 1.') -output kmServiceAccessKey1 string = KernelMemory__ServiceAuthorization__AccessKey1 - -@description('Contains KM Service AccessKey 2.') -output kmServiceAccessKey2 string = KernelMemory__ServiceAuthorization__AccessKey2 - -@description('The FQDN of the frontend web app service.') -output kmServiceFQDN string = kmService.properties.configuration.ingress.fqdn diff --git a/App/kernel-memory/infra/modules/container-apps-environment.bicep b/App/kernel-memory/infra/modules/container-apps-environment.bicep deleted file mode 100644 index 437338db..00000000 --- a/App/kernel-memory/infra/modules/container-apps-environment.bicep +++ /dev/null @@ -1,84 +0,0 @@ -targetScope = 'resourceGroup' - -@description('Required. Suffix to create unique resource names; 4-15 characters.') -@minLength(4) -@maxLength(15) -param suffix string = uniqueString(resourceGroup().id) - -@description('Required. 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('Required. The name of the container apps environment. If set, it overrides the name generated by the template.') -param containerAppsEnvironmentName string = 'km-cae-${suffix}' - -@description('Required. The name of the log analytics workspace. If set, it overrides the name generated by the template.') -param logAnalyticsWorkspaceName string = 'km-log-${suffix}' - -@description('Required. The name of the application insights. If set, it overrides the name generated by the template.') -param applicationInsightsName string = 'km-appins-${suffix}' - -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { - name: logAnalyticsWorkspaceName - location: location - tags: tags - properties: any({ - features: { - searchVersion: 1 - } - sku: { - name: 'PerGB2018' - } - retentionInDays: 30 - }) -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { - name: applicationInsightsName - location: location - tags: tags - kind: 'web' - properties: { - Application_Type: 'web' - WorkspaceResourceId: logAnalyticsWorkspace.id - } -} - -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 - } - } - } -} - -@description('Contains App Environment Name.') -output containerAppsEnvironmentName string = containerAppsEnvironment.name - -@description('Contains App Environment ID.') -output containerAppsEnvironmentId string = containerAppsEnvironment.id - -@description('Contains LogAnalytics Workspace Name.') -output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name - -@description('Contains App Insights Name.') -output applicationInsightsName string = applicationInsights.name - -@description('Contains App Insights Instrumentation Key.') -output applicationInsightsInstrumentationKey string = applicationInsights.properties.InstrumentationKey - -@description('Contains App Insights Connection String.') -output applicationInsightsConnectionString string = applicationInsights.properties.ConnectionString diff --git a/App/kernel-memory/infra/modules/managed-identity.bicep b/App/kernel-memory/infra/modules/managed-identity.bicep deleted file mode 100644 index 66c9ff6c..00000000 --- a/App/kernel-memory/infra/modules/managed-identity.bicep +++ /dev/null @@ -1,39 +0,0 @@ -@description('Required. Suffix to create unique resource names; 4-15 characters.') -@minLength(4) -@maxLength(15) -param suffix string = uniqueString(resourceGroup().id) - -@description('Managed Identity name.') -@minLength(2) -@maxLength(60) -param name string = 'km-UAidentity-${suffix}' - -@description('Location for all resources.') -param location string = resourceGroup().location - -resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: name - location: location -} - -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('${resourceGroup().id}contributor') - scope: resourceGroup() - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - 'b24988ac-6180-42a0-ab88-20f7382dd24c' - ) - principalId: managedIdentity.properties.principalId - principalType: 'ServicePrincipal' - } -} - -@description('Contains Managed Identity ID.') -output managedIdentityId string = managedIdentity.id - -@description('Contains Managed Identity Principal ID.') -output managedIdentityPrincipalId string = managedIdentity.properties.principalId - -@description('Contains Managed Identity Client ID.') -output managedIdentityClientId string = managedIdentity.properties.clientId diff --git a/App/kernel-memory/infra/modules/storage.bicep b/App/kernel-memory/infra/modules/storage.bicep deleted file mode 100644 index d499ecc7..00000000 --- a/App/kernel-memory/infra/modules/storage.bicep +++ /dev/null @@ -1,137 +0,0 @@ -targetScope = 'resourceGroup' - -@description('Required. Suffix to create unique resource names; 4-15 characters.') -@minLength(4) -@maxLength(15) -param suffix string = uniqueString(resourceGroup().id) - -@description('Required. Contains Resource Group Location.') -param location string = resourceGroup().location - -@description('Optional. 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)}' - -@description('The name of the Container in Azure Storage.') -param storageBlobContainerName string = 'smemory' - -@description('The name of the Queue in Azure Storage.') -param externalTasksQueueName string = 'km-queue-${suffix}' - -@description('Required. Contains ManagedIdentity Principal ID.') -param managedIdentityPrincipalId string - -resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { - name: storageAccountName - tags: tags - location: location - sku: { - name: 'Standard_LRS' - } - kind: 'StorageV2' - properties: { - allowBlobPublicAccess: false - allowSharedKeyAccess: false - } -} - -// Storage Queue Data Contributor -resource roleAssignment1 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Storage Queue Data Contributor-${suffix}') - scope: storageAccount - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - '974c5e8b-45b9-4653-ba55-5f855dd0fb88' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -// Storage Blob Data Contributor -resource roleAssignment2 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Storage Blob Data Contributor-${suffix}') - scope: storageAccount - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -// Storage Blob Data Owner -resource roleAssignment22 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Storage Blob Data Owner-${suffix}') - scope: storageAccount - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -// Storage Queue Data Message Sender -resource roleAssignment3 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('Storage Queue Data Message Sender-${suffix}') - scope: storageAccount - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -// Storage Queue Data Message Processor -resource roleAssignment4 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid('// Storage Queue Data Message Processor-${suffix}') - scope: storageAccount - properties: { - roleDefinitionId: subscriptionResourceId( - 'Microsoft.Authorization/roleDefinitions', - '8a0f0c08-91a1-4084-bc3d-661d67233fed' - ) - principalId: managedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -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 -} - -@description('The storage account name.') -output storageAccountName string = storageAccount.name - -@description('The storage account name.') -output blobContainerName string = blobContainer.name - -@description('The storage account name.') -output queueName string = queue.name diff --git a/App/kernel-memory/infra/README.md b/infra/README.md similarity index 100% rename from App/kernel-memory/infra/README.md rename to infra/README.md diff --git a/App/kernel-memory/infra/build-main.json.sh b/infra/build-main.json.sh similarity index 100% rename from App/kernel-memory/infra/build-main.json.sh rename to infra/build-main.json.sh diff --git a/App/kernel-memory/infra/images/ACA-ApplicationUrl.png b/infra/images/ACA-ApplicationUrl.png similarity index 100% rename from App/kernel-memory/infra/images/ACA-ApplicationUrl.png rename to infra/images/ACA-ApplicationUrl.png diff --git a/App/kernel-memory/infra/images/ACA-EnvVar.png b/infra/images/ACA-EnvVar.png similarity index 100% rename from App/kernel-memory/infra/images/ACA-EnvVar.png rename to infra/images/ACA-EnvVar.png diff --git a/App/kernel-memory/infra/main.bicep b/infra/main.bicep similarity index 79% rename from App/kernel-memory/infra/main.bicep rename to infra/main.bicep index 3c4970bb..e1b12113 100644 --- a/App/kernel-memory/infra/main.bicep +++ b/infra/main.bicep @@ -190,6 +190,7 @@ var dnsZoneIndex = { sqlServer: 13 appService: 14 search: 15 + formRecognizer: 16 } @batchSize(5) module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.7.1' = [ @@ -293,20 +294,6 @@ module network 'modules/network.bicep' = if (enablePrivateNetworking) { } } -/* - Module to create a Managed Identity. - See https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview - - The managed identity is the main code-to-services and service-to-service authentication mechanism. -*/ -// module managedidentity 'modules/managed-identity.bicep' = { -// name: 'managedidentity-${suffix}' -// scope: rg -// params: { -// location: location -// suffix: suffix -// } -// } // ========== AVM WAF ========== // // ========== User Assigned Identity ========== // @@ -322,22 +309,6 @@ module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-id } } -/* - Module to create a Storage Account - See https://learn.microsoft.com/azure/storage/common/storage-account-overview - - 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}' -// scope: rg -// params: { -// location: location -// suffix: suffix -// managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId -// } -// } // ========== AVM WAF ========== // // ========== Storage account module ========== // @@ -415,24 +386,6 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { } } -/* - Module to create a Azure AI Search service - See https://azure.microsoft.com/products/ai-services/ai-search - - 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}' -// scope: rg -// params: { -// location: location -// name: 'km-search-${suffix}' -// suffix: suffix -// managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId -// } -// } - // ========== AI Foundry: AI Search ========== // var aiSearchName = 'srch-${suffix}' // var aiSearchConnectionName = 'myCon-${suffix}' @@ -488,43 +441,18 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = } } -/* - Module to create a Azure OpenAI service - See https://azure.microsoft.com/products/ai-services/openai-service - and https://github.com/Azure-Samples/azure-search-openai-demo/blob/main/infra/main.bicep for more details - - 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}' -// scope: rg -// params: { -// suffix: suffix -// managedIdentityPrincipalId: managedidentity.outputs.managedIdentityPrincipalId -// name: 'km-openai-${suffix}' -// location: location -// sku: { -// name: 'S0' -// } -// deployments: openAiDeployments -// tags : tags -// } -// } // ========== AVM WAF ========== // // ========== Cognitive Services - OpenAI module ========== // var openAiAccountName = 'openai-${suffix}' - module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${openAiAccountName}', 64) params: { name: openAiAccountName location: location kind: 'OpenAI' - sku: { - name: 'S0' - } + sku: 'S0' tags: tags enableTelemetry: enableTelemetry @@ -549,7 +477,7 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { privateDnsZoneGroupConfigs: [ { name: 'openai-dns-zone-group' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAi]!.outputs.resourceId + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId } ] } @@ -576,26 +504,6 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { } } -/* - Module to create a Azure Document Intelligence service - 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}' -// scope: rg -// params: { -// suffix: suffix -// managedIdentityPrincipalId: userAssignedIdentity.outputs.managedIdentityPrincipalId -// name: 'km-docIntel-${suffix}' -// location: location -// sku: { -// name: 'S0' -// } -// tags : tags -// } -// } - // ========== AVM WAF ========== // // ========== Cognitive Services - docIntel module ========== // @@ -610,9 +518,7 @@ module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { location: location kind: 'FormRecognizer' tags: tags - sku: { - name: 'S0' - } + sku: 'S0' managedIdentities: { systemAssigned: true } @@ -655,55 +561,6 @@ module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { } -/* - 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}' - scope: rg - params: { - location: location - suffix: suffix - } -} - -/* - Module to create web app containing the Docker image - See https://azure.microsoft.com/products/container-apps - - The Azure Container app hosts the docker container containing KM web service. -*/ -// module containerAppService 'modules/container-app.bicep' = { -// name: 'containerAppService-${suffix}' -// scope: rg -// params: { -// location: location -// suffix: suffix -// containerAppsEnvironmentId: containerAppsEnvironment.outputs.containerAppsEnvironmentId -// appInsightsInstrumentationKey: containerAppsEnvironment.outputs.applicationInsightsInstrumentationKey -// applicationInsightsConnectionString: containerAppsEnvironment.outputs.applicationInsightsConnectionString -// managedIdentityId: us.outputs.managedIdentityId -// managedIdentityClientId: managedidentity.outputs.managedIdentityClientId - -// 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 -// tags : tags -// } -// } - - // ========== AVM WAF ========== // // ========== Container App module ========== // @@ -715,17 +572,17 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = { tags: tags location: location enableTelemetry: enableTelemetry - environmentResourceId: containerAppsEnvironment.outputs.resourceId + environmentResourceId: containerAppEnvironment.outputs.resourceId managedIdentities: { systemAssigned: true } ingressTargetPort: 8000 ingressExternal: true activeRevisionsMode: 'Single' - corsPolicy: { - allowedOrigins: [ - 'https://${webSiteName}.azurewebsites.net' - 'http://${webSiteName}.azurewebsites.net' - ] - } + // corsPolicy: { + // allowedOrigins: [ + // 'https://${webSiteName}.azurewebsites.net' + // 'http://${webSiteName}.azurewebsites.net' + // ] + // } // WAF aligned configuration for Scalability scaleSettings: { maxReplicas: enableScalability ? 3 : 1 @@ -760,10 +617,82 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = { } } +var containerAppEnvironmentResourceName = 'cae-${suffix}' +module containerAppEnvironment 'br/public:avm/res/app/managed-environment:0.11.2' = { + name: take('avm.res.app.managed-environment.${containerAppEnvironmentResourceName}', 64) + params: { + name: containerAppEnvironmentResourceName + location: location + tags: tags + enableTelemetry: enableTelemetry + publicNetworkAccess: 'Enabled' + internal: false + // WAF aligned configuration for Private Networking + infrastructureSubnetResourceId: enablePrivateNetworking ? network.?outputs.?subnetResourceIds[3] : null + + // WAF aligned configuration for Monitoring + appLogsConfiguration: enableMonitoring + ? { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace!.outputs.logAnalyticsWorkspaceId + sharedKey: logAnalyticsWorkspace!.outputs!.primarySharedKey + } + } + : null + appInsightsConnectionString: enableMonitoring ? applicationInsights!.outputs.connectionString : null + // WAF aligned configuration for Redundancy + zoneRedundant: enableRedundancy ? true : false + infrastructureResourceGroupName: enableRedundancy ? '${resourceGroup().name}-infra' : null + workloadProfiles: enableRedundancy + ? [ + { + maximumCount: 3 + minimumCount: 3 + name: 'CAW01' + workloadProfileType: 'D4' + } + ] + : [ + { + name: 'Consumption' + workloadProfileType: 'Consumption' + } + ] + } +} + +// ========== Application Insights ========== // +var applicationInsightsResourceName = 'appi-${suffix}' +module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (enableMonitoring) { + name: take('avm.res.insights.component.${applicationInsightsResourceName}', 64) + params: { + name: applicationInsightsResourceName + tags: tags + location: location + enableTelemetry: enableTelemetry + retentionInDays: 365 + kind: 'web' + disableIpMasking: false + flowType: 'Bluefield' + // WAF aligned configuration for Monitoring + workspaceResourceId: enableMonitoring ? logAnalyticsWorkspace.outputs.resourceId : '' + diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }] : null + } +} /* Outputs */ +@description('Contains Solution Name.') +output SOLUTION_NAME string = suffix + +@description('Contains Resource Group Name.') +output RESOURCE_GROUP_NAME string = resourceGroup().name + +@description('Contains Resource Group Location.') +output RESOURCE_GROUP_LOCATION string = location + // @description('The FQDN of the frontend web app service.') // output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN diff --git a/App/kernel-memory/infra/main.json b/infra/main.json similarity index 100% rename from App/kernel-memory/infra/main.json rename to infra/main.json diff --git a/App/kernel-memory/infra/modules/network.bicep b/infra/modules/network.bicep similarity index 100% rename from App/kernel-memory/infra/modules/network.bicep rename to infra/modules/network.bicep diff --git a/App/kernel-memory/infra/modules/network/bastionHost.bicep b/infra/modules/network/bastionHost.bicep similarity index 100% rename from App/kernel-memory/infra/modules/network/bastionHost.bicep rename to infra/modules/network/bastionHost.bicep diff --git a/App/kernel-memory/infra/modules/network/jumpbox.bicep b/infra/modules/network/jumpbox.bicep similarity index 100% rename from App/kernel-memory/infra/modules/network/jumpbox.bicep rename to infra/modules/network/jumpbox.bicep diff --git a/App/kernel-memory/infra/modules/network/main.bicep b/infra/modules/network/main.bicep similarity index 100% rename from App/kernel-memory/infra/modules/network/main.bicep rename to infra/modules/network/main.bicep diff --git a/App/kernel-memory/infra/modules/network/virtualNetwork.bicep b/infra/modules/network/virtualNetwork.bicep similarity index 100% rename from App/kernel-memory/infra/modules/network/virtualNetwork.bicep rename to infra/modules/network/virtualNetwork.bicep From c6d9bf74e6ea410abd6e25c2ce2a18f82fdf59ef Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Tue, 9 Sep 2025 16:33:39 +0530 Subject: [PATCH 04/29] feat: Fixed build issues --- azure.yaml | 10 ++ infra/main.bicep | 342 ++++++++++++++++++------------------- infra/main.parameters.json | 12 ++ 3 files changed, 187 insertions(+), 177 deletions(-) create mode 100644 azure.yaml create mode 100644 infra/main.parameters.json diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 00000000..73c9ac79 --- /dev/null +++ b/azure.yaml @@ -0,0 +1,10 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +name: document-knowledge-mining-solution-accelerator + +requiredVersions: + azd: ">= 1.15.0" + +# metadata: +# template: document-knowledge-mining-solution-accelerator@1.0 +# name: document-knowledge-mining-solution-accelerator@1.0 diff --git a/infra/main.bicep b/infra/main.bicep index e1b12113..a5ee7e33 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1,7 +1,27 @@ -@description('Suffix to create unique resource names; 4-6 characters. Default is a random 6 characters.') -@minLength(4) -@maxLength(6) -param suffix string = substring(newGuid(), 0, 6) +// ========== main.bicep ========== // +targetScope = 'resourceGroup' + +@minLength(3) +@maxLength(20) +@description('Required. A unique prefix for all resources in this deployment. This should be 3-20 characters long:') +param solutionName string = 'dkm' + +@description('Optional. Azure location for the solution. If not provided, it defaults to the resource group location.') +param location string = '' + +@maxLength(5) +@description('Optional. A unique token for the solution. This is used to ensure resource names are unique for global resources. Defaults to a 5-character substring of the unique string generated from the subscription ID, resource group name, and solution name.') +param solutionUniqueToken string = substring(uniqueString(subscription().id, resourceGroup().name, solutionName), 0, 5) + +var solutionSuffix= toLower(trim(replace( + replace( + replace(replace(replace(replace('${solutionName}${solutionUniqueToken}', '-', ''), '_', ''), '.', ''), '/', ''), + ' ', + '' + ), + '*', + '' +))) @description(''' gpt-35-turbo-16k deployment model\'s Tokens-Per-Minute (TPM) capacity, measured in thousands. @@ -21,26 +41,6 @@ For model limits specific to your region, refer to the documentation at https:// @maxValue(40) param embeddingDeploymentCapacity int = 30 -@description(''' -PLEASE CHOOSE A SECURE AND SECRET KEY ! - -Kernel Memory Service Authorization AccessKey 1. -The value is stored as an environment variable and is required by the web service to authenticate HTTP requests. -''') -@minLength(32) -@maxLength(128) -@secure() -param WebServiceAuthorizationKey1 string - -@description(''' -PLEASE CHOOSE A SECURE AND SECRET KEY ! - -Kernel Memory Service Authorization AccessKey 2. -The value is stored as an environment variable and is required by the web service to authenticate HTTP requests. -''') -@minLength(32) -@maxLength(128) -@secure() -param WebServiceAuthorizationKey2 string - @description('Optional. The tags to apply to all deployed Azure resources.') param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} @@ -76,6 +76,8 @@ param enableScalability bool = false @description('Optional. Enable purge protection for the Key Vault') param enablePurgeProtection bool = false +var solutionLocation = empty(location) ? resourceGroup().location : location + // @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') // param secretsExportConfiguration secretsExportConfigurationType? // Replica regions list based on article in [Azure regions list](https://learn.microsoft.com/azure/reliability/regions-list) and [Enhance resilience by replicating your Log Analytics workspace across regions](https://learn.microsoft.com/azure/azure-monitor/logs/workspace-replication#supported-regions) for supported regions for Log Analytics Workspace. @@ -91,7 +93,7 @@ var replicaRegionPairs = { uksouth: 'westeurope' westeurope: 'northeurope' } -var replicaLocation = replicaRegionPairs[resourceGroup().location] +var replicaLocation = replicaRegionPairs[solutionLocation] @description('Optional. The Container Registry hostname where the docker images for the container app are located.') param containerRegistryHostname string = 'biabcontainerreg.azurecr.io' @@ -108,14 +110,10 @@ var logAnalyticsWorkspaceResourceId = useExistingLogAnalytics ? existingLogAnalyticsWorkspaceId : logAnalyticsWorkspace!.outputs.resourceId -var rg = resourceGroup() - -var location = resourceGroup().location - var chatGpt = { - modelName: 'gpt-35-turbo-16k' + modelName: 'gpt-4.1-mini' deploymentName: 'chat' - deploymentVersion: '0613' + deploymentVersion: '2025-04-14' deploymentCapacity: chatGptDeploymentCapacity } @@ -135,7 +133,7 @@ var openAiDeployments = [ version: chatGpt.deploymentVersion } sku: { - name: 'Standard' + name: 'GlobalStandard' capacity: chatGpt.deploymentCapacity } } @@ -147,7 +145,7 @@ var openAiDeployments = [ version: embedding.deploymentVersion } sku: { - name: 'Standard' + name: 'GlobalStandard' capacity: embedding.deploymentCapacity } } @@ -158,18 +156,14 @@ var privateDnsZones = [ 'privatelink.cognitiveservices.azure.com' 'privatelink.openai.azure.com' 'privatelink.services.ai.azure.com' - 'privatelink.contentunderstanding.ai.azure.com' 'privatelink.blob.${environment().suffixes.storage}' 'privatelink.queue.${environment().suffixes.storage}' 'privatelink.file.${environment().suffixes.storage}' 'privatelink.api.azureml.ms' - 'privatelink.notebooks.azure.net' 'privatelink.mongo.cosmos.azure.com' 'privatelink.azconfig.io' 'privatelink.vaultcore.azure.net' 'privatelink.azurecr.io' - 'privatelink${environment().suffixes.sqlServerHostname}' - 'privatelink.azurewebsites.net' 'privatelink.search.windows.net' ] // DNS Zone Index Constants @@ -177,20 +171,15 @@ var dnsZoneIndex = { cognitiveServices: 0 openAI: 1 aiServices: 2 - contentUnderstanding: 3 - storageBlob: 4 - storageQueue: 5 - storageFile: 6 - aiFoundry: 7 - notebooks: 8 - cosmosDB: 9 - appConfig: 10 - keyVault: 11 - containerRegistry: 12 - sqlServer: 13 - appService: 14 - search: 15 - formRecognizer: 16 + storageBlob: 3 + storageQueue: 4 + storageFile: 5 + aiFoundry: 6 + cosmosDB: 7 + appConfig: 8 + keyVault: 9 + containerRegistry: 10 + search: 11 } @batchSize(5) module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.7.1' = [ @@ -220,13 +209,13 @@ param aiDeploymentsLocation string // ========== Log Analytics Workspace ========== // // WAF best practices for Log Analytics: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-log-analytics // WAF PSRules for Log Analytics: https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/#azure-monitor-logs -var logAnalyticsWorkspaceResourceName = 'log-${suffix}' +var logAnalyticsWorkspaceResourceName = 'log-${solutionSuffix}' module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.12.0' = if (enableMonitoring) { name: take('avm.res.operational-insights.workspace.${logAnalyticsWorkspaceResourceName}', 64) params: { name: logAnalyticsWorkspaceResourceName tags: tags - location: location + location: solutionLocation enableTelemetry: enableTelemetry skuName: 'PerGB2018' dataRetention: 365 @@ -281,14 +270,14 @@ module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0 } module network 'modules/network.bicep' = if (enablePrivateNetworking) { - name: take('network-${suffix}-deployment', 64) + name: take('network-${solutionSuffix}-deployment', 64) params: { - resourcesName: suffix + resourcesName: solutionSuffix logAnalyticsWorkSpaceResourceId: logAnalyticsWorkspaceResourceId vmAdminUsername: vmAdminUsername ?? 'JumpboxAdminUser' vmAdminPassword: vmAdminPassword ?? 'JumpboxAdminP@ssw0rd1234!' vmSize: vmSize ?? 'Standard_DS2_v2' // Default VM size - location: location + location: solutionLocation tags: tags enableTelemetry: enableTelemetry } @@ -298,12 +287,12 @@ module network 'modules/network.bicep' = if (enablePrivateNetworking) { // ========== AVM WAF ========== // // ========== User Assigned Identity ========== // // WAF best practices for identity and access management: https://learn.microsoft.com/en-us/azure/well-architected/security/identity-access -var userAssignedIdentityResourceName = 'id-${suffix}' +var userAssignedIdentityResourceName = 'id-${solutionSuffix}' module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.1' = { name: take('avm.res.managed-identity.user-assigned-identity.${userAssignedIdentityResourceName}', 64) params: { name: userAssignedIdentityResourceName - location: location + location: solutionLocation tags: tags enableTelemetry: enableTelemetry } @@ -313,12 +302,12 @@ module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-id // ========== AVM WAF ========== // // ========== Storage account module ========== // -var storageAccountName = 'storage-${suffix}' +var storageAccountName = 'st${solutionSuffix}' module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { name: take('avm.res.storage.storage-account.${storageAccountName}', 64) params : { name: storageAccountName - location: location + location: solutionLocation managedIdentities: { systemAssigned: true } minimumTlsVersion: 'TLS1_2' enableTelemetry: enableTelemetry @@ -345,7 +334,7 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { privateEndpoints: enablePrivateNetworking ? [ { - name: 'pep-blob-${suffix}' + name: 'pep-blob-${solutionSuffix}' privateDnsZoneGroup: { privateDnsZoneGroupConfigs: [ { @@ -358,7 +347,7 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { service: 'blob' } { - name: 'pep-queue-${suffix}' + name: 'pep-queue-${solutionSuffix}' privateDnsZoneGroup: { privateDnsZoneGroupConfigs: [ { @@ -387,8 +376,8 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { } // ========== AI Foundry: AI Search ========== // -var aiSearchName = 'srch-${suffix}' -// var aiSearchConnectionName = 'myCon-${suffix}' +var aiSearchName = 'srch-${solutionSuffix}' +// var aiSearchConnectionName = 'myCon-${solutionSuffix}' // var varKvSecretNameAzureSearchKey = 'AZURE-SEARCH-KEY' // AI Foundry: AI Search module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = { @@ -396,22 +385,22 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = params: { name: aiSearchName tags: tags - location: aiDeploymentsLocation + location: solutionLocation enableTelemetry: enableTelemetry diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null - sku: 'standard3' + sku: enableScalability ? 'standard' : 'basic' managedIdentities: { userAssignedResourceIds: [userAssignedIdentity!.outputs.resourceId] } replicaCount: 1 partitionCount: 1 roleAssignments: [ { - roleDefinitionIdOrName: 'Cognitive Services Contributor' // Cognitive Search Contributor + roleDefinitionIdOrName: 'Search Index Data Contributor' // Cognitive Search Contributor principalId: userAssignedIdentity.outputs.principalId principalType: 'ServicePrincipal' } { - roleDefinitionIdOrName: 'Cognitive Services OpenAI User'//'5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'// Cognitive Services OpenAI User + roleDefinitionIdOrName: 'Search Index Data Reader' //'5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'// Cognitive Services OpenAI User principalId: userAssignedIdentity.outputs.principalId principalType: 'ServicePrincipal' } @@ -445,12 +434,12 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = // ========== AVM WAF ========== // // ========== Cognitive Services - OpenAI module ========== // -var openAiAccountName = 'openai-${suffix}' +var openAiAccountName = 'openai-${solutionSuffix}' module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${openAiAccountName}', 64) params: { name: openAiAccountName - location: location + location: solutionLocation kind: 'OpenAI' sku: 'S0' tags: tags @@ -470,7 +459,7 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { privateEndpoints: enablePrivateNetworking ? [ { - name: 'pep-openai-${suffix}' + name: 'pep-openai-${solutionSuffix}' subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId service: 'account' privateDnsZoneGroup: { @@ -508,14 +497,13 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { // ========== Cognitive Services - docIntel module ========== // // Document Intelligence (Form Recognizer) -var docIntelAccountName = 'docIntel-${suffix}' +var docIntelAccountName = 'docIntel-${solutionSuffix}' module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${docIntelAccountName}', 64) - scope: rg params: { name: docIntelAccountName - location: location + location: solutionLocation kind: 'FormRecognizer' tags: tags sku: 'S0' @@ -534,14 +522,14 @@ module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { privateEndpoints: enablePrivateNetworking ? [ { - name: 'pep-docintel-${suffix}' + name: 'pep-docintel-${solutionSuffix}' subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId service: 'account' privateDnsZoneGroup: { privateDnsZoneGroupConfigs: [ { name: 'docintel-dns-zone-group' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.formRecognizer]!.outputs.resourceId + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId } ] } @@ -564,112 +552,112 @@ module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { // ========== AVM WAF ========== // // ========== Container App module ========== // -var containerAppResourceName = 'ca-${suffix}' -module containerApp 'br/public:avm/res/app/container-app:0.18.1' = { - name: take('avm.res.app.container-app.${containerAppResourceName}', 64) - params: { - name: containerAppResourceName - tags: tags - location: location - enableTelemetry: enableTelemetry - environmentResourceId: containerAppEnvironment.outputs.resourceId - managedIdentities: { systemAssigned: true } - ingressTargetPort: 8000 - ingressExternal: true - activeRevisionsMode: 'Single' - // corsPolicy: { - // allowedOrigins: [ - // 'https://${webSiteName}.azurewebsites.net' - // 'http://${webSiteName}.azurewebsites.net' - // ] - // } - // WAF aligned configuration for Scalability - scaleSettings: { - maxReplicas: enableScalability ? 3 : 1 - minReplicas: enableScalability ? 2 : 1 - rules: [ - { - name: 'http-scaler' - http: { - metadata: { - concurrentRequests: '100' - } - } - } - ] - } - containers: [ - { - name: 'backend' - image: '${containerRegistryHostname}/${containerImageName}:${containerImageTag}' - resources: { - cpu: '2.0' - memory: '4.0Gi' - } - env: [ - { - name: '{ENVIRONMENT_VARIABLE_NAME}' - value: '{ENVIRONMENT_VARIABLE_VALUE}' - } - ] - } - ] - } -} - -var containerAppEnvironmentResourceName = 'cae-${suffix}' -module containerAppEnvironment 'br/public:avm/res/app/managed-environment:0.11.2' = { - name: take('avm.res.app.managed-environment.${containerAppEnvironmentResourceName}', 64) - params: { - name: containerAppEnvironmentResourceName - location: location - tags: tags - enableTelemetry: enableTelemetry - publicNetworkAccess: 'Enabled' - internal: false - // WAF aligned configuration for Private Networking - infrastructureSubnetResourceId: enablePrivateNetworking ? network.?outputs.?subnetResourceIds[3] : null - - // WAF aligned configuration for Monitoring - appLogsConfiguration: enableMonitoring - ? { - destination: 'log-analytics' - logAnalyticsConfiguration: { - customerId: logAnalyticsWorkspace!.outputs.logAnalyticsWorkspaceId - sharedKey: logAnalyticsWorkspace!.outputs!.primarySharedKey - } - } - : null - appInsightsConnectionString: enableMonitoring ? applicationInsights!.outputs.connectionString : null - // WAF aligned configuration for Redundancy - zoneRedundant: enableRedundancy ? true : false - infrastructureResourceGroupName: enableRedundancy ? '${resourceGroup().name}-infra' : null - workloadProfiles: enableRedundancy - ? [ - { - maximumCount: 3 - minimumCount: 3 - name: 'CAW01' - workloadProfileType: 'D4' - } - ] - : [ - { - name: 'Consumption' - workloadProfileType: 'Consumption' - } - ] - } -} +//var containerAppResourceName = 'ca-${solutionSuffix}' +// module containerApp 'br/public:avm/res/app/container-app:0.18.1' = { +// name: take('avm.res.app.container-app.${containerAppResourceName}', 64) +// params: { +// name: containerAppResourceName +// tags: tags +// location: solutionLocation +// enableTelemetry: enableTelemetry +// environmentResourceId: containerAppEnvironment.outputs.resourceId +// managedIdentities: { systemAssigned: true } +// ingressTargetPort: 8000 +// ingressExternal: true +// activeRevisionsMode: 'Single' +// // corsPolicy: { +// // allowedOrigins: [ +// // 'https://${webSiteName}.azurewebsites.net' +// // 'http://${webSiteName}.azurewebsites.net' +// // ] +// // } +// // WAF aligned configuration for Scalability +// scaleSettings: { +// maxReplicas: enableScalability ? 3 : 1 +// minReplicas: enableScalability ? 2 : 1 +// rules: [ +// { +// name: 'http-scaler' +// http: { +// metadata: { +// concurrentRequests: '100' +// } +// } +// } +// ] +// } +// containers: [ +// { +// name: 'backend' +// image: '${containerRegistryHostname}/${containerImageName}:${containerImageTag}' +// resources: { +// cpu: '2.0' +// memory: '4.0Gi' +// } +// env: [ +// { +// name: '{ENVIRONMENT_VARIABLE_NAME}' +// value: '{ENVIRONMENT_VARIABLE_VALUE}' +// } +// ] +// } +// ] +// } +// } + +// var containerAppEnvironmentResourceName = 'cae-${solutionSuffix}' +// module containerAppEnvironment 'br/public:avm/res/app/managed-environment:0.11.2' = { +// name: take('avm.res.app.managed-environment.${containerAppEnvironmentResourceName}', 64) +// params: { +// name: containerAppEnvironmentResourceName +// location: solutionLocation +// tags: tags +// enableTelemetry: enableTelemetry +// publicNetworkAccess: 'Enabled' +// internal: false +// // WAF aligned configuration for Private Networking +// infrastructureSubnetResourceId: enablePrivateNetworking ? network.?outputs.?subnetResourceIds[3] : null + +// // WAF aligned configuration for Monitoring +// appLogsConfiguration: enableMonitoring +// ? { +// destination: 'log-analytics' +// logAnalyticsConfiguration: { +// customerId: logAnalyticsWorkspace!.outputs.logAnalyticsWorkspaceId +// sharedKey: logAnalyticsWorkspace!.outputs!.primarySharedKey +// } +// } +// : null +// appInsightsConnectionString: enableMonitoring ? applicationInsights!.outputs.connectionString : null +// // WAF aligned configuration for Redundancy +// zoneRedundant: enableRedundancy ? true : false +// infrastructureResourceGroupName: enableRedundancy ? '${resourceGroup().name}-infra' : null +// workloadProfiles: enableRedundancy +// ? [ +// { +// maximumCount: 3 +// minimumCount: 3 +// name: 'CAW01' +// workloadProfileType: 'D4' +// } +// ] +// : [ +// { +// name: 'Consumption' +// workloadProfileType: 'Consumption' +// } +// ] +// } +// } // ========== Application Insights ========== // -var applicationInsightsResourceName = 'appi-${suffix}' +var applicationInsightsResourceName = 'appi-${solutionSuffix}' module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (enableMonitoring) { name: take('avm.res.insights.component.${applicationInsightsResourceName}', 64) params: { name: applicationInsightsResourceName tags: tags - location: location + location: solutionLocation enableTelemetry: enableTelemetry retentionInDays: 365 kind: 'web' @@ -685,13 +673,13 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en */ @description('Contains Solution Name.') -output SOLUTION_NAME string = suffix +output SOLUTION_NAME string = solutionSuffix @description('Contains Resource Group Name.') output RESOURCE_GROUP_NAME string = resourceGroup().name @description('Contains Resource Group Location.') -output RESOURCE_GROUP_LOCATION string = location +output RESOURCE_GROUP_LOCATION string = solutionLocation // @description('The FQDN of the frontend web app service.') // output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 00000000..c41d73d7 --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "solutionName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + } + } +} \ No newline at end of file From e9ab3a77cc7bbe58e702c5207633246d6b39c233 Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Tue, 9 Sep 2025 19:15:09 +0530 Subject: [PATCH 05/29] resourcedeployment ps changes --- Deployment/resourcedeployment.ps1 | 214 +++++++++++++++++------------- 1 file changed, 121 insertions(+), 93 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 925c4e66..acc3f49b 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -188,55 +188,59 @@ function LoginAzure([string]$subscriptionID) { Write-Host "Switched subscription to '$subscriptionID' `r`n" -ForegroundColor Yellow } -function DeployAzureResources([string]$location, [string]$modelLocation) { +function DeployAzureResources() { Write-Host "Started Deploying Knowledge Mining Solution Accelerator Service Azure resources.....`r`n" -ForegroundColor Yellow - + try { - # Generate a random number between 0 and 99999 + # Load variables from .env file + if (Test-Path ".env") { + Get-Content ".env" | ForEach-Object { + if ($_ -match "^\s*([^#][^=]+)=(.+)$") { + $key = $matches[1].Trim() + $value = $matches[2].Trim() + Set-Variable -Name $key -Value $value -Scope Script + } + } + Write-Host "Loaded variables from .env file" -ForegroundColor Green + } + else { + Write-Host ".env file not found. Please create one." -ForegroundColor Red + failureBanner + exit 1 + } + + # Required values from .env + $envLocation = if ($env:LOCATION) { $env:LOCATION } else { "eastus" } + $environmentName = if ($env:ENVIRONMENT_NAME) { $env:ENVIRONMENT_NAME } else { "dev" } + $resourceGroupName = if ($env:RESOURCE_GROUP_NAME) { $env:RESOURCE_GROUP_NAME } else { "" } + + # Generate random deployment name $randomNumber = Get-Random -Minimum 0 -Maximum 99999 - # Pad the number with leading zeros to ensure it is 5 digits long $randomNumberPadded = $randomNumber.ToString("D5") - # Make deployment name unique by appending random number $deploymentName = "KM_SA_Deployment$randomNumberPadded" - - if (-not $resourceGroupName) { - # Generate a new RG name using your existing logic - - # Load abbreviation from abbreviations.json (optional) + if (-not $resourceGroupName -or $resourceGroupName -eq "") { + # Load abbreviation for RG prefix $abbrs = Get-Content -Raw -Path "./abbreviations.json" | ConvertFrom-Json if (-not $abbrs -or -not $abbrs.managementGovernance.resourceGroup) { Write-Host "abbreviations.json is missing or malformed." failureBanner exit 1 } - $rgPrefix = $abbrs.managementGovernance.resourceGroup # e.g., "rg-" - - # Constants + $rgPrefix = $abbrs.managementGovernance.resourceGroup $resourceprefix_name = "kmgs" - # Call Bicep to generate resourcePrefix - $resourcePrefix = az deployment sub create ` - --location $location ` - --name $deploymentName ` - --template-file ./resourcePrefix.bicep ` - --parameters environmentName=$environmentName location=$location ` - --query "properties.outputs.resourcePrefix.value" ` - -o tsv - - # Final Resource Group Name - $resourceGroupName = "$rgPrefix$resourceprefix_name$resourcePrefix" + # Just generate RG name using prefix and random suffix + $resourceGroupName = "$rgPrefix$resourceprefix_name$randomNumberPadded" Write-Host "Generated Resource Group Name: $resourceGroupName" - - Write-Host "No RG provided. Creating new RG: $resourceGroupName" -ForegroundColor Yellow - az group create --name $resourceGroupName --location $location --tags EnvironmentName=$environmentName TemplateName="DKM" | Out-Null + az group create --name $resourceGroupName --location $envLocation --tags EnvironmentName=$environmentName TemplateName="DKM" | Out-Null } else { $exists = az group exists --name $resourceGroupName | ConvertFrom-Json if (-not $exists) { Write-Host "Specified RG does not exist. Creating RG: $resourceGroupName" -ForegroundColor Yellow - az group create --name $resourceGroupName --location $location --tags EnvironmentName=$environmentName TemplateName="DKM" | Out-Null + az group create --name $resourceGroupName --location $envLocation --tags EnvironmentName=$environmentName TemplateName="DKM" | Out-Null } else { az group update --name $resourceGroupName --set tags.EnvironmentName=$environmentName tags.TemplateName="DKM" | Out-Null @@ -244,34 +248,44 @@ function DeployAzureResources([string]$location, [string]$modelLocation) { } } - # Perform a what-if deployment to preview changes - Write-Host "Evaluating Deployment resource availabilities to preview changes..." -ForegroundColor Yellow - $whatIfResult = az deployment group what-if --resource-group $resourceGroupName --template-file "./main.bicep" --name $deploymentName --parameters modeldatacenter=$modelLocation location=$location environmentName=$environmentName - - if ($LASTEXITCODE -ne 0) { - Write-Host "There might be something wrong with your deployment." -ForegroundColor Red - Write-Host $whatIfResult -ForegroundColor Red - failureBanner - exit 1 + # Not taking values from main.bicep. + # Instead, you can deploy resources manually with az CLI using values from .env. + # Example: deploying storage account (if defined in .env) + if ($env:STORAGE_ACCOUNT_NAME) { + Write-Host "Deploying storage account: $($env:STORAGE_ACCOUNT_NAME)" -ForegroundColor Yellow + az storage account create ` + --name $env:STORAGE_ACCOUNT_NAME ` + --resource-group $resourceGroupName ` + --location $envLocation ` + --sku Standard_LRS ` + --kind StorageV2 | Out-Null } - # Proceed with the actual deployment - Write-Host "Proceeding with Deployment..." -ForegroundColor Yellow - Write-Host "Resource Group Name: $resourceGroupName" -ForegroundColor Yellow - $deploymentResult = az deployment group create --resource-group $resourceGroupName --template-file "./main.bicep" --name $deploymentName --parameters modeldatacenter=$modelLocation location=$location environmentName=$environmentName - # Check if deploymentResult is valid - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult -variableName "Deployment Result" - if ($LASTEXITCODE -ne 0) { - Write-Host "Deployment failed. Stopping execution." -ForegroundColor Red - Write-Host $deploymentResult -ForegroundColor Red - failureBanner - exit 1 + + # Example: deploying Cognitive Services (if defined in .env) + if ($env:COGNITIVE_ACCOUNT_NAME) { + Write-Host "Deploying Cognitive Services: $($env:COGNITIVE_ACCOUNT_NAME)" -ForegroundColor Yellow + az cognitiveservices account create ` + --name $env:COGNITIVE_ACCOUNT_NAME ` + --resource-group $resourceGroupName ` + --kind CognitiveServices ` + --sku S0 ` + --location $envLocation ` + --yes | Out-Null } - $joinedString = $deploymentResult -join "" - $jsonString = ConvertFrom-Json $joinedString - - return $jsonString - } catch { + Write-Host "Deployment completed successfully using .env values" -ForegroundColor Green + + # Build JSON result + $result = @{ + ResourceGroupName = $resourceGroupName + DeploymentName = $deploymentName + Location = $envLocation + Environment = $environmentName + Timestamp = (Get-Date).ToString("s") + } + return ($result | ConvertTo-Json -Depth 3) + } + catch { Write-Host "An error occurred during the deployment process:" -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red Write-Host $_.InvocationInfo.PositionMessage -ForegroundColor Red @@ -281,6 +295,7 @@ function DeployAzureResources([string]$location, [string]$modelLocation) { } } + function DisplayResult([pscustomobject]$jsonString) { $resourcegroupName = $jsonString.properties.outputs.gs_resourcegroup_name.value $solutionPrefix = $jsonString.properties.outputs.gs_solution_prefix.value @@ -511,62 +526,75 @@ try { ############################################################### $deploymentResult = [DeploymentResult]::new() LoginAzure($subscriptionID) - # Deploy Azure Resources - Write-Host "Deploying Azure resources in $location region.....`r`n" -ForegroundColor Yellow + # Deploy Azure Resources using .env values + Write-Host "Deploying Azure resources from .env configuration.....`r`n" -ForegroundColor Yellow - $resultJson = DeployAzureResources -location $location -modelLocation $modelLocation + $resultJson = DeployAzureResources + # Convert JSON string back to object + $resultObj = $resultJson | ConvertFrom-Json # Ensure ResourceGroupName is set correctly - $deploymentResult.ResourceGroupName = $resourceGroupName + $deploymentResult.ResourceGroupName = $resultObj.ResourceGroupName # Map the deployment result to DeploymentResult object $deploymentResult.MapResult($resultJson) # Display the deployment result - DisplayResult($resultJson) + DisplayResult $resultObj ############################################################### # Step 2 : Get Secrets from Azure resources Show-Banner -Title "Step 2 : Get Secrets from Azure resources" ############################################################### - # Validate if the Storage Account Name is empty or null + + # Pull values from environment variables (or fall back to defaults) + # Build deployment result object from .env values + $deploymentResult = [PSCustomObject]@{ + ResourceGroupName = $env:RESOURCE_GROUP_NAME + StorageAccountName = $env:STORAGE_ACCOUNT_NAME + AzCosmosDBName = $env:AZ_COSMOSDB_NAME + AzCognitiveServiceName = $env:AZ_COGNITIVE_SERVICE_NAME + AzSearchServiceName = $env:AZ_SEARCH_SERVICE_NAME + AzOpenAiServiceName = $env:AZ_OPENAI_SERVICE_NAME + AzOpenAiServiceEndpoint = $env:AZ_OPENAI_SERVICE_ENDPOINT + AzSearchServiceEndpoint = $env:AZ_SEARCH_SERVICE_ENDPOINT + AzCognitiveServiceEndpoint = $env:AZ_COGNITIVE_SERVICE_ENDPOINT + AzGPT4oModelId = $env:AZ_GPT4O_MODEL_ID + AzGPT4oModelName = $env:AZ_GPT4O_MODEL_NAME + AzGPTEmbeddingModelName = $env:AZ_GPT_EMBEDDING_MODEL_NAME + } + + # Validate Storage Account Name ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.StorageAccountName -variableName "Storage Account Name" # Get the storage account key - $storageAccountKey = az storage account keys list --account-name $deploymentResult.StorageAccountName --resource-group $deploymentResult.ResourceGroupName --query "[0].value" -o tsv - - # Validate if the storage account key is empty or null - ValidateVariableIsNullOrEmpty -variableValue $storageAccountKey -variableName "Storage account key" - - ## Construct the connection string manually + $storageAccountKey = az storage account keys list ` + --account-name $deploymentResult.StorageAccountName ` + --resource-group $deploymentResult.ResourceGroupName ` + --query "[0].value" -o tsv + + # Validate storage account key + ValidateVariableIsNullOrEmpty -variableValue $storageAccountKey -variableName "Storage account key" + + # Construct connection string manually $storageAccountConnectionString = "DefaultEndpointsProtocol=https;AccountName=$($deploymentResult.StorageAccountName);AccountKey=$storageAccountKey;EndpointSuffix=core.windows.net" - # Validate if the Storage Account Connection String is empty or null + + # Validate storage account connection string ValidateVariableIsNullOrEmpty -variableValue $storageAccountConnectionString -variableName "Storage Account Connection String" - - ## Assign the connection string to the deployment result object - $deploymentResult.StorageAccountConnectionString = $storageAccountConnectionString - - # Check if ResourceGroupName is valid - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupName -variableName "Resource group name" - - # Check if AzCosmosDBName is valid - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCosmosDBName -variableName "Az Cosmos DB name" - - # Check if AzCognitiveServiceName is valid - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCognitiveServiceName -variableName "Az Cognitive Service name" - - # Check if AzSearchServiceName is valid - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzSearchServiceName -variableName "Az Search Service name" - - # Check if AzOpenAiServiceName is valid - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzOpenAiServiceName -variableName "Az OpenAI Service name" - - # Get MongoDB connection string - $deploymentResult.AzCosmosDBConnectionString = az cosmosdb keys list --name $deploymentResult.AzCosmosDBName --resource-group $deploymentResult.ResourceGroupName --type connection-strings --query "connectionStrings[0].connectionString" -o tsv - # Get Azure Cognitive Service API Key - $deploymentResult.AzCognitiveServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzCognitiveServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv - # Get Azure Search Service Admin Key - $deploymentResult.AzSearchAdminKey = az search admin-key show --service-name $deploymentResult.AzSearchServiceName --resource-group $deploymentResult.ResourceGroupName --query "primaryKey" -o tsv - # Get Azure Open AI Service API Key - $deploymentResult.AzOpenAiServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzOpenAiServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv + + # Assign connection string + $deploymentResult | Add-Member -NotePropertyName StorageAccountConnectionString -NotePropertyValue $storageAccountConnectionString -Force + + # Validate core service names + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupName -variableName "Resource group name" + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCosmosDBName -variableName "Az Cosmos DB name" + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCognitiveServiceName -variableName "Az Cognitive Service name" + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzSearchServiceName -variableName "Az Search Service name" + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzOpenAiServiceName -variableName "Az OpenAI Service name" + + # Retrieve secrets + $deploymentResult | Add-Member -NotePropertyName AzCosmosDBConnectionString -NotePropertyValue (az cosmosdb keys list --name $deploymentResult.AzCosmosDBName --resource-group $deploymentResult.ResourceGroupName --type connection-strings --query "connectionStrings[0].connectionString" -o tsv) -Force + $deploymentResult | Add-Member -NotePropertyName AzCognitiveServiceKey -NotePropertyValue (az cognitiveservices account keys list --name $deploymentResult.AzCognitiveServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv) -Force + $deploymentResult | Add-Member -NotePropertyName AzSearchAdminKey -NotePropertyValue (az search admin-key show --service-name $deploymentResult.AzSearchServiceName --resource-group $deploymentResult.ResourceGroupName --query "primaryKey" -o tsv) -Force + $deploymentResult | Add-Member -NotePropertyName AzOpenAiServiceKey -NotePropertyValue (az cognitiveservices account keys list --name $deploymentResult.AzOpenAiServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv) -Force Write-Host "Secrets have been retrieved successfully." -ForegroundColor Green From adca3cdd854a055465e8e623ace8f5b4a353ae79 Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Tue, 9 Sep 2025 19:56:44 +0530 Subject: [PATCH 06/29] output variables added --- Deployment/resourcedeployment.ps1 | 4 +- infra/main.bicep | 71 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index acc3f49b..5fdeccd5 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -210,8 +210,8 @@ function DeployAzureResources() { } # Required values from .env - $envLocation = if ($env:LOCATION) { $env:LOCATION } else { "eastus" } - $environmentName = if ($env:ENVIRONMENT_NAME) { $env:ENVIRONMENT_NAME } else { "dev" } + $envLocation = if ($env:RESOURCE_GROUP_LOCATION) { $env:RESOURCE_GROUP_LOCATION } else { "eastus" } + $environmentName = if ($env:AZURE_ENV_NAME) { $env:AZURE_ENV_NAME } else { "dev" } $resourceGroupName = if ($env:RESOURCE_GROUP_NAME) { $env:RESOURCE_GROUP_NAME } else { "" } # Generate random deployment name diff --git a/infra/main.bicep b/infra/main.bicep index a5ee7e33..6c7f762b 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -76,6 +76,37 @@ param enableScalability bool = false @description('Optional. Enable purge protection for the Key Vault') param enablePurgeProtection bool = false +@description('Optional. Cognitive Account Name') +param cognitiveAccountName string = '' + +@description('Optional. Azure Search Service Name') +param azureSearchServiceName string = '' + +@description('Optional. Azure Cosmos DB Name') +param azureCosmosDbName string = '' + +@description('Optional. Azure Cognitive Service Name') +param azureCognitiveServiceName string = '' + +@description('Optional. Contains Azure OpenAI Service Endpoint.') +param azureOpenAiServiceEndpoint string = '' + +@description('Optional. Contains Azure Search Service Endpoint.') +param azureSearchServiceEndpoint string = '' + +@description('Optional. Contains Azure Cognitive Service Endpoint.') +param azureCognitiveServiceEndpoint string = '' + +@minLength(1) +@description('Optional. Name of the Text Embedding model to deploy:') +@allowed([ + 'text-embedding-ada-002' +]) +param embeddingModel string = 'text-embedding-ada-002' + +@description('Optional. Contains Azure GPT 40 Model Name.') +param azureGpt40ModelName string = '' + var solutionLocation = empty(location) ? resourceGroup().location : location // @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') @@ -681,6 +712,46 @@ output RESOURCE_GROUP_NAME string = resourceGroup().name @description('Contains Resource Group Location.') output RESOURCE_GROUP_LOCATION string = solutionLocation +@description('Contains Resource Group Name.') +output STORAGE_ACCOUNT_NAME string = storageAccountName + +@description('Contains Cognitive Account Name.') +output COGNITIVE_ACCOUNT_NAME string = cognitiveAccountName + +@description('Contains Cosmos DB Name.') +output AZ_COSMOSDB_NAME string = azureCosmosDbName + +@description('Contains Cognitive Service Name.') +output AZ_COGNITIVE_SERVICE_NAME string = azureCognitiveServiceName + +@description('Contains Azure Search Service Name.') +output AZ_SEARCH_SERVICE_NAME string = azureSearchServiceName + +@description('Contains Azure OpenAI Search Service Name.') +output AZ_OPENAI_SERVICE_NAME string = openAiAccountName + +@description('Contains Azure OpenAI Service Endpoint.') +output AZ_OPENAI_SERVICE_ENDPOINT string = azureOpenAiServiceEndpoint + +@description('Contains Azure Search Service Endpoint.') +output AZ_SEARCH_SERVICE_ENDPOINT string = azureSearchServiceEndpoint + +@description('Contains Azure Cognitive Service Endpoint.') +output AZ_COGNITIVE_SERVICE_ENDPOINT string = azureCognitiveServiceEndpoint + +@description('Contains Azure GPT40 Model ID.') +output AZ_GPT4O_MODEL_ID string = azureSearchServiceName + +@description('Contains Azure OpenAI embedding model name.') +output AZ_GPT4O_MODEL_NAME string = azureGpt40ModelName + +@description('Contains Azure OpenAI embedding model name.') +output AZ_GPT_EMBEDDING_MODEL_NAME string = embeddingModel + +@description('Contains Azure OpenAI embedding model capacity.') +output AZ_OPENAI_EMBEDDING_MODEL_CAPACITY int = embeddingDeploymentCapacity + + // @description('The FQDN of the frontend web app service.') // output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN From 2412c4f0a6d89320f3b9e65fb04178126011c591 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Tue, 9 Sep 2025 20:04:39 +0530 Subject: [PATCH 07/29] feat: Added Modules for Cosmos, Conainer Registry, App Config --- infra/main.bicep | 299 ++++++++++++++----------- infra/modules/container-registry.bicep | 41 ++++ 2 files changed, 211 insertions(+), 129 deletions(-) create mode 100644 infra/modules/container-registry.bicep diff --git a/infra/main.bicep b/infra/main.bicep index a5ee7e33..b0e797f2 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -4,7 +4,7 @@ targetScope = 'resourceGroup' @minLength(3) @maxLength(20) @description('Required. A unique prefix for all resources in this deployment. This should be 3-20 characters long:') -param solutionName string = 'dkm' +param solutionName string = 'kmgs' @description('Optional. Azure location for the solution. If not provided, it defaults to the resource group location.') param location string = '' @@ -48,7 +48,7 @@ param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = param enableTelemetry bool = true @description('Optional. Enable private networking for applicable resources, aligned with the WAF recommendations. Defaults to false.') -param enablePrivateNetworking bool = false +param enablePrivateNetworking bool = true @description('Optional: Existing Log Analytics Workspace Resource ID') param existingLogAnalyticsWorkspaceId string = '' @@ -65,7 +65,7 @@ param vmAdminPassword string? param vmSize string = 'Standard_DS2_v2' @description('Optional. Enable monitoring applicable resources, aligned with the Well Architected Framework recommendations. This setting enables Application Insights and Log Analytics and configures all the resources applicable resources to send logs. Defaults to false.') -param enableMonitoring bool = false +param enableMonitoring bool = true @description('Optional. Enable redundancy for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false.') param enableRedundancy bool = false @@ -106,9 +106,6 @@ param containerImageTag string = 'latest_2025-07-22_895' // Extracts subscription, resource group, and workspace name from the resource ID when using an existing Log Analytics workspace var useExistingLogAnalytics = !empty(existingLogAnalyticsWorkspaceId) -var logAnalyticsWorkspaceResourceId = useExistingLogAnalytics - ? existingLogAnalyticsWorkspaceId - : logAnalyticsWorkspace!.outputs.resourceId var chatGpt = { modelName: 'gpt-4.1-mini' @@ -155,15 +152,15 @@ var openAiDeployments = [ var privateDnsZones = [ 'privatelink.cognitiveservices.azure.com' 'privatelink.openai.azure.com' - 'privatelink.services.ai.azure.com' + 'privatelink.services.ai.azure.com' // Todo: to be deleted 'privatelink.blob.${environment().suffixes.storage}' 'privatelink.queue.${environment().suffixes.storage}' - 'privatelink.file.${environment().suffixes.storage}' + 'privatelink.file.${environment().suffixes.storage}' // Todo: to be deleted 'privatelink.api.azureml.ms' 'privatelink.mongo.cosmos.azure.com' 'privatelink.azconfig.io' - 'privatelink.vaultcore.azure.net' - 'privatelink.azurecr.io' + 'privatelink.vaultcore.azure.net' // Todo: to be deleted + 'privatelink.azurecr.io' // Todo: to be deleted 'privatelink.search.windows.net' ] // DNS Zone Index Constants @@ -189,7 +186,7 @@ module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.7.1' = [ name: zone tags: tags enableTelemetry: enableTelemetry - virtualNetworkLinks: [{ virtualNetworkResourceId: network.outputs.subnetPrivateEndpointsResourceId }] + virtualNetworkLinks: [{ virtualNetworkResourceId: network!.outputs.vnetResourceId }] } } ] @@ -210,7 +207,7 @@ param aiDeploymentsLocation string // WAF best practices for Log Analytics: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-log-analytics // WAF PSRules for Log Analytics: https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/#azure-monitor-logs var logAnalyticsWorkspaceResourceName = 'log-${solutionSuffix}' -module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.12.0' = if (enableMonitoring) { +module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0.12.0' = if (enableMonitoring && !useExistingLogAnalytics) { name: take('avm.res.operational-insights.workspace.${logAnalyticsWorkspaceResourceName}', 64) params: { name: logAnalyticsWorkspaceResourceName @@ -268,6 +265,7 @@ module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0 : null } } +var logAnalyticsWorkspaceResourceId = useExistingLogAnalytics ? existingLogAnalyticsWorkspaceId : logAnalyticsWorkspace!.outputs.resourceId module network 'modules/network.bicep' = if (enablePrivateNetworking) { name: take('network-${solutionSuffix}-deployment', 64) @@ -283,8 +281,6 @@ module network 'modules/network.bicep' = if (enablePrivateNetworking) { } } - -// ========== AVM WAF ========== // // ========== User Assigned Identity ========== // // WAF best practices for identity and access management: https://learn.microsoft.com/en-us/azure/well-architected/security/identity-access var userAssignedIdentityResourceName = 'id-${solutionSuffix}' @@ -298,8 +294,119 @@ module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-id } } +// ========== Container Registry ========== // +module avmContainerRegistry './modules/container-registry.bicep' = { + //name: format(deployment_param.resource_name_format_string, abbrs.containers.containerRegistry) + params: { + acrName: 'cr${replace(solutionSuffix, '-', '')}' + location: solutionLocation + acrSku: 'Standard' + publicNetworkAccess: 'Enabled' + zoneRedundancy: 'Disabled' + roleAssignments: [ + { + principalId: managedCluster.outputs.systemAssignedMIPrincipalId + roleDefinitionIdOrName: 'AcrPull' + principalType: 'ServicePrincipal' + } + ] + tags: tags + } +} + +// ========== Cosmos Database for Mongo DB ========== // +module avmCosmosDB 'br/public:avm/res/document-db/database-account:0.15.0' = { + name: 'cosmos-${solutionSuffix}' + params: { + name: 'cosmos-${solutionSuffix}' + location: solutionLocation + mongodbDatabases: [ + { + name: 'default' + tag: 'default database' + } + ] + tags: tags + enableTelemetry: enableTelemetry + databaseAccountOfferType: 'Standard' + automaticFailover: false + serverVersion: '7.0' + capabilitiesToAdd: [ + 'EnableMongo' + ] + enableAnalyticalStorage: true + defaultConsistencyLevel: 'Session' + maxIntervalInSeconds: 5 + maxStalenessPrefix: 100 + zoneRedundant: false + + // WAF related parameters + networkRestrictions: { + publicNetworkAccess: (enablePrivateNetworking) ? 'Disabled' : 'Enabled' + ipRules: [] + virtualNetworkRules: [] + } + + privateEndpoints: (enablePrivateNetworking) + ? [ + { + name: 'cosmosdb-private-endpoint-${solutionSuffix}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cosmosDB].outputs.resourceId + } + ] + } + service: 'MongoDB' + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId // Use the backend subnet + } + ] + : [] + } +} + +// ========== App Configuration store ========== // +var appConfigName = 'appcs-${solutionSuffix}' +module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6.3' = { + name: take('avm.res.app-configuration.configuration-store.${appConfigName}', 64) + params: { + name: appConfigName + location: location + managedIdentities: { systemAssigned: true } + sku: 'Standard' + enableTelemetry: enableTelemetry + tags: tags + + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'App Configuration Data Reader' + principalType: 'ServicePrincipal' + } + ] + + // WAF aligned networking + publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' + privateEndpoints: enablePrivateNetworking + ? [ + { + name: 'pep-appconfig-${solutionSuffix}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'appconfig-dns-zone-group' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.appConfig].outputs.resourceId + } + ] + } + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId + } + ] + : [] + } +} -// ========== AVM WAF ========== // // ========== Storage account module ========== // var storageAccountName = 'st${solutionSuffix}' @@ -392,7 +499,7 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = managedIdentities: { userAssignedResourceIds: [userAssignedIdentity!.outputs.resourceId] } replicaCount: 1 partitionCount: 1 - + roleAssignments: [ { roleDefinitionIdOrName: 'Search Index Data Contributor' // Cognitive Search Contributor @@ -430,11 +537,9 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = } } +// // ========== Cognitive Services - OpenAI module ========== // -// ========== AVM WAF ========== // -// ========== Cognitive Services - OpenAI module ========== // - -var openAiAccountName = 'openai-${solutionSuffix}' +var openAiAccountName = 'oai-${solutionSuffix}' module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${openAiAccountName}', 64) params: { @@ -444,7 +549,7 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { sku: 'S0' tags: tags enableTelemetry: enableTelemetry - + customSubDomainName: openAiAccountName managedIdentities: { systemAssigned: true } @@ -493,12 +598,8 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { } } -// ========== AVM WAF ========== // -// ========== Cognitive Services - docIntel module ========== // - -// Document Intelligence (Form Recognizer) -var docIntelAccountName = 'docIntel-${solutionSuffix}' - +// ========== Cognitive Services - Document Intellignece module ========== // +var docIntelAccountName = 'di-${solutionSuffix}' module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${docIntelAccountName}', 64) params: { @@ -507,6 +608,7 @@ module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { kind: 'FormRecognizer' tags: tags sku: 'S0' + customSubDomainName: docIntelAccountName managedIdentities: { systemAssigned: true } @@ -548,107 +650,46 @@ module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { } } - -// ========== AVM WAF ========== // -// ========== Container App module ========== // - -//var containerAppResourceName = 'ca-${solutionSuffix}' -// module containerApp 'br/public:avm/res/app/container-app:0.18.1' = { -// name: take('avm.res.app.container-app.${containerAppResourceName}', 64) -// params: { -// name: containerAppResourceName -// tags: tags -// location: solutionLocation -// enableTelemetry: enableTelemetry -// environmentResourceId: containerAppEnvironment.outputs.resourceId -// managedIdentities: { systemAssigned: true } -// ingressTargetPort: 8000 -// ingressExternal: true -// activeRevisionsMode: 'Single' -// // corsPolicy: { -// // allowedOrigins: [ -// // 'https://${webSiteName}.azurewebsites.net' -// // 'http://${webSiteName}.azurewebsites.net' -// // ] -// // } -// // WAF aligned configuration for Scalability -// scaleSettings: { -// maxReplicas: enableScalability ? 3 : 1 -// minReplicas: enableScalability ? 2 : 1 -// rules: [ -// { -// name: 'http-scaler' -// http: { -// metadata: { -// concurrentRequests: '100' -// } -// } -// } -// ] -// } -// containers: [ -// { -// name: 'backend' -// image: '${containerRegistryHostname}/${containerImageName}:${containerImageTag}' -// resources: { -// cpu: '2.0' -// memory: '4.0Gi' -// } -// env: [ -// { -// name: '{ENVIRONMENT_VARIABLE_NAME}' -// value: '{ENVIRONMENT_VARIABLE_VALUE}' -// } -// ] -// } -// ] -// } -// } - -// var containerAppEnvironmentResourceName = 'cae-${solutionSuffix}' -// module containerAppEnvironment 'br/public:avm/res/app/managed-environment:0.11.2' = { -// name: take('avm.res.app.managed-environment.${containerAppEnvironmentResourceName}', 64) -// params: { -// name: containerAppEnvironmentResourceName -// location: solutionLocation -// tags: tags -// enableTelemetry: enableTelemetry -// publicNetworkAccess: 'Enabled' -// internal: false -// // WAF aligned configuration for Private Networking -// infrastructureSubnetResourceId: enablePrivateNetworking ? network.?outputs.?subnetResourceIds[3] : null - -// // WAF aligned configuration for Monitoring -// appLogsConfiguration: enableMonitoring -// ? { -// destination: 'log-analytics' -// logAnalyticsConfiguration: { -// customerId: logAnalyticsWorkspace!.outputs.logAnalyticsWorkspaceId -// sharedKey: logAnalyticsWorkspace!.outputs!.primarySharedKey -// } -// } -// : null -// appInsightsConnectionString: enableMonitoring ? applicationInsights!.outputs.connectionString : null -// // WAF aligned configuration for Redundancy -// zoneRedundant: enableRedundancy ? true : false -// infrastructureResourceGroupName: enableRedundancy ? '${resourceGroup().name}-infra' : null -// workloadProfiles: enableRedundancy -// ? [ -// { -// maximumCount: 3 -// minimumCount: 3 -// name: 'CAW01' -// workloadProfileType: 'D4' -// } -// ] -// : [ -// { -// name: 'Consumption' -// workloadProfileType: 'Consumption' -// } -// ] -// } -// } +// ========== Azure Kubernetes Service (AKS) ========== // +module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10.1' = { + name: take('avm.res.container-service.managed-cluster.aks-${solutionSuffix}', 64) + params: { + name: 'aks-${solutionSuffix}' + location: solutionLocation + tags: tags + enableTelemetry: enableTelemetry + kubernetesVersion: '1.30.4' + dnsPrefix: 'aks-${solutionSuffix}' + enableRBAC: true + disableLocalAccounts: false + managedIdentities: { + systemAssigned: true + // userAssignedResourceIds: [ + // userAssignedIdentity.outputs.resourceId + // ] + } + primaryAgentPoolProfiles: [ + { + name: 'agentpool' + vmSize: 'Standard_D4ds_v5' + count: 2 + osType: 'Linux' + mode: 'System' + type: 'VirtualMachineScaleSets' + } + ] + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + // WAF aligned configuration for Monitoring + monitoringWorkspaceResourceId: enableMonitoring ? logAnalyticsWorkspaceResourceId : null + // WAF aligned configuration for Private Networking + } +} // ========== Application Insights ========== // var applicationInsightsResourceName = 'appi-${solutionSuffix}' @@ -668,10 +709,10 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }] : null } } + /* Outputs */ - @description('Contains Solution Name.') output SOLUTION_NAME string = solutionSuffix diff --git a/infra/modules/container-registry.bicep b/infra/modules/container-registry.bicep new file mode 100644 index 00000000..9731ce61 --- /dev/null +++ b/infra/modules/container-registry.bicep @@ -0,0 +1,41 @@ +metadata name = 'Container Registry Module' +// AVM-compliant Azure Container Registry deployment + +@description('The name of the Azure Container Registry') +param acrName string + +@description('The location of the Azure Container Registry') +param location string + +@description('SKU for the Azure Container Registry') +param acrSku string = 'Basic' + +@description('Public network access setting for the Azure Container Registry') +param publicNetworkAccess string = 'Enabled' + +@description('Zone redundancy setting for the Azure Container Registry') +param zoneRedundancy string = 'Disabled' + +import { roleAssignmentType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType[]? + +@description('Tags to be applied to the Container Registry') +param tags object = {} + +module avmContainerRegistry 'br/public:avm/res/container-registry/registry:0.9.1' = { + name: acrName + params: { + name: acrName + location: location + acrSku: acrSku + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + roleAssignments: roleAssignments + tags: tags + } +} + +output name string = avmContainerRegistry.outputs.name +output resourceId string = avmContainerRegistry.outputs.resourceId +output loginServer string = avmContainerRegistry.outputs.loginServer From e86deb709834d36fba05791c07424a25b192bbbd Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Tue, 9 Sep 2025 21:58:23 +0530 Subject: [PATCH 08/29] feat: Added Changes for Deployment script to remove the deployment related steps --- Deployment/resourcedeployment.ps1 | 323 ++++++++++-------------------- infra/main.bicep | 74 +++---- 2 files changed, 143 insertions(+), 254 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 5fdeccd5..c069d9e4 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT license. #https://patorjk.com/software/taag @@ -152,18 +152,18 @@ function PromptForParameters { } } -# Prompt for parameters with kind messages -$params = PromptForParameters -tenantId $tenantId -subscriptionID $subscriptionID -environmentName $environmentName -resourceGroupName $resourceGroupName -location $location -modelLocation $modelLocation -email $email -# Assign the parameters to variables -$tenantId = $params.tenantId -$subscriptionID = $params.subscriptionID -$environmentName = $params.environmentName -$resourceGroupName = $params.resourceGroupName -$location = $params.location -$modelLocation = $params.modelLocation -$email = $params.email - -function LoginAzure([string]$subscriptionID) { +# # Prompt for parameters with kind messages +# $params = PromptForParameters -tenantId $tenantId -subscriptionID $subscriptionID -environmentName $environmentName -resourceGroupName $resourceGroupName -location $location -modelLocation $modelLocation -email $email +# # Assign the parameters to variables +# $tenantId = $params.tenantId +# $subscriptionID = $params.subscriptionID +# $environmentName = $params.environmentName +# $resourceGroupName = $params.resourceGroupName +# $location = $params.location +# $modelLocation = $params.modelLocation +# $email = $params.email + +function LoginAzure([string]$tenantId, [string]$subscriptionID) { Write-Host "Log in to Azure.....`r`n" -ForegroundColor Yellow if ($env:CI -eq "true"){ az login --service-principal ` @@ -188,127 +188,19 @@ function LoginAzure([string]$subscriptionID) { Write-Host "Switched subscription to '$subscriptionID' `r`n" -ForegroundColor Yellow } -function DeployAzureResources() { - Write-Host "Started Deploying Knowledge Mining Solution Accelerator Service Azure resources.....`r`n" -ForegroundColor Yellow +function DisplayResult([DeploymentResult]$displayResult) { + $resourcegroupName = $displayResult.ResourceGroupName + # $solutionPrefix = $displayResult.S - try { - # Load variables from .env file - if (Test-Path ".env") { - Get-Content ".env" | ForEach-Object { - if ($_ -match "^\s*([^#][^=]+)=(.+)$") { - $key = $matches[1].Trim() - $value = $matches[2].Trim() - Set-Variable -Name $key -Value $value -Scope Script - } - } - Write-Host "Loaded variables from .env file" -ForegroundColor Green - } - else { - Write-Host ".env file not found. Please create one." -ForegroundColor Red - failureBanner - exit 1 - } - - # Required values from .env - $envLocation = if ($env:RESOURCE_GROUP_LOCATION) { $env:RESOURCE_GROUP_LOCATION } else { "eastus" } - $environmentName = if ($env:AZURE_ENV_NAME) { $env:AZURE_ENV_NAME } else { "dev" } - $resourceGroupName = if ($env:RESOURCE_GROUP_NAME) { $env:RESOURCE_GROUP_NAME } else { "" } - - # Generate random deployment name - $randomNumber = Get-Random -Minimum 0 -Maximum 99999 - $randomNumberPadded = $randomNumber.ToString("D5") - $deploymentName = "KM_SA_Deployment$randomNumberPadded" - - if (-not $resourceGroupName -or $resourceGroupName -eq "") { - # Load abbreviation for RG prefix - $abbrs = Get-Content -Raw -Path "./abbreviations.json" | ConvertFrom-Json - if (-not $abbrs -or -not $abbrs.managementGovernance.resourceGroup) { - Write-Host "abbreviations.json is missing or malformed." - failureBanner - exit 1 - } - $rgPrefix = $abbrs.managementGovernance.resourceGroup - $resourceprefix_name = "kmgs" - - # Just generate RG name using prefix and random suffix - $resourceGroupName = "$rgPrefix$resourceprefix_name$randomNumberPadded" - - Write-Host "Generated Resource Group Name: $resourceGroupName" - az group create --name $resourceGroupName --location $envLocation --tags EnvironmentName=$environmentName TemplateName="DKM" | Out-Null - } - else { - $exists = az group exists --name $resourceGroupName | ConvertFrom-Json - if (-not $exists) { - Write-Host "Specified RG does not exist. Creating RG: $resourceGroupName" -ForegroundColor Yellow - az group create --name $resourceGroupName --location $envLocation --tags EnvironmentName=$environmentName TemplateName="DKM" | Out-Null - } - else { - az group update --name $resourceGroupName --set tags.EnvironmentName=$environmentName tags.TemplateName="DKM" | Out-Null - Write-Host "Using existing RG: $resourceGroupName" -ForegroundColor Green - } - } - - # Not taking values from main.bicep. - # Instead, you can deploy resources manually with az CLI using values from .env. - # Example: deploying storage account (if defined in .env) - if ($env:STORAGE_ACCOUNT_NAME) { - Write-Host "Deploying storage account: $($env:STORAGE_ACCOUNT_NAME)" -ForegroundColor Yellow - az storage account create ` - --name $env:STORAGE_ACCOUNT_NAME ` - --resource-group $resourceGroupName ` - --location $envLocation ` - --sku Standard_LRS ` - --kind StorageV2 | Out-Null - } - - # Example: deploying Cognitive Services (if defined in .env) - if ($env:COGNITIVE_ACCOUNT_NAME) { - Write-Host "Deploying Cognitive Services: $($env:COGNITIVE_ACCOUNT_NAME)" -ForegroundColor Yellow - az cognitiveservices account create ` - --name $env:COGNITIVE_ACCOUNT_NAME ` - --resource-group $resourceGroupName ` - --kind CognitiveServices ` - --sku S0 ` - --location $envLocation ` - --yes | Out-Null - } - - Write-Host "Deployment completed successfully using .env values" -ForegroundColor Green - - # Build JSON result - $result = @{ - ResourceGroupName = $resourceGroupName - DeploymentName = $deploymentName - Location = $envLocation - Environment = $environmentName - Timestamp = (Get-Date).ToString("s") - } - return ($result | ConvertTo-Json -Depth 3) - } - catch { - Write-Host "An error occurred during the deployment process:" -ForegroundColor Red - Write-Host $_.Exception.Message -ForegroundColor Red - Write-Host $_.InvocationInfo.PositionMessage -ForegroundColor Red - Write-Host $_.ScriptStackTrace -ForegroundColor Red - failureBanner - exit 1 - } -} - - -function DisplayResult([pscustomobject]$jsonString) { - $resourcegroupName = $jsonString.properties.outputs.gs_resourcegroup_name.value - $solutionPrefix = $jsonString.properties.outputs.gs_solution_prefix.value + $storageAccountName = $displayResult.StorageAccountName + $azsearchServiceName = $displayResult.AzSearchServiceName + $aksName = $displayResult.AksName - $storageAccountName = $jsonString.properties.outputs.gs_storageaccount_name.value - $azsearchServiceName = $jsonString.properties.outputs.gs_azsearch_name.value - $aksName = $jsonString.properties.outputs.gs_aks_name.value - - $containerRegistryName = $jsonString.properties.outputs.gs_containerregistry_name.value - $azcognitiveserviceName = $jsonString.properties.outputs.gs_azcognitiveservice_name.value - $azopenaiServiceName = $jsonString.properties.outputs.gs_openaiservice_name.value - $azcosmosDBName = $jsonString.properties.outputs.gs_cosmosdb_name.value - $azappConfigEndpoint = $jsonString.properties.outputs.gs_appconfig_endpoint.value + $containerRegistryName = $displayResult.ContainerRegistryName + $azcognitiveserviceName = $displayResult.AzCognitiveServiceName + $azopenaiServiceName = $displayResult.AzOpenAIServiceName + $azcosmosDBName = $displayResult.AzCosmosDBName + $azappConfigEndpoint = $displayResult.AzAppConfigEndpoint # Display banner Write-Host "********************************************************************************" -ForegroundColor Blue @@ -385,6 +277,8 @@ function Show-Banner { } class DeploymentResult { + [string]$TenantId + [string]$SubscriptionId [string]$ResourceGroupName [string]$ResourceGroupId [string]$StorageAccountName @@ -412,6 +306,8 @@ class DeploymentResult { DeploymentResult() { # Resource Group + $this.TenantId = "" + $this.SubscriptionId = "" $this.ResourceGroupName = "" $this.ResourceGroupId = "" # Storage Account @@ -450,41 +346,58 @@ class DeploymentResult { } - [void]MapResult([pscustomobject]$jsonString) { + [hashtable]GetAzdEnvValues() { + $envVars = @{} + + azd env get-values | ForEach-Object { + if ($_ -match "^\s*([^#][^=]+?)\s*=\s*(.+)\s*$") { + $key = $matches[1].Trim() + $value = $matches[2].Trim() + $envVars[$key] = $value + } + } + + return $envVars + } + + [void]MapResult() { + + $envValues = Get-AzdEnvValues + $this.TenantId = $envValues["AZURE_TENANT_ID"] + $this.SubscriptionId = $envValues["AZURE_SUBSCRIPTION_ID"] + # Add your code here - $this.ResourceGroupName = $jsonString.properties.outputs.gs_resourcegroup_name.value - $this.ResourceGroupId = $jsonString.properties.outputs.gs_resourcegroup_id.value + $this.ResourceGroupName = $envValues["AZURE_RESOURCE_GROUP"] + $this.ResourceGroupId = $envValues["AZURE_RESOURCE_GROUP_ID"] # Storage Account - $this.StorageAccountName = $jsonString.properties.outputs.gs_storageaccount_name.value + $this.StorageAccountName = $envValues["STORAGE_ACCOUNT_NAME"] # Azure Search - $this.AzSearchServiceName = $jsonString.properties.outputs.gs_azsearch_name.value + $this.AzSearchServiceName = $envValues["AZURE_SEARCH_SERVICE_NAME"] $this.AzSearchServicEndpoint = "https://$($this.AzSearchServiceName).search.windows.net" # Azure Kubernetes - $this.AksName = $jsonString.properties.outputs.gs_aks_name.value - $this.AksMid = $jsonString.properties.outputs.gs_aks_serviceprincipal_id.value + $this.AksName = $envValues["AZURE_AKS_NAME"] + $this.AksMid = $envValues["AZURE_AKS_MI_ID"] # Azure Container Registry - $this.AzContainerRegistryName = $jsonString.properties.outputs.gs_containerregistry_name.value + $this.AzContainerRegistryName = $envValues["AZURE_CONTAINER_REGISTRY_NAME"] # Azure Cognitive Service - Azure AI Document Intelligence Service - $this.AzCognitiveServiceName = $jsonString.properties.outputs.gs_azcognitiveservice_name.value - $this.AzCognitiveServiceEndpoint = $jsonString.properties.outputs.gs_azcognitiveservice_endpoint.value + $this.AzCognitiveServiceName = $envValues["AZURE_COGNITIVE_SERVICE_NAME"] + $this.AzCognitiveServiceEndpoint = $envValues["AZURE_COGNITIVE_SERVICE_ENDPOINT"] # Azure Open AI Service - $this.AzOpenAiServiceName = $jsonString.properties.outputs.gs_openaiservice_name.value - $this.AzOpenAiServiceEndpoint = $jsonString.properties.outputs.gs_openaiservice_endpoint.value - $this.AzOpenAiServiceKey = $jsonString.properties.outputs.gs_openaiservice_key.value + $this.AzOpenAiServiceName = $envValues["AZURE_OPENAI_SERVICE_NAME"] + $this.AzOpenAiServiceEndpoint = $envValues["AZURE_OPENAI_SERVICE_ENDPOINT"] # Azure Cosmos DB - $this.AzCosmosDBName = $jsonString.properties.outputs.gs_cosmosdb_name.value + $this.AzCosmosDBName = $envValues["AZURE_COSMOSDB_NAME"] # Azure Open AI Service Models - $this.AzGPT4oModelName = $jsonString.properties.outputs.gs_openaiservicemodels_gpt4o_model_name.value - $this.AzGPT4oModelId = $jsonString.properties.outputs.gs_openaiservicemodels_gpt4o_model_id.value - $this.AzGPTEmbeddingModelName = $jsonString.properties.outputs.gs_openaiservicemodels_text_embedding_model_name.value - $this.AzGPTEmbeddingModelId = $jsonString.properties.outputs.gs_openaiservicemodels_text_embedding_model_id.value + $this.AzGPT4oModelName = $envValues["AZ_GPT4O_MODEL_NAME"] + $this.AzGPT4oModelId = $envValues["AZ_GPT4O_MODEL_ID"] + $this.AzGPTEmbeddingModelName = $envValues["AZ_GPT_EMBEDDING_MODEL_NAME"] + $this.AzGPTEmbeddingModelId = $envValues["AZ_GPT_EMBEDDING_MODEL_ID"] # Azure App Configuration - $this.AzAppConfigEndpoint = $jsonString.properties.outputs.gs_appconfig_endpoint.value - # App Config Name - $this.AzAppConfigName = "appconfig-" + $jsonString.properties.outputs.gs_solution_prefix.value - + $this.AzAppConfigEndpoint = $envValues["AZURE_APP_CONFIG_ENDPOINT"] + # App Config Name + $this.AzAppConfigName = $envValues["AZURE_APP_CONFIG_NAME"] } } @@ -525,76 +438,61 @@ try { Show-Banner -Title "Step 1 : Deploy Azure resources" ############################################################### $deploymentResult = [DeploymentResult]::new() - LoginAzure($subscriptionID) - # Deploy Azure Resources using .env values - Write-Host "Deploying Azure resources from .env configuration.....`r`n" -ForegroundColor Yellow - - $resultJson = DeployAzureResources + + # Deploy Azure Resources + Write-Host "Retrieving the deployment details.....`r`n" -ForegroundColor Yellow - # Convert JSON string back to object - $resultObj = $resultJson | ConvertFrom-Json - # Ensure ResourceGroupName is set correctly - $deploymentResult.ResourceGroupName = $resultObj.ResourceGroupName # Map the deployment result to DeploymentResult object - $deploymentResult.MapResult($resultJson) + $deploymentResult.MapResult() # Display the deployment result - DisplayResult $resultObj + DisplayResult(deploymentResult) + + LoginAzure($deploymentResult.TenantId, $deploymentResult.SubscriptionId) ############################################################### # Step 2 : Get Secrets from Azure resources Show-Banner -Title "Step 2 : Get Secrets from Azure resources" ############################################################### - - # Pull values from environment variables (or fall back to defaults) - # Build deployment result object from .env values - $deploymentResult = [PSCustomObject]@{ - ResourceGroupName = $env:RESOURCE_GROUP_NAME - StorageAccountName = $env:STORAGE_ACCOUNT_NAME - AzCosmosDBName = $env:AZ_COSMOSDB_NAME - AzCognitiveServiceName = $env:AZ_COGNITIVE_SERVICE_NAME - AzSearchServiceName = $env:AZ_SEARCH_SERVICE_NAME - AzOpenAiServiceName = $env:AZ_OPENAI_SERVICE_NAME - AzOpenAiServiceEndpoint = $env:AZ_OPENAI_SERVICE_ENDPOINT - AzSearchServiceEndpoint = $env:AZ_SEARCH_SERVICE_ENDPOINT - AzCognitiveServiceEndpoint = $env:AZ_COGNITIVE_SERVICE_ENDPOINT - AzGPT4oModelId = $env:AZ_GPT4O_MODEL_ID - AzGPT4oModelName = $env:AZ_GPT4O_MODEL_NAME - AzGPTEmbeddingModelName = $env:AZ_GPT_EMBEDDING_MODEL_NAME - } - - # Validate Storage Account Name + # Validate if the Storage Account Name is empty or null ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.StorageAccountName -variableName "Storage Account Name" # Get the storage account key - $storageAccountKey = az storage account keys list ` - --account-name $deploymentResult.StorageAccountName ` - --resource-group $deploymentResult.ResourceGroupName ` - --query "[0].value" -o tsv - - # Validate storage account key - ValidateVariableIsNullOrEmpty -variableValue $storageAccountKey -variableName "Storage account key" - - # Construct connection string manually + $storageAccountKey = az storage account keys list --account-name $deploymentResult.StorageAccountName --resource-group $deploymentResult.ResourceGroupName --query "[0].value" -o tsv + + # Validate if the storage account key is empty or null + ValidateVariableIsNullOrEmpty -variableValue $storageAccountKey -variableName "Storage account key" + + ## Construct the connection string manually $storageAccountConnectionString = "DefaultEndpointsProtocol=https;AccountName=$($deploymentResult.StorageAccountName);AccountKey=$storageAccountKey;EndpointSuffix=core.windows.net" - - # Validate storage account connection string + # Validate if the Storage Account Connection String is empty or null ValidateVariableIsNullOrEmpty -variableValue $storageAccountConnectionString -variableName "Storage Account Connection String" - - # Assign connection string - $deploymentResult | Add-Member -NotePropertyName StorageAccountConnectionString -NotePropertyValue $storageAccountConnectionString -Force - - # Validate core service names - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupName -variableName "Resource group name" - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCosmosDBName -variableName "Az Cosmos DB name" - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCognitiveServiceName -variableName "Az Cognitive Service name" - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzSearchServiceName -variableName "Az Search Service name" - ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzOpenAiServiceName -variableName "Az OpenAI Service name" - - # Retrieve secrets - $deploymentResult | Add-Member -NotePropertyName AzCosmosDBConnectionString -NotePropertyValue (az cosmosdb keys list --name $deploymentResult.AzCosmosDBName --resource-group $deploymentResult.ResourceGroupName --type connection-strings --query "connectionStrings[0].connectionString" -o tsv) -Force - $deploymentResult | Add-Member -NotePropertyName AzCognitiveServiceKey -NotePropertyValue (az cognitiveservices account keys list --name $deploymentResult.AzCognitiveServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv) -Force - $deploymentResult | Add-Member -NotePropertyName AzSearchAdminKey -NotePropertyValue (az search admin-key show --service-name $deploymentResult.AzSearchServiceName --resource-group $deploymentResult.ResourceGroupName --query "primaryKey" -o tsv) -Force - $deploymentResult | Add-Member -NotePropertyName AzOpenAiServiceKey -NotePropertyValue (az cognitiveservices account keys list --name $deploymentResult.AzOpenAiServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv) -Force + + ## Assign the connection string to the deployment result object + $deploymentResult.StorageAccountConnectionString = $storageAccountConnectionString + + # Check if ResourceGroupName is valid + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupName -variableName "Resource group name" + + # Check if AzCosmosDBName is valid + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCosmosDBName -variableName "Az Cosmos DB name" + + # Check if AzCognitiveServiceName is valid + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzCognitiveServiceName -variableName "Az Cognitive Service name" + + # Check if AzSearchServiceName is valid + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzSearchServiceName -variableName "Az Search Service name" + + # Check if AzOpenAiServiceName is valid + ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzOpenAiServiceName -variableName "Az OpenAI Service name" + + # Get MongoDB connection string + $deploymentResult.AzCosmosDBConnectionString = az cosmosdb keys list --name $deploymentResult.AzCosmosDBName --resource-group $deploymentResult.ResourceGroupName --type connection-strings --query "connectionStrings[0].connectionString" -o tsv + # Get Azure Cognitive Service API Key + $deploymentResult.AzCognitiveServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzCognitiveServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv + # Get Azure Search Service Admin Key + $deploymentResult.AzSearchAdminKey = az search admin-key show --service-name $deploymentResult.AzSearchServiceName --resource-group $deploymentResult.ResourceGroupName --query "primaryKey" -o tsv + # Get Azure Open AI Service API Key + $deploymentResult.AzOpenAiServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzOpenAiServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv Write-Host "Secrets have been retrieved successfully." -ForegroundColor Green @@ -1086,5 +984,4 @@ catch { Write-Host "Error details:" -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red Write-Host $_.Exception.StackTrace -ForegroundColor Red -} - +} \ No newline at end of file diff --git a/infra/main.bicep b/infra/main.bicep index 171980ab..e510a89b 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -76,27 +76,6 @@ param enableScalability bool = false @description('Optional. Enable purge protection for the Key Vault') param enablePurgeProtection bool = false -@description('Optional. Cognitive Account Name') -param cognitiveAccountName string = '' - -@description('Optional. Azure Search Service Name') -param azureSearchServiceName string = '' - -@description('Optional. Azure Cosmos DB Name') -param azureCosmosDbName string = '' - -@description('Optional. Azure Cognitive Service Name') -param azureCognitiveServiceName string = '' - -@description('Optional. Contains Azure OpenAI Service Endpoint.') -param azureOpenAiServiceEndpoint string = '' - -@description('Optional. Contains Azure Search Service Endpoint.') -param azureSearchServiceEndpoint string = '' - -@description('Optional. Contains Azure Cognitive Service Endpoint.') -param azureCognitiveServiceEndpoint string = '' - @minLength(1) @description('Optional. Name of the Text Embedding model to deploy:') @allowed([ @@ -631,7 +610,7 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { // ========== Cognitive Services - Document Intellignece module ========== // var docIntelAccountName = 'di-${solutionSuffix}' -module docIntel 'br/public:avm/res/cognitive-services/account:0.13.2' = { +module documentIntelligence 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${docIntelAccountName}', 64) params: { name: docIntelAccountName @@ -744,6 +723,8 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en /* Outputs */ +output AZURE_TENANT_ID string = subscription().tenantId + @description('Contains Solution Name.') output SOLUTION_NAME string = solutionSuffix @@ -753,45 +734,56 @@ output RESOURCE_GROUP_NAME string = resourceGroup().name @description('Contains Resource Group Location.') output RESOURCE_GROUP_LOCATION string = solutionLocation -@description('Contains Resource Group Name.') -output STORAGE_ACCOUNT_NAME string = storageAccountName +output AZURE_RESOURCE_GROUP_ID string = resourceGroup().id -@description('Contains Cognitive Account Name.') -output COGNITIVE_ACCOUNT_NAME string = cognitiveAccountName +output AZURE_APP_CONFIG_NAME string = avmAppConfig.outputs.name + +output AZURE_APP_CONFIG_ENDPOINT string = avmAppConfig.outputs.endpoint + +@description('Contains Resource Group Name.') +output STORAGE_ACCOUNT_NAME string = avmStorageAccount.outputs.name @description('Contains Cosmos DB Name.') -output AZ_COSMOSDB_NAME string = azureCosmosDbName +output AZURE_COSMOSDB_NAME string = avmCosmosDB.outputs.name @description('Contains Cognitive Service Name.') -output AZ_COGNITIVE_SERVICE_NAME string = azureCognitiveServiceName +output AZURE_COGNITIVE_SERVICE_NAME string = documentIntelligence.outputs.name + +@description('Contains Azure Cognitive Service Endpoint.') +output AZURE_COGNITIVE_SERVICE_ENDPOINT string = documentIntelligence.outputs.endpoint + +@description('Contains Azure Search Service Name.') +output AZURE_SEARCH_SERVICE_NAME string = avmSearchSearchServices.outputs.name + +@description('Contains Azure Search Service Name.') +output AZURE_AKS_NAME string = managedCluster.outputs.name @description('Contains Azure Search Service Name.') -output AZ_SEARCH_SERVICE_NAME string = azureSearchServiceName +output AZURE_AKS_MI_ID string = managedCluster.outputs.systemAssignedMIPrincipalId + +@description('Contains Azure Search Service Name.') +output AZURE_CONTAINER_REGISTRY_NAME string = avmContainerRegistry.outputs.name @description('Contains Azure OpenAI Search Service Name.') -output AZ_OPENAI_SERVICE_NAME string = openAiAccountName +output AZURE_OPENAI_SERVICE_NAME string = avmOpenAi.outputs.name @description('Contains Azure OpenAI Service Endpoint.') -output AZ_OPENAI_SERVICE_ENDPOINT string = azureOpenAiServiceEndpoint +output AZURE_OPENAI_SERVICE_ENDPOINT string = avmOpenAi.outputs.endpoint @description('Contains Azure Search Service Endpoint.') -output AZ_SEARCH_SERVICE_ENDPOINT string = azureSearchServiceEndpoint - -@description('Contains Azure Cognitive Service Endpoint.') -output AZ_COGNITIVE_SERVICE_ENDPOINT string = azureCognitiveServiceEndpoint +output AZ_SEARCH_SERVICE_ENDPOINT string = avmSearchSearchServices.outputs.name @description('Contains Azure GPT40 Model ID.') -output AZ_GPT4O_MODEL_ID string = azureSearchServiceName +output AZ_GPT4O_MODEL_ID string = chatGpt.deploymentName @description('Contains Azure OpenAI embedding model name.') -output AZ_GPT4O_MODEL_NAME string = azureGpt40ModelName +output AZ_GPT4O_MODEL_NAME string = chatGpt.modelName @description('Contains Azure OpenAI embedding model name.') -output AZ_GPT_EMBEDDING_MODEL_NAME string = embeddingModel - -@description('Contains Azure OpenAI embedding model capacity.') -output AZ_OPENAI_EMBEDDING_MODEL_CAPACITY int = embeddingDeploymentCapacity +output AZ_GPT_EMBEDDING_MODEL_NAME string = embedding.modelName +@description('Contains Azure OpenAI embedding model name.') +output AZ_GPT_EMBEDDING_MODEL_ID string = embedding.deploymentName // @description('The FQDN of the frontend web app service.') // output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN From fd4d917f300bb46e748f57cfc94fa03ccc387402 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Tue, 9 Sep 2025 23:42:40 +0530 Subject: [PATCH 09/29] fieat: Updated the power shell script --- Deployment/resourcedeployment.ps1 | 19 +++++++------------ infra/main.bicep | 7 ++++--- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index c069d9e4..93128c8a 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. +# Copyright (c) Microsoft Corporation. # Licensed under the MIT license. #https://patorjk.com/software/taag @@ -346,23 +346,18 @@ class DeploymentResult { } - [hashtable]GetAzdEnvValues() { - $envVars = @{} + [void]MapResult() { + $envValues = @{} azd env get-values | ForEach-Object { if ($_ -match "^\s*([^#][^=]+?)\s*=\s*(.+)\s*$") { $key = $matches[1].Trim() $value = $matches[2].Trim() - $envVars[$key] = $value + $envValues[$key] = $value.Trim('\"') } } - return $envVars - } - - [void]MapResult() { - - $envValues = Get-AzdEnvValues + # $envValues = Get-AzdEnvValues $this.TenantId = $envValues["AZURE_TENANT_ID"] $this.SubscriptionId = $envValues["AZURE_SUBSCRIPTION_ID"] @@ -445,9 +440,9 @@ try { # Map the deployment result to DeploymentResult object $deploymentResult.MapResult() # Display the deployment result - DisplayResult(deploymentResult) + DisplayResult($deploymentResult) - LoginAzure($deploymentResult.TenantId, $deploymentResult.SubscriptionId) + LoginAzure $deploymentResult.TenantId $deploymentResult.SubscriptionId ############################################################### # Step 2 : Get Secrets from Azure resources diff --git a/infra/main.bicep b/infra/main.bicep index e510a89b..af372b41 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -48,7 +48,7 @@ param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = param enableTelemetry bool = true @description('Optional. Enable private networking for applicable resources, aligned with the WAF recommendations. Defaults to false.') -param enablePrivateNetworking bool = true +param enablePrivateNetworking bool = false @description('Optional: Existing Log Analytics Workspace Resource ID') param existingLogAnalyticsWorkspaceId string = '' @@ -326,7 +326,7 @@ module avmContainerRegistry './modules/container-registry.bicep' = { // ========== Cosmos Database for Mongo DB ========== // module avmCosmosDB 'br/public:avm/res/document-db/database-account:0.15.0' = { - name: 'cosmos-${solutionSuffix}' + name: take('avm.res.cosmos-${solutionSuffix}', 64) params: { name: 'cosmos-${solutionSuffix}' location: solutionLocation @@ -382,7 +382,7 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 name: take('avm.res.app-configuration.configuration-store.${appConfigName}', 64) params: { name: appConfigName - location: location + location: solutionLocation managedIdentities: { systemAssigned: true } sku: 'Standard' enableTelemetry: enableTelemetry @@ -672,6 +672,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10. dnsPrefix: 'aks-${solutionSuffix}' enableRBAC: true disableLocalAccounts: false + publicNetworkAccess: 'Enabled' managedIdentities: { systemAssigned: true // userAssignedResourceIds: [ From ec2a094eefdbcae6a828f000c7057d1b8083d1f5 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 10 Sep 2025 01:58:02 +0530 Subject: [PATCH 10/29] fix: Added DIsable local Auth for Now Waf deployment to work --- Deployment/resourcedeployment.ps1 | 10 +- infra/main.bicep | 152 ++++++++++++++++++++++++++++-- 2 files changed, 151 insertions(+), 11 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 93128c8a..4658ef94 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -713,7 +713,7 @@ try { ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupName -variableName "Resource group name" ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AksName -variableName "AKS cluster name" - + # Get vmss resource group name $vmssResourceGroupName = $(az aks show --resource-group $deploymentResult.ResourceGroupName --name $deploymentResult.AksName --query nodeResourceGroup --output tsv) @@ -728,7 +728,7 @@ try { # Create System Assigned Managed Identity $systemAssignedIdentity = $(az vmss identity assign --resource-group $vmssResourceGroupName --name $vmssName --query systemAssignedIdentity --output tsv) - + # Validate if System Assigned Identity is null or empty ValidateVariableIsNullOrEmpty -variableValue $systemAssignedIdentity -variableName "System-assigned managed identity" @@ -737,14 +737,14 @@ try { # Assign the role for aks system assigned managed identity to App Configuration Data Reader role with the scope of Resourcegroup az role assignment create --assignee $systemAssignedIdentity --role "App Configuration Data Reader" --scope $deploymentResult.ResourceGroupId - + # Assign the role for aks system assigned managed identity to Azure blob storage Data Contributor role with the scope of Storage Account Write-Host "Assign the role for aks system assigned managed identity to App Storage Blob Data Contributor role" -ForegroundColor Green - az role assignment create --assignee $systemAssignedIdentity --role "Storage Blob Data Contributor" --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($deploymentResult.StorageAccountName)" + az role assignment create --assignee $systemAssignedIdentity --role "Storage Blob Data Contributor" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($deploymentResult.StorageAccountName)" # Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account Write-Host "Assign the role for aks system assigned managed identity to App Storage Queue Data Contributor role" -ForegroundColor Green - az role assignment create --assignee $systemAssignedIdentity --role "Storage Queue Data Contributor" --scope "/subscriptions/$subscriptionID/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($deploymentResult.StorageAccountName)" + az role assignment create --assignee $systemAssignedIdentity --role "Storage Queue Data Contributor" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($deploymentResult.StorageAccountName)" # 8. Update aks nodepools to updated new role try { diff --git a/infra/main.bicep b/infra/main.bicep index af372b41..c8a337d9 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -39,7 +39,7 @@ For model limits specific to your region, refer to the documentation at https:// ''') @minValue(1) @maxValue(40) -param embeddingDeploymentCapacity int = 30 +param embeddingDeploymentCapacity int = 1 @description('Optional. The tags to apply to all deployed Azure resources.') param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} @@ -119,15 +119,15 @@ var useExistingLogAnalytics = !empty(existingLogAnalyticsWorkspaceId) var chatGpt = { modelName: 'gpt-4.1-mini' - deploymentName: 'chat' + deploymentName: 'gpt-4.1-mini' deploymentVersion: '2025-04-14' deploymentCapacity: chatGptDeploymentCapacity } var embedding = { - modelName: 'text-embedding-ada-002' - deploymentName: 'embedding' - deploymentVersion: '2' + modelName: 'text-embedding-3-large' + deploymentName: 'text-embedding-3-large' + deploymentVersion: '1' deploymentCapacity: embeddingDeploymentCapacity } @@ -387,7 +387,7 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 sku: 'Standard' enableTelemetry: enableTelemetry tags: tags - + disableLocalAuth: false roleAssignments: [ { principalId: userAssignedIdentity.outputs.principalId @@ -396,6 +396,144 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 } ] + // keyValues: [ + // { + // name: 'Application:AIServices:GPT-4o-mini:Endpoint' + // value: avmOpenAi.outputs.endpoint + // } + // { + // name: 'Application:AIServices:GPT-4o-mini:Key' + // value: '' // Todo: avmOpenAi.outputs. + // } + // { + // name: 'Application:AIServices:GPT-4o-mini:ModelName' + // value: chatGpt.modelName + // } + // { + // name: 'Application:AIServices:GPT-4o:Endpoint' + // value: avmOpenAi.outputs.endpoint + // } + // { + // name: 'Application:AIServices:GPT-4o:Key' + // value: '{gpt-4o-mini-apikey}' + // } + // { + // name: 'Application:AIServices:GPT-4o:ModelName' + // value: chatGpt.modelName + // } + // { + // name: 'Application:AIServices:TextEmbedding:Endpoint' + // value: avmOpenAi.outputs.endpoint + // } + // { + // name: 'Application:AIServices:TextEmbedding:Key' + // value: '{textembedding-key}' + // } + // { + // name: 'Application:AIServices:TextEmbedding:ModelName' + // value: embedding.modelName + // } + // { + // name: 'Application:Services:CognitiveService:DocumentIntelligence:APIKey' + // value: documentIntelligence.outputs.exportedSecrets.primaryAdminKey + // } + // { + // name: 'Application:Services:CognitiveService:DocumentIntelligence:Endpoint' + // value: documentIntelligence.outputs.endpoint + // } + // { + // name: 'Application:Services:KernelMemory:Endpoint' + // value: 'http://kernelmemory-service' + // } + // { + // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Collection' + // value: 'ChatHistory' + // } + // { + // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Database' + // value: 'DPS' + // } + // { + // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Collection' + // value: 'Documents' + // } + // { + // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Database' + // value: 'DPS' + // } + // { + // name: 'Application:Services:PersistentStorage:CosmosMongo:ConnectionString' + // value: avmCosmosDB.outputs.primaryReadWriteConnectionString + // } + // { + // name: 'Application:Services:AzureAISearch:APIKey' + // value: avmSearchSearchServices.outputs.exportedSecrets.primaryAdminKey + // } + // { + // name: 'Application:Services:AzureAISearch:Endpoint' + // value: 'https://${avmSearchSearchServices.outputs.name}.search.windows.net' + // } + // { + // name: 'KernelMemory:Services:AzureAIDocIntel:APIKey' + // value: documentIntelligence.outputs.exportedSecrets.primaryAdminKey + // } + // { + // name: 'KernelMemory:Services:AzureAIDocIntel:Endpoint' + // value: documentIntelligence.outputs.endpoint + // } + // { + // name: 'KernelMemory:Services:AzureAISearch:APIKey' + // value: avmSearchSearchServices.outputs.exportedSecrets.primaryAdminKey + // } + // { + // name: 'KernelMemory:Services:AzureAISearch:Endpoint' + // value: 'https://${avmSearchSearchServices.outputs.name}.search.windows.net' + // } + // { + // name: 'KernelMemory:Services:AzureBlobs:Account' + // value: avmStorageAccount.outputs.name + // } + // { + // name: 'KernelMemory:Services:AzureBlobs:ConnectionString' + // value: avmStorageAccount.outputs.primaryConnectionString + // } + // { + // name: 'KernelMemory:Services:AzureBlobs:Container' + // value: 'smemory' + // } + // { + // name: 'KernelMemory:Services:AzureOpenAIEmbedding:APIKey' + // value: '{azureopenaiembedding-apikey}' + // } + // { + // name: 'KernelMemory:Services:AzureOpenAIEmbedding:Deployment' + // value: embedding.deploymentName + // } + // { + // name: 'KernelMemory:Services:AzureOpenAIEmbedding:Endpoint' + // value: avmOpenAi.outputs.endpoint + // } + // { + // name: 'KernelMemory:Services:AzureOpenAIText:APIKey' + // value: '{azureopenaitext-apikey}' + // } + // { + // name: 'KernelMemory:Services:AzureOpenAIText:Deployment' + // value: chatGpt.deploymentName + // } + // { + // name: 'KernelMemory:Services:AzureOpenAIText:Endpoint' + // value: avmOpenAi.outputs.endpoint + // } + // { + // name: 'KernelMemory:Services:AzureQueues:Account' + // value: avmStorageAccount.outputs.name + // } + // { + // name: 'KernelMemory:Services:AzureQueues:ConnectionString' + // value: avmStorageAccount.outputs.primaryConnectionString + // } + // ] // WAF aligned networking publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' privateEndpoints: enablePrivateNetworking @@ -560,6 +698,7 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { tags: tags enableTelemetry: enableTelemetry customSubDomainName: openAiAccountName + disableLocalAuth: false managedIdentities: { systemAssigned: true } @@ -619,6 +758,7 @@ module documentIntelligence 'br/public:avm/res/cognitive-services/account:0.13.2 tags: tags sku: 'S0' customSubDomainName: docIntelAccountName + disableLocalAuth: false managedIdentities: { systemAssigned: true } From 471381bdb58c319929c68509ab8a0c65c768fac5 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 10 Sep 2025 03:52:42 +0530 Subject: [PATCH 11/29] fix: Added prmopt for Email Ifd --- Deployment/resourcedeployment.ps1 | 68 ++----------------------------- 1 file changed, 4 insertions(+), 64 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 4658ef94..5b343cc9 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -73,13 +73,7 @@ function ValidateVariableIsNullOrEmpty { # Function to prompt for parameters with kind messages function PromptForParameters { param( - [string]$tenantId, - [string]$subscriptionID, - [string]$environmentName, - [string]$resourceGroupName, - [string]$location, - [string]$modelLocation, - [string]$email + [string]$email ) Clear-Host @@ -88,72 +82,18 @@ function PromptForParameters { startBanner - $availableRegions = @( - 'EastUS', 'EastUS2', 'WestUS', 'WestUS2', 'WestUS3', 'CentralUS', 'NorthCentralUS', 'SouthCentralUS', - 'WestEurope', 'NorthEurope', 'SoutheastAsia', 'EastAsia', 'JapanEast', 'JapanWest', - 'AustraliaEast', 'AustraliaSoutheast', 'CentralIndia', 'SouthIndia', 'CanadaCentral', - 'CanadaEast', 'UKSouth', 'UKWest', 'FranceCentral', 'FranceSouth', 'KoreaCentral', - 'KoreaSouth', 'GermanyWestCentral', 'GermanyNorth', 'NorwayWest', 'NorwayEast', - 'SwitzerlandNorth', 'SwitzerlandWest', 'UAENorth', 'UAECentral', 'SouthAfricaNorth', - 'SouthAfricaWest', 'BrazilSouth', 'BrazilSoutheast', 'QatarCentral', 'ChinaNorth', - 'ChinaEast', 'ChinaNorth2', 'ChinaEast2' - ) - - $availableModelRegions = @( - 'EastUS', 'EastUS2', 'SwedenCentral', 'WestUS3' - ) - - if (-not $tenantId) { - Write-Host "Please enter your Azure tenant ID" -ForegroundColor Cyan - $tenantId = Read-Host -Prompt '> ' - } - if (-not $subscriptionID) { - Write-Host "Please enter your Azure subscription ID to deploy your resources" -ForegroundColor Cyan - $subscriptionID = Read-Host -Prompt '> ' - } - - if (-not $environmentName) { - Write-Host "Please enter Environment name" -ForegroundColor Cyan - $environmentName = Read-Host -Prompt '> ' - } - - if (-not $resourceGroupName) { - Write-Host "Please enter your Azure Resource Group Name to deploy your resources (leave blank to auto-generate one)" -ForegroundColor Cyan - $resourceGroupName = Read-Host -Prompt '> ' - } - - if (-not $location) { - Write-Host "Please enter the Azure Data Center Region to deploy your resources" -ForegroundColor Cyan - Write-Host "Available regions are:" -ForegroundColor Cyan - Write-Host ($availableRegions -join ', ') -ForegroundColor Yellow - $location = Read-Host -Prompt '> ' - } - - if (-not $modelLocation) { - Write-Host "Please enter the Azure Data Center Region to deploy your GPT model" -ForegroundColor Cyan - Write-Host "Available regions are:" -ForegroundColor Cyan - Write-Host ($availableModelRegions -join ', ') -ForegroundColor Yellow - $modelLocation = Read-Host -Prompt '> ' - } - if (-not $email) { Write-Host "Please enter your email address for certificate management" -ForegroundColor Cyan $email = Read-Host -Prompt '> ' } return @{ - tenantId = $tenantId - subscriptionID = $subscriptionID - environmentName = $environmentName - resourceGroupName = $resourceGroupName - location = $location - modelLocation = $modelLocation email = $email } } -# # Prompt for parameters with kind messages -# $params = PromptForParameters -tenantId $tenantId -subscriptionID $subscriptionID -environmentName $environmentName -resourceGroupName $resourceGroupName -location $location -modelLocation $modelLocation -email $email +# Prompt for parameters with kind messages + $params = PromptForParameters -email $email # # Assign the parameters to variables # $tenantId = $params.tenantId # $subscriptionID = $params.subscriptionID @@ -161,7 +101,7 @@ function PromptForParameters { # $resourceGroupName = $params.resourceGroupName # $location = $params.location # $modelLocation = $params.modelLocation -# $email = $params.email +$email = $params.email function LoginAzure([string]$tenantId, [string]$subscriptionID) { Write-Host "Log in to Azure.....`r`n" -ForegroundColor Yellow From 090fc7a34c7b64c67a7cb9e639c99eddc7a14e1e Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 10 Sep 2025 04:33:19 +0530 Subject: [PATCH 12/29] fix: Removed unwanted keys from AppConfig --- infra/main.bicep | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index c8a337d9..9e6ad805 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -410,38 +410,6 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 // value: chatGpt.modelName // } // { - // name: 'Application:AIServices:GPT-4o:Endpoint' - // value: avmOpenAi.outputs.endpoint - // } - // { - // name: 'Application:AIServices:GPT-4o:Key' - // value: '{gpt-4o-mini-apikey}' - // } - // { - // name: 'Application:AIServices:GPT-4o:ModelName' - // value: chatGpt.modelName - // } - // { - // name: 'Application:AIServices:TextEmbedding:Endpoint' - // value: avmOpenAi.outputs.endpoint - // } - // { - // name: 'Application:AIServices:TextEmbedding:Key' - // value: '{textembedding-key}' - // } - // { - // name: 'Application:AIServices:TextEmbedding:ModelName' - // value: embedding.modelName - // } - // { - // name: 'Application:Services:CognitiveService:DocumentIntelligence:APIKey' - // value: documentIntelligence.outputs.exportedSecrets.primaryAdminKey - // } - // { - // name: 'Application:Services:CognitiveService:DocumentIntelligence:Endpoint' - // value: documentIntelligence.outputs.endpoint - // } - // { // name: 'Application:Services:KernelMemory:Endpoint' // value: 'http://kernelmemory-service' // } From c0e78db2b0244a2426527276d187d2bcd82e1836 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 10 Sep 2025 07:49:14 +0530 Subject: [PATCH 13/29] feat: Implemented Identity based Authentication --- .../ServiceDependencies.cs | 5 +- .../Microsoft.GS.DPS/Microsoft.GS.DPS.csproj | 1 + .../Storage/AISearch/TagUpdater.cs | 5 +- .../DataFormats/Image/ImageContextDecoder.cs | 3 +- .../DataFormats/Pdf/PdfMarkdownDecoder.cs | 4 +- .../Core/Handlers/KeywordExtractingHandler.cs | 3 +- Deployment/resourcedeployment.ps1 | 17 ++ infra/main.bicep | 202 ++++++++---------- 8 files changed, 125 insertions(+), 115 deletions(-) diff --git a/App/backend-api/Microsoft.GS.DPS.Host/DependencyConfiguration/ServiceDependencies.cs b/App/backend-api/Microsoft.GS.DPS.Host/DependencyConfiguration/ServiceDependencies.cs index f51021db..359841e4 100644 --- a/App/backend-api/Microsoft.GS.DPS.Host/DependencyConfiguration/ServiceDependencies.cs +++ b/App/backend-api/Microsoft.GS.DPS.Host/DependencyConfiguration/ServiceDependencies.cs @@ -12,6 +12,7 @@ using Microsoft.GS.DPS.Storage.AISearch; using Microsoft.GS.DPSHost.AppConfiguration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.GS.DPSHost.Helpers; namespace Microsoft.GS.DPSHost.ServiceConfiguration { @@ -31,7 +32,7 @@ public static void Inject(IHostApplicationBuilder builder) return Kernel.CreateBuilder() .AddAzureOpenAIChatCompletion(deploymentName: builder.Configuration.GetSection("Application:AIServices:GPT-4o-mini")["ModelName"] ?? "", endpoint: builder.Configuration.GetSection("Application:AIServices:GPT-4o-mini")["Endpoint"] ?? "", - apiKey: builder.Configuration.GetSection("Application:AIServices:GPT-4o-mini")["Key"] ?? "") + credentials: AzureCredentialHelper.GetAzureCredential()) .Build(); }) @@ -66,7 +67,7 @@ public static void Inject(IHostApplicationBuilder builder) .AddSingleton(x => { var services = x.GetRequiredService>().Value; - return new TagUpdater(services.AzureAISearch.Endpoint, services.AzureAISearch.APIKey); + return new TagUpdater(services.AzureAISearch.Endpoint, AzureCredentialHelper.GetAzureCredential()); }) diff --git a/App/backend-api/Microsoft.GS.DPS/Microsoft.GS.DPS.csproj b/App/backend-api/Microsoft.GS.DPS/Microsoft.GS.DPS.csproj index 181c7a38..2d31585c 100644 --- a/App/backend-api/Microsoft.GS.DPS/Microsoft.GS.DPS.csproj +++ b/App/backend-api/Microsoft.GS.DPS/Microsoft.GS.DPS.csproj @@ -8,6 +8,7 @@ + diff --git a/App/backend-api/Microsoft.GS.DPS/Storage/AISearch/TagUpdater.cs b/App/backend-api/Microsoft.GS.DPS/Storage/AISearch/TagUpdater.cs index 8dde69f1..3b6d1a41 100644 --- a/App/backend-api/Microsoft.GS.DPS/Storage/AISearch/TagUpdater.cs +++ b/App/backend-api/Microsoft.GS.DPS/Storage/AISearch/TagUpdater.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Azure; +using Azure.Core; using Azure.Search.Documents; using Azure.Search.Documents.Models; @@ -12,9 +13,9 @@ public class TagUpdater { private readonly SearchClient _searchClient; - public TagUpdater(string searchEndPoint, string searchAPIKey, string indexName = "default") + public TagUpdater(string searchEndPoint, TokenCredential tokenCredential, string indexName = "default") { - _searchClient = new SearchClient(new Uri(searchEndPoint), indexName, new AzureKeyCredential(searchAPIKey)); + _searchClient = new SearchClient(new Uri(searchEndPoint), indexName, tokenCredential); } public async Task UpdateTags(string documentId, List updatingTags) diff --git a/App/kernel-memory/service/Core/DataFormats/Image/ImageContextDecoder.cs b/App/kernel-memory/service/Core/DataFormats/Image/ImageContextDecoder.cs index 385ec530..8dee9fc0 100644 --- a/App/kernel-memory/service/Core/DataFormats/Image/ImageContextDecoder.cs +++ b/App/kernel-memory/service/Core/DataFormats/Image/ImageContextDecoder.cs @@ -12,6 +12,7 @@ using Microsoft.SemanticKernel.ChatCompletion; using Azure.AI.OpenAI; using System.Collections.Generic; +using Azure.Identity; namespace Microsoft.KernelMemory.DataFormats.Image { @@ -32,7 +33,7 @@ public ImageContextDecoder(KernelMemoryConfig config, ILoggerFactory? loggerFact this._kernel = Kernel.CreateBuilder() .AddAzureOpenAIChatCompletion(deploymentName: (string)this._config.Services["AzureOpenAIText"]["Deployment"], endpoint: (string)this._config.Services["AzureOpenAIText"]["Endpoint"], - apiKey: (string)this._config.Services["AzureOpenAIText"]["APIKey"]) + credentials: new DefaultAzureCredential()) .Build(); } diff --git a/App/kernel-memory/service/Core/DataFormats/Pdf/PdfMarkdownDecoder.cs b/App/kernel-memory/service/Core/DataFormats/Pdf/PdfMarkdownDecoder.cs index 9e84fcc4..71b0b104 100644 --- a/App/kernel-memory/service/Core/DataFormats/Pdf/PdfMarkdownDecoder.cs +++ b/App/kernel-memory/service/Core/DataFormats/Pdf/PdfMarkdownDecoder.cs @@ -15,13 +15,13 @@ using Azure.Core; using Azure; using Microsoft.KernelMemory.Configuration; +using Azure.Identity; namespace Microsoft.KernelMemory.DataFormats.Pdf; public sealed class PdfMarkdownDecoder(KernelMemoryConfig config, ILoggerFactory? loggerFactory = null) : IContentDecoder { private readonly ILogger _log = (loggerFactory ?? DefaultLogger.Factory).CreateLogger(); private DocumentIntelligenceClient _client = null!; // Initialize _client as null - private string _apiKey = (string)config.Services["AzureAIDocIntel"]["APIKey"]; private string _endpoint = (string)config.Services["AzureAIDocIntel"]["Endpoint"]; /// @@ -49,7 +49,7 @@ public async Task DecodeAsync(BinaryData data, CancellationToken ca Retry = { Delay = TimeSpan.FromSeconds(90), MaxDelay = TimeSpan.FromSeconds(180), MaxRetries = 3, Mode = RetryMode.Exponential }, }; - this._client = new DocumentIntelligenceClient(new Uri(this._endpoint), new AzureKeyCredential(this._apiKey), options); + this._client = new DocumentIntelligenceClient(new Uri(this._endpoint), new DefaultAzureCredential(), options); Operation operation = null; operation = await this._client.AnalyzeDocumentAsync(WaitUntil.Completed, analyzeDocumentOptions, cancellationToken).ConfigureAwait(false); diff --git a/App/kernel-memory/service/Core/Handlers/KeywordExtractingHandler.cs b/App/kernel-memory/service/Core/Handlers/KeywordExtractingHandler.cs index b5f95c9b..28001501 100644 --- a/App/kernel-memory/service/Core/Handlers/KeywordExtractingHandler.cs +++ b/App/kernel-memory/service/Core/Handlers/KeywordExtractingHandler.cs @@ -5,6 +5,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Azure.Identity; using Microsoft.Extensions.Logging; using Microsoft.KernelMemory.Diagnostics; using Microsoft.KernelMemory.Pipeline; @@ -38,7 +39,7 @@ public KeywordExtractingHandler( this._kernel = Kernel.CreateBuilder() .AddAzureOpenAIChatCompletion(deploymentName: (string)this._config.Services["AzureOpenAIText"]["Deployment"], endpoint: (string)this._config.Services["AzureOpenAIText"]["Endpoint"], - apiKey: (string)this._config.Services["AzureOpenAIText"]["APIKey"]) + credentials: new DefaultAzureCredential()) .Build(); } diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 5b343cc9..c7f8f9f8 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -686,6 +686,23 @@ try { Write-Host "Assign the role for aks system assigned managed identity to App Storage Queue Data Contributor role" -ForegroundColor Green az role assignment create --assignee $systemAssignedIdentity --role "Storage Queue Data Contributor" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($deploymentResult.StorageAccountName)" + # Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account + Write-Host "Assign the role for aks system assigned managed identity to App Cognitive Services OpenAI User role" -ForegroundColor Green + az role assignment create --assignee $systemAssignedIdentity --role "Cognitive Services OpenAI User" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.CognitiveServices/accounts/$($deploymentResult.AzOpenAiServiceName)" + + # Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account + Write-Host "Assign the role for aks system assigned managed identity to App Search Index Data Contributor role" -ForegroundColor Green + az role assignment create --assignee $systemAssignedIdentity --role "Search Index Data Contributor" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Search/searchServices/$($deploymentResult.AzSearchServiceName)" + + # Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account + Write-Host "Assign the role for aks system assigned managed identity to App Search Service Contributor role" -ForegroundColor Green + az role assignment create --assignee $systemAssignedIdentity --role "Search Service Contributor" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.Search/searchServices/$($deploymentResult.AzSearchServiceName)" + + # Assign the role for aks system assigned managed identity to Azure Queue Data Contributor role with the scope of Storage Account + Write-Host "Assign the role for aks system assigned managed identity to App Cognitive Services User role" -ForegroundColor Green + az role assignment create --assignee $systemAssignedIdentity --role "Cognitive Services User" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.CognitiveServices/accounts/$($deploymentResult.AzCognitiveServiceName)" + + # 8. Update aks nodepools to updated new role try { Write-Host "Upgrading node pools..." -ForegroundColor Cyan diff --git a/infra/main.bicep b/infra/main.bicep index 9e6ad805..a00be80d 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -396,112 +396,96 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 } ] - // keyValues: [ - // { - // name: 'Application:AIServices:GPT-4o-mini:Endpoint' - // value: avmOpenAi.outputs.endpoint - // } - // { - // name: 'Application:AIServices:GPT-4o-mini:Key' - // value: '' // Todo: avmOpenAi.outputs. - // } - // { - // name: 'Application:AIServices:GPT-4o-mini:ModelName' - // value: chatGpt.modelName - // } - // { - // name: 'Application:Services:KernelMemory:Endpoint' - // value: 'http://kernelmemory-service' - // } - // { - // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Collection' - // value: 'ChatHistory' - // } - // { - // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Database' - // value: 'DPS' - // } - // { - // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Collection' - // value: 'Documents' - // } - // { - // name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Database' - // value: 'DPS' - // } - // { - // name: 'Application:Services:PersistentStorage:CosmosMongo:ConnectionString' - // value: avmCosmosDB.outputs.primaryReadWriteConnectionString - // } - // { - // name: 'Application:Services:AzureAISearch:APIKey' - // value: avmSearchSearchServices.outputs.exportedSecrets.primaryAdminKey - // } - // { - // name: 'Application:Services:AzureAISearch:Endpoint' - // value: 'https://${avmSearchSearchServices.outputs.name}.search.windows.net' - // } - // { - // name: 'KernelMemory:Services:AzureAIDocIntel:APIKey' - // value: documentIntelligence.outputs.exportedSecrets.primaryAdminKey - // } - // { - // name: 'KernelMemory:Services:AzureAIDocIntel:Endpoint' - // value: documentIntelligence.outputs.endpoint - // } - // { - // name: 'KernelMemory:Services:AzureAISearch:APIKey' - // value: avmSearchSearchServices.outputs.exportedSecrets.primaryAdminKey - // } - // { - // name: 'KernelMemory:Services:AzureAISearch:Endpoint' - // value: 'https://${avmSearchSearchServices.outputs.name}.search.windows.net' - // } - // { - // name: 'KernelMemory:Services:AzureBlobs:Account' - // value: avmStorageAccount.outputs.name - // } - // { - // name: 'KernelMemory:Services:AzureBlobs:ConnectionString' - // value: avmStorageAccount.outputs.primaryConnectionString - // } - // { - // name: 'KernelMemory:Services:AzureBlobs:Container' - // value: 'smemory' - // } - // { - // name: 'KernelMemory:Services:AzureOpenAIEmbedding:APIKey' - // value: '{azureopenaiembedding-apikey}' - // } - // { - // name: 'KernelMemory:Services:AzureOpenAIEmbedding:Deployment' - // value: embedding.deploymentName - // } - // { - // name: 'KernelMemory:Services:AzureOpenAIEmbedding:Endpoint' - // value: avmOpenAi.outputs.endpoint - // } - // { - // name: 'KernelMemory:Services:AzureOpenAIText:APIKey' - // value: '{azureopenaitext-apikey}' - // } - // { - // name: 'KernelMemory:Services:AzureOpenAIText:Deployment' - // value: chatGpt.deploymentName - // } - // { - // name: 'KernelMemory:Services:AzureOpenAIText:Endpoint' - // value: avmOpenAi.outputs.endpoint - // } - // { - // name: 'KernelMemory:Services:AzureQueues:Account' - // value: avmStorageAccount.outputs.name - // } - // { - // name: 'KernelMemory:Services:AzureQueues:ConnectionString' - // value: avmStorageAccount.outputs.primaryConnectionString - // } - // ] + keyValues: [ + { + name: 'Application:AIServices:GPT-4o-mini:Endpoint' + value: avmOpenAi.outputs.endpoint + } + { + name: 'Application:AIServices:GPT-4o-mini:ModelName' + value: chatGpt.modelName + } + { + name: 'Application:Services:KernelMemory:Endpoint' + value: 'http://kernelmemory-service' + } + { + name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Collection' + value: 'ChatHistory' + } + { + name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Database' + value: 'DPS' + } + { + name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Collection' + value: 'Documents' + } + { + name: 'Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Database' + value: 'DPS' + } + { + name: 'Application:Services:PersistentStorage:CosmosMongo:ConnectionString' + value: avmCosmosDB.outputs.primaryReadWriteConnectionString + } + { + name: 'Application:Services:AzureAISearch:Endpoint' + value: 'https://${avmSearchSearchServices.outputs.name}.search.windows.net' + } + { + name: 'KernelMemory:Services:AzureAIDocIntel:Auth' + value: 'AzureIdentity' + } + { + name: 'KernelMemory:Services:AzureAIDocIntel:Endpoint' + value: documentIntelligence.outputs.endpoint + } + { + name: 'KernelMemory:Services:AzureAISearch:Auth' + value: 'AzureIdentity' + } + { + name: 'KernelMemory:Services:AzureAISearch:Endpoint' + value: 'https://${avmSearchSearchServices.outputs.name}.search.windows.net' + } + { + name: 'KernelMemory:Services:AzureBlobs:Account' + value: avmStorageAccount.outputs.name + } + { + name: 'KernelMemory:Services:AzureBlobs:Auth' + value: 'AzureIdentity' + } + { + name: 'KernelMemory:Services:AzureBlobs:Container' + value: 'smemory' + } + { + name: 'KernelMemory:Services:AzureOpenAIEmbedding:Deployment' + value: embedding.deploymentName + } + { + name: 'KernelMemory:Services:AzureOpenAIEmbedding:Endpoint' + value: avmOpenAi.outputs.endpoint + } + { + name: 'KernelMemory:Services:AzureOpenAIText:Deployment' + value: chatGpt.deploymentName + } + { + name: 'KernelMemory:Services:AzureOpenAIText:Endpoint' + value: avmOpenAi.outputs.endpoint + } + { + name: 'KernelMemory:Services:AzureQueues:Account' + value: avmStorageAccount.outputs.name + } + { + name: 'KernelMemory:Services:AzureQueues:Auth' + value: 'AzureIdentity' + } + ] // WAF aligned networking publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' privateEndpoints: enablePrivateNetworking @@ -615,7 +599,11 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = managedIdentities: { userAssignedResourceIds: [userAssignedIdentity!.outputs.resourceId] } replicaCount: 1 partitionCount: 1 - + authOptions: { + aadOrApiKey: { + aadAuthFailureMode: 'http401WithBearerChallenge' + } + } roleAssignments: [ { roleDefinitionIdOrName: 'Search Index Data Contributor' // Cognitive Search Contributor From d8967277c1ee2ae7b24345f8c6e972c6a4a03cc4 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 10 Sep 2025 09:06:30 +0530 Subject: [PATCH 14/29] feat: Removed unwanted sections from Power shell script --- Deployment/resourcedeployment.ps1 | 130 +++++++++++------------------- infra/main.bicep | 16 ++-- 2 files changed, 53 insertions(+), 93 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index c7f8f9f8..5faeffa1 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -73,7 +73,7 @@ function ValidateVariableIsNullOrEmpty { # Function to prompt for parameters with kind messages function PromptForParameters { param( - [string]$email + [string]$email ) Clear-Host @@ -88,7 +88,7 @@ function PromptForParameters { } return @{ - email = $email + email = $email } } @@ -222,15 +222,12 @@ class DeploymentResult { [string]$ResourceGroupName [string]$ResourceGroupId [string]$StorageAccountName - [string]$StorageAccountConnectionString [string]$AzSearchServiceName [string]$AzSearchServicEndpoint - [string]$AzSearchAdminKey [string]$AksName [string]$AksMid [string]$AzContainerRegistryName [string]$AzCognitiveServiceName - [string]$AzCognitiveServiceKey [string]$AzCognitiveServiceEndpoint [string]$AzOpenAiServiceName [string]$AzGPT4oModelName @@ -238,7 +235,6 @@ class DeploymentResult { [string]$AzGPTEmbeddingModelName [string]$AzGPTEmbeddingModelId [string]$AzOpenAiServiceEndpoint - [string]$AzOpenAiServiceKey [string]$AzCosmosDBName [string]$AzCosmosDBConnectionString [string]$AzAppConfigEndpoint @@ -252,11 +248,9 @@ class DeploymentResult { $this.ResourceGroupId = "" # Storage Account $this.StorageAccountName = "" - $this.StorageAccountConnectionString = "" # Azure Search $this.AzSearchServiceName = "" $this.AzSearchServicEndpoint = "" - $this.AzSearchAdminKey = "" # AKS $this.AksName = "" $this.AksMid = "" @@ -265,11 +259,9 @@ class DeploymentResult { # Cognitive Service - Azure AI Intelligence Document Service $this.AzCognitiveServiceName = "" $this.AzCognitiveServiceEndpoint = "" - $this.AzCognitiveServiceKey = "" # Open AI Service $this.AzOpenAiServiceName = "" $this.AzOpenAiServiceEndpoint = "" - $this.AzOpenAiServiceKey = "" # Model - GPT4o $this.AzGPT4oModelName = "" $this.AzGPT4oModelId = "" @@ -377,34 +369,21 @@ try { # Deploy Azure Resources Write-Host "Retrieving the deployment details.....`r`n" -ForegroundColor Yellow - # Map the deployment result to DeploymentResult object + # Map the deployment result to DeploymentResult object from .env file $deploymentResult.MapResult() + # Display the deployment result DisplayResult($deploymentResult) LoginAzure $deploymentResult.TenantId $deploymentResult.SubscriptionId ############################################################### - # Step 2 : Get Secrets from Azure resources - Show-Banner -Title "Step 2 : Get Secrets from Azure resources" + # Step 2 : Validate deloyment result + Show-Banner -Title "Step 2 : Validate deloyment result" ############################################################### # Validate if the Storage Account Name is empty or null ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.StorageAccountName -variableName "Storage Account Name" - # Get the storage account key - $storageAccountKey = az storage account keys list --account-name $deploymentResult.StorageAccountName --resource-group $deploymentResult.ResourceGroupName --query "[0].value" -o tsv - - # Validate if the storage account key is empty or null - ValidateVariableIsNullOrEmpty -variableValue $storageAccountKey -variableName "Storage account key" - - ## Construct the connection string manually - $storageAccountConnectionString = "DefaultEndpointsProtocol=https;AccountName=$($deploymentResult.StorageAccountName);AccountKey=$storageAccountKey;EndpointSuffix=core.windows.net" - # Validate if the Storage Account Connection String is empty or null - ValidateVariableIsNullOrEmpty -variableValue $storageAccountConnectionString -variableName "Storage Account Connection String" - - ## Assign the connection string to the deployment result object - $deploymentResult.StorageAccountConnectionString = $storageAccountConnectionString - # Check if ResourceGroupName is valid ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupName -variableName "Resource group name" @@ -422,12 +401,6 @@ try { # Get MongoDB connection string $deploymentResult.AzCosmosDBConnectionString = az cosmosdb keys list --name $deploymentResult.AzCosmosDBName --resource-group $deploymentResult.ResourceGroupName --type connection-strings --query "connectionStrings[0].connectionString" -o tsv - # Get Azure Cognitive Service API Key - $deploymentResult.AzCognitiveServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzCognitiveServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv - # Get Azure Search Service Admin Key - $deploymentResult.AzSearchAdminKey = az search admin-key show --service-name $deploymentResult.AzSearchServiceName --resource-group $deploymentResult.ResourceGroupName --query "primaryKey" -o tsv - # Get Azure Open AI Service API Key - $deploymentResult.AzOpenAiServiceKey = az cognitiveservices account keys list --name $deploymentResult.AzOpenAiServiceName --resource-group $deploymentResult.ResourceGroupName --query "key1" -o tsv Write-Host "Secrets have been retrieved successfully." -ForegroundColor Green @@ -440,11 +413,7 @@ try { $aiServicePlaceholders = @{ '{gpt-4o-mini-endpoint}' = $deploymentResult.AzOpenAiServiceEndpoint - '{gpt-4o-mini-apikey}' = $deploymentResult.AzOpenAiServiceKey - '{azureaisearch-apikey}' = $deploymentResult.AzSearchAdminKey - '{documentintelligence-apikey}' = $deploymentResult.AzCognitiveServiceKey '{cosmosmongo-connection-string}' = $deploymentResult.AzCosmosDBConnectionString - '{azureblobs-connection-string}' = $deploymentResult.StorageAccountConnectionString '{azureblobs-account}' = $deploymentResult.StorageAccountName '{azureaisearch-endpoint}' = $deploymentResult.AzSearchServicEndpoint '{gpt-4o-mini-modelname}' = $deploymentResult.AzGPT4oModelId @@ -453,12 +422,7 @@ try { '{azureopenaiembedding-endpoint}' = $deploymentResult.AzOpenAiServiceEndpoint '{azureopenaitext-endpoint}' = $deploymentResult.AzOpenAiServiceEndpoint '{azureopenaitext-deployment}' = $deploymentResult.AzGPT4oModelId - '{gpt-4o-key}' = $deploymentResult.AzOpenAiServiceKey - '{textembedding-key}' = $deploymentResult.AzOpenAiServiceKey - '{azureopenaiembedding-apikey}' = $deploymentResult.AzOpenAiServiceKey - '{azureopenaitext-apikey}' = $deploymentResult.AzOpenAiServiceKey '{textembedding-modelname}' = $deploymentResult.AzGPTEmbeddingModelName - '{azureaidocintel-apikey}' = $deploymentResult.AzCognitiveServiceKey '{cosmosmongo-chat-history-collection}' = "ChatHistory" '{cosmosmongo-chat-history-database}' = "DPS" '{cosmosmongo-document-manager-collection}' = "Documents" @@ -467,7 +431,6 @@ try { '{documentintelligence-endpoint}' = $deploymentResult.AzCognitiveServiceEndpoint '{azureblobs-container}' = "smemory" '{azurequeues-account}' = $deploymentResult.StorageAccountName - '{azurequeues-connection-string}' = $deploymentResult.StorageAccountConnectionString '{gpt-4o-modelname}' = $deploymentResult.AzGPT4oModelName '{azureopenaiembedding-deployment}' = $deploymentResult.AzGPTEmbeddingModelName '{kernelmemory-endpoint}' = "http://kernelmemory-service" @@ -477,43 +440,43 @@ try { $aiServiceConfigTemplate = Get-Content -Path .\appconfig\aiservice\appconfig.jsonl -Raw $aiServiceConfigTemplate = Invoke-PlaceholdersReplacement $aiServiceConfigTemplate $aiServicePlaceholders - ## Save the updated AI service configuration file - $aiServiceConfigPath = ".\appconfig\aiservice\appsettings.dev.jsonl" - $aiServiceConfigTemplate | Set-Content -Path $aiServiceConfigPath -Force - Write-Host "Knowledge Mining Solution Accelerator Service Application Configuration file has been updated successfully." -ForegroundColor Green - - ## Set error action preference to silently continue - $ErrorActionPreference = "SilentlyContinue" - ## Get the current script directory dynamically - $scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Definition - - ## Construct the relative path to the JSON file - $filePath = Join-Path $scriptDirectory ".\appconfig\aiservice\appsettings.dev.jsonl" - - ## Other variables - $appConfigName = $deploymentResult.AzAppConfigName - - ## Output the file path for verification - #write-host "Using file path: $filePath" - - ## Execute the az appconfig kv import command using PowerShell - az appconfig kv import ` - --name $appConfigName ` - --source file ` - --path $filePath ` - --format json ` - --separator "," ` - --content-type "application/x-ndjson" ` - --yes - - ## Check if the file exists and delete it - if (Test-Path $aiServiceConfigPath) { - Remove-Item $aiServiceConfigPath -Force - #Write-Host "File '$aiServiceConfigPath' has been deleted." - } else { - Write-Host "File '$aiServiceConfigPath' does not exist." - } - $ErrorActionPreference = "Continue" + # ## Save the updated AI service configuration file + # $aiServiceConfigPath = ".\appconfig\aiservice\appsettings.dev.jsonl" + # $aiServiceConfigTemplate | Set-Content -Path $aiServiceConfigPath -Force + # Write-Host "Knowledge Mining Solution Accelerator Service Application Configuration file has been updated successfully." -ForegroundColor Green + + # ## Set error action preference to silently continue + # $ErrorActionPreference = "SilentlyContinue" + # ## Get the current script directory dynamically + # $scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Definition + + # ## Construct the relative path to the JSON file + # $filePath = Join-Path $scriptDirectory ".\appconfig\aiservice\appsettings.dev.jsonl" + + # ## Other variables + # $appConfigName = $deploymentResult.AzAppConfigName + + # ## Output the file path for verification + # #write-host "Using file path: $filePath" + + # ## Execute the az appconfig kv import command using PowerShell + # az appconfig kv import ` + # --name $appConfigName ` + # --source file ` + # --path $filePath ` + # --format json ` + # --separator "," ` + # --content-type "application/x-ndjson" ` + # --yes + + # ## Check if the file exists and delete it + # if (Test-Path $aiServiceConfigPath) { + # Remove-Item $aiServiceConfigPath -Force + # #Write-Host "File '$aiServiceConfigPath' has been deleted." + # } else { + # Write-Host "File '$aiServiceConfigPath' does not exist." + # } + # $ErrorActionPreference = "Continue" ###################################################################################################################### @@ -646,9 +609,7 @@ try { # Validate if the FQDN is null or empty ValidateVariableIsNullOrEmpty -variableValue $fqdn -variableName "FQDN" - - # 7. Assign the role for aks system assigned managed identity to App Configuration Data Reader role with the scope of Resourcegroup - Write-Host "Assign the role for aks system assigned managed identity to App Configuration Data Reader role" -ForegroundColor Green + # Ensure that the required fields are not null or empty ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupName -variableName "Resource group name" @@ -676,6 +637,7 @@ try { ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.ResourceGroupId -variableName "ResourceGroupId" # Assign the role for aks system assigned managed identity to App Configuration Data Reader role with the scope of Resourcegroup + Write-Host "Assign the role for aks system assigned managed identity to App Configuration Data Reader role" -ForegroundColor Green az role assignment create --assignee $systemAssignedIdentity --role "App Configuration Data Reader" --scope $deploymentResult.ResourceGroupId # Assign the role for aks system assigned managed identity to Azure blob storage Data Contributor role with the scope of Storage Account @@ -702,7 +664,6 @@ try { Write-Host "Assign the role for aks system assigned managed identity to App Cognitive Services User role" -ForegroundColor Green az role assignment create --assignee $systemAssignedIdentity --role "Cognitive Services User" --scope "/subscriptions/$($deploymentResult.SubscriptionId)/resourceGroups/$($deploymentResult.ResourceGroupName)/providers/Microsoft.CognitiveServices/accounts/$($deploymentResult.AzCognitiveServiceName)" - # 8. Update aks nodepools to updated new role try { Write-Host "Upgrading node pools..." -ForegroundColor Cyan @@ -711,7 +672,6 @@ try { Write-Host "Upgrading node pool: $nodePool" -ForegroundColor Cyan Write-Host "Node pool $nodePool upgrade initiated." -ForegroundColor Green az aks nodepool upgrade --resource-group $deploymentResult.ResourceGroupName --cluster-name $deploymentResult.AksName --name $nodePool - } } catch { diff --git a/infra/main.bicep b/infra/main.bicep index a00be80d..2a85cdc6 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -461,6 +461,10 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 name: 'KernelMemory:Services:AzureBlobs:Container' value: 'smemory' } + { + name: 'KernelMemory:Services:AzureOpenAIEmbedding:Auth' + value: 'AzureIdentity' + } { name: 'KernelMemory:Services:AzureOpenAIEmbedding:Deployment' value: embedding.deploymentName @@ -469,6 +473,10 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 name: 'KernelMemory:Services:AzureOpenAIEmbedding:Endpoint' value: avmOpenAi.outputs.endpoint } + { + name: 'KernelMemory:Services:AzureOpenAIText:Auth' + value: 'AzureIdentity' + } { name: 'KernelMemory:Services:AzureOpenAIText:Deployment' value: chatGpt.deploymentName @@ -599,11 +607,6 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = managedIdentities: { userAssignedResourceIds: [userAssignedIdentity!.outputs.resourceId] } replicaCount: 1 partitionCount: 1 - authOptions: { - aadOrApiKey: { - aadAuthFailureMode: 'http401WithBearerChallenge' - } - } roleAssignments: [ { roleDefinitionIdOrName: 'Search Index Data Contributor' // Cognitive Search Contributor @@ -616,7 +619,6 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = principalType: 'ServicePrincipal' } ] - disableLocalAuth: false semanticSearch: 'free' // secretsExportConfiguration: { // keyVaultResourceId: keyvault.outputs.resourceId @@ -654,7 +656,6 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { tags: tags enableTelemetry: enableTelemetry customSubDomainName: openAiAccountName - disableLocalAuth: false managedIdentities: { systemAssigned: true } @@ -714,7 +715,6 @@ module documentIntelligence 'br/public:avm/res/cognitive-services/account:0.13.2 tags: tags sku: 'S0' customSubDomainName: docIntelAccountName - disableLocalAuth: false managedIdentities: { systemAssigned: true } From 5414d29970b1b47ce6f8616946f03680d00be1fa Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Wed, 10 Sep 2025 13:07:35 +0530 Subject: [PATCH 15/29] feat: Working for WAF aligned change --- infra/main.bicep | 27 ++++++++++++++++++++++++--- infra/modules/network.bicep | 14 +++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 2a85cdc6..92ef4365 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -48,7 +48,7 @@ param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = param enableTelemetry bool = true @description('Optional. Enable private networking for applicable resources, aligned with the WAF recommendations. Defaults to false.') -param enablePrivateNetworking bool = false +param enablePrivateNetworking bool = true @description('Optional: Existing Log Analytics Workspace Resource ID') param existingLogAnalyticsWorkspaceId string = '' @@ -494,6 +494,22 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 value: 'AzureIdentity' } ] + + publicNetworkAccess: 'Enabled' + } +} + +module avmAppConfigUpdated 'br/public:avm/res/app-configuration/configuration-store:0.6.3' = if(enablePrivateNetworking) { + name: take('avm.res.app-configuration.configuration-store-update.${appConfigName}', 64) + params: { + name: appConfigName + location: solutionLocation + managedIdentities: { systemAssigned: true } + sku: 'Standard' + enableTelemetry: enableTelemetry + tags: tags + disableLocalAuth: true + // WAF aligned networking publicNetworkAccess: enablePrivateNetworking ? 'Disabled' : 'Enabled' privateEndpoints: enablePrivateNetworking @@ -513,10 +529,12 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 ] : [] } + dependsOn: [ + avmAppConfig + ] } // ========== Storage account module ========== // - var storageAccountName = 'st${solutionSuffix}' module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { name: take('avm.res.storage.storage-account.${storageAccountName}', 64) @@ -644,7 +662,6 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = } // // ========== Cognitive Services - OpenAI module ========== // - var openAiAccountName = 'oai-${solutionSuffix}' module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${openAiAccountName}', 64) @@ -775,6 +792,9 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10. // userAssignedIdentity.outputs.resourceId // ] } + serviceCidr: '10.20.0.0/16' + dnsServiceIP: '10.20.0.10' + enablePrivateCluster: false primaryAgentPoolProfiles: [ { name: 'agentpool' @@ -783,6 +803,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10. osType: 'Linux' mode: 'System' type: 'VirtualMachineScaleSets' + vnetSubnetResourceId: enableMonitoring ? network!.outputs.subnetWebResourceId : null } ] roleAssignments: [ diff --git a/infra/modules/network.bicep b/infra/modules/network.bicep index 55a161fb..18c28944 100644 --- a/infra/modules/network.bicep +++ b/infra/modules/network.bicep @@ -90,6 +90,19 @@ module network 'network/main.bicep' = { sourcePortRange: '*' destinationPortRange: '443' sourceAddressPrefixes: ['0.0.0.0/0'] + destinationAddressPrefix: '*' + } + } + { + name: 'AllowHttpInbound' + properties: { + access: 'Allow' + direction: 'Inbound' + priority: 110 + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '80' + sourceAddressPrefixes: ['0.0.0.0/0'] destinationAddressPrefixes: ['10.0.0.0/23'] } } @@ -121,7 +134,6 @@ module network 'network/main.bicep' = { } ] } - delegation: 'Microsoft.App/environments' } { name: 'peps' From 23a2edd48b7464a9e3f887ef8e129c59b045273e Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Thu, 11 Sep 2025 04:39:56 +0530 Subject: [PATCH 16/29] feat: Added WAF aligned params for AKS --- Deployment/resourcedeployment.ps1 | 137 +++------- docs/DeploymentGuide.md | 74 +---- .../deployment/Deployment_Input_Param_01.png | Bin 0 -> 32280 bytes infra/main.bicep | 258 ++++++++++-------- infra/modules/network.bicep | 2 +- 5 files changed, 193 insertions(+), 278 deletions(-) create mode 100644 docs/images/deployment/Deployment_Input_Param_01.png diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 5faeffa1..73cce68d 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -129,37 +129,23 @@ function LoginAzure([string]$tenantId, [string]$subscriptionID) { } function DisplayResult([DeploymentResult]$displayResult) { - $resourcegroupName = $displayResult.ResourceGroupName - # $solutionPrefix = $displayResult.S - - $storageAccountName = $displayResult.StorageAccountName - $azsearchServiceName = $displayResult.AzSearchServiceName - $aksName = $displayResult.AksName - - $containerRegistryName = $displayResult.ContainerRegistryName - $azcognitiveserviceName = $displayResult.AzCognitiveServiceName - $azopenaiServiceName = $displayResult.AzOpenAIServiceName - $azcosmosDBName = $displayResult.AzCosmosDBName - $azappConfigEndpoint = $displayResult.AzAppConfigEndpoint - - # Display banner Write-Host "********************************************************************************" -ForegroundColor Blue Write-Host "* Deployed Azure Resources Information *" -ForegroundColor Blue Write-Host "********************************************************************************" -ForegroundColor Blue - Write-Host "* Tenant Id: " -ForegroundColor Yellow -NoNewline; Write-Host "$tenantId" -ForegroundColor Green - Write-Host "* Subscription Id: " -ForegroundColor Yellow -NoNewline; Write-Host "$subscriptionID" -ForegroundColor Green - Write-Host "* Knowledge Mining Digital Asset resource group: " -ForegroundColor Yellow -NoNewline; Write-Host "$resourcegroupName" -ForegroundColor Green - Write-Host "* Azure Kubernetes Account " -ForegroundColor Yellow -NoNewline; Write-Host "$aksName" -ForegroundColor Green - Write-Host "* Azure Container Registry " -ForegroundColor Yellow -NoNewline; Write-Host "$containerRegistryName" -ForegroundColor Green - Write-Host "* Azure Search Service " -ForegroundColor Yellow -NoNewline; Write-Host "$azsearchServiceName" -ForegroundColor Green - Write-Host "* Azure Open AI Service " -ForegroundColor Yellow -NoNewline; Write-Host "$azopenaiServiceName" -ForegroundColor Green - Write-Host "* Azure Cognitive Service " -ForegroundColor Yellow -NoNewline; Write-Host "$azcognitiveserviceName" -ForegroundColor Green - Write-Host "* Azure Storage Account " -ForegroundColor Yellow -NoNewline; Write-Host "$storageAccountName" -ForegroundColor Green - Write-Host "* Azure Cosmos DB " -ForegroundColor Yellow -NoNewline; Write-Host "$azcosmosDBName" -ForegroundColor Green - Write-Host "* Azure App Configuration Endpoint " -ForegroundColor Yellow -NoNewline; Write-Host "$azappConfigEndpoint" -ForegroundColor Green - Write-Output "rg_name=$resourcegroupName" >> $Env:GITHUB_ENV - - Write-Output "SOLUTION_PREFIX=$solutionPrefix" >> $Env:GITHUB_ENV + Write-Host "* Tenant Id: " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.TenantId)" -ForegroundColor Green + Write-Host "* Subscription Id: " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.SubscriptionId)" -ForegroundColor Green + Write-Host "* Knowledge Mining Digital Asset resource group: " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.ResourceGroupName)" -ForegroundColor Green + Write-Host "* Azure Kubernetes Account " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.AksName)" -ForegroundColor Green + Write-Host "* Azure Container Registry " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.AzContainerRegistryName)" -ForegroundColor Green + Write-Host "* Azure Search Service " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.AzSearchServiceName)" -ForegroundColor Green + Write-Host "* Azure Open AI Service " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.AzOpenAIServiceName)" -ForegroundColor Green + Write-Host "* Azure Cognitive Service " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.AzCognitiveServiceName)" -ForegroundColor Green + Write-Host "* Azure Storage Account " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.StorageAccountName)" -ForegroundColor Green + Write-Host "* Azure Cosmos DB " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.AzCosmosDBName)" -ForegroundColor Green + Write-Host "* Azure App Configuration Endpoint " -ForegroundColor Yellow -NoNewline; Write-Host "$($displayResult.AzAppConfigEndpoint)" -ForegroundColor Green + Write-Output "rg_name=$($displayResult.ResourceGroupName)" >> $Env:GITHUB_ENV + + Write-Output "SOLUTION_PREFIX=$($displayResult.SolutionPrefix)" >> $Env:GITHUB_ENV } # Function to replace placeholders in a template with actual values @@ -372,15 +358,12 @@ try { # Map the deployment result to DeploymentResult object from .env file $deploymentResult.MapResult() + LoginAzure $deploymentResult.TenantId $deploymentResult.SubscriptionId + # Display the deployment result DisplayResult($deploymentResult) - LoginAzure $deploymentResult.TenantId $deploymentResult.SubscriptionId - - ############################################################### - # Step 2 : Validate deloyment result - Show-Banner -Title "Step 2 : Validate deloyment result" - ############################################################### + # Step 1.2 Validate the deployment result # Validate if the Storage Account Name is empty or null ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.StorageAccountName -variableName "Storage Account Name" @@ -402,13 +385,9 @@ try { # Get MongoDB connection string $deploymentResult.AzCosmosDBConnectionString = az cosmosdb keys list --name $deploymentResult.AzCosmosDBName --resource-group $deploymentResult.ResourceGroupName --type connection-strings --query "connectionStrings[0].connectionString" -o tsv - Write-Host "Secrets have been retrieved successfully." -ForegroundColor Green + Write-Host "Validation Completed" -ForegroundColor Green - ###################################################################################################################### - # Step 3 : Update App Configuration files with Secrets and information for AI Service and Kernel Memory Service. - Show-Banner -Title "Step 3 : Update App Configuration files with Secrets and information for AI Service and Kernel Memory Service." - ###################################################################################################################### - # Step 3-1 Loading aiservice's configution file template then replace the placeholder with the actual values + # Step 1-3 Loading aiservice's configution file template then replace the placeholder with the actual values # Define the placeholders and their corresponding values for AI service configuration $aiServicePlaceholders = @{ @@ -440,48 +419,11 @@ try { $aiServiceConfigTemplate = Get-Content -Path .\appconfig\aiservice\appconfig.jsonl -Raw $aiServiceConfigTemplate = Invoke-PlaceholdersReplacement $aiServiceConfigTemplate $aiServicePlaceholders - # ## Save the updated AI service configuration file - # $aiServiceConfigPath = ".\appconfig\aiservice\appsettings.dev.jsonl" - # $aiServiceConfigTemplate | Set-Content -Path $aiServiceConfigPath -Force - # Write-Host "Knowledge Mining Solution Accelerator Service Application Configuration file has been updated successfully." -ForegroundColor Green - - # ## Set error action preference to silently continue - # $ErrorActionPreference = "SilentlyContinue" - # ## Get the current script directory dynamically - # $scriptDirectory = Split-Path -Parent $MyInvocation.MyCommand.Definition - - # ## Construct the relative path to the JSON file - # $filePath = Join-Path $scriptDirectory ".\appconfig\aiservice\appsettings.dev.jsonl" - - # ## Other variables - # $appConfigName = $deploymentResult.AzAppConfigName - - # ## Output the file path for verification - # #write-host "Using file path: $filePath" - - # ## Execute the az appconfig kv import command using PowerShell - # az appconfig kv import ` - # --name $appConfigName ` - # --source file ` - # --path $filePath ` - # --format json ` - # --separator "," ` - # --content-type "application/x-ndjson" ` - # --yes - - # ## Check if the file exists and delete it - # if (Test-Path $aiServiceConfigPath) { - # Remove-Item $aiServiceConfigPath -Force - # #Write-Host "File '$aiServiceConfigPath' has been deleted." - # } else { - # Write-Host "File '$aiServiceConfigPath' does not exist." - # } - # $ErrorActionPreference = "Continue" - + Write-Host "Update Configuration files Completed." -ForegroundColor Green ###################################################################################################################### - # Step 4 : Configure Kubernetes Infrastructure - Show-Banner -Title "Step 4 : Configure Kubernetes Infrastructure" + # Step 2 : Configure Kubernetes Infrastructure + Show-Banner -Title "Step 2 : Configure Kubernetes Infrastructure" ###################################################################################################################### # 0. Attach Container Registry to AKS Write-Host "Attach Container Registry to AKS" -ForegroundColor Green @@ -535,7 +477,6 @@ try { exit 1 } - # 2.Connect to AKS cluster try { Write-Host "Connecting to AKS cluster..." -ForegroundColor Cyan @@ -684,18 +625,18 @@ try { } ######################################################################################################################################### - # Step 5 : Update Kubernetes configuration files with the FQDN, Container Image Path and Email address for the certificate management - #Write-Host "Step 5 : Update Kubernetes yaml files with Container Image Path and Email address for the certificate management" -ForegroundColor Yellow - Show-Banner -Title "Step 5 : Update Kubernetes yaml files with Container Image Path and Email address for the certificate management" + # Step 3 : Update Kubernetes configuration files with the FQDN, Container Image Path and Email address for the certificate management + #Write-Host "Step 3 : Update Kubernetes yaml files with Container Image Path and Email address for the certificate management" -ForegroundColor Yellow + Show-Banner -Title "Step 3 : Update Kubernetes yaml files with Container Image Path and Email address for the certificate management" ######################################################################################################################################### - # 5.1 Update deploy.certclusterissuer.yaml.template file and save as deploy.certclusterissuer.yaml + # 3.1 Update deploy.certclusterissuer.yaml.template file and save as deploy.certclusterissuer.yaml $certManagerTemplate = Get-Content -Path .\kubernetes\deploy.certclusterissuer.yaml.template -Raw $certManagerTemplate = $certManagerTemplate -replace '{{ your-email }}', $email $certManagerPath = ".\kubernetes\deploy.certclusterissuer.yaml" $certManagerTemplate | Set-Content -Path $certManagerPath -Force - # 5.2 Update deploy.ingress.yaml.template file and save as deploy.ingress.yaml + # 3.2 Update deploy.ingress.yaml.template file and save as deploy.ingress.yaml # webfront / apibackend $ingressPlaceholders = @{ '{{ fqdn }}' = $fqdn @@ -708,7 +649,7 @@ try { Write-Host "Ingress Controller configuration file have been updated successfully." -ForegroundColor Green - # 5.3 Update deploy.deployment.yaml.template file and save as deploy.deployment.yaml + # 3.3 Update deploy.deployment.yaml.template file and save as deploy.deployment.yaml # Validate AzContainerRegistryName IsNull Or Empty. ValidateVariableIsNullOrEmpty -variableValue $deploymentResult.AzContainerRegistryName -variableName "Azure Container Registry Name" @@ -737,9 +678,9 @@ try { $deployment | Set-Content -Path $deploymentPath -Force ######################################################################################################################################################## - # Step 6 : Configure AKS (deploy Cert Manager, Ingress Controller) and Deploy Images on the kubernetes cluster - #Write-Host "Step 6 : Configure AKS (deploy Cert Manager) and Deploy Images on the kubernetes cluster" -ForegroundColor Yellow - Show-Banner -Title "Step 6 : Configure AKS (deploy Cert Manager) and Deploy Images on the kubernetes cluster" + # Step 4 : Configure AKS (deploy Cert Manager, Ingress Controller) and Deploy Images on the kubernetes cluster + #Write-Host "Step 4 : Configure AKS (deploy Cert Manager) and Deploy Images on the kubernetes cluster" -ForegroundColor Yellow + Show-Banner -Title "Step 4 : Configure AKS (deploy Cert Manager) and Deploy Images on the kubernetes cluster" ######################################################################################################################################################## function Wait-ForCertManager { Write-Host "Waiting for Cert-Manager to be ready..." -ForegroundColor Cyan @@ -756,7 +697,7 @@ try { } Write-Host "Deploying Cert Manager" -ForegroundColor Green - # 6.1. Install Cert Manager and nginx ingress controller in Kubernetes for SSL/TLS certificate + # 4.1. Install Cert Manager and nginx ingress controller in Kubernetes for SSL/TLS certificate # Install Cert-Manager Write-Host "Deploying...." -ForegroundColor Green helm repo add jetstack https://charts.jetstack.io --force-update @@ -823,8 +764,8 @@ try { Copy-Item -Path $frontAppConfigPath -Destination "..\App\frontend-app\.env" -Force ###################################################################################################################### - # Step 7 : docker build and push container images to Azure Container Registry - Show-Banner -Title "Step 7 : docker build and push container images to Azure Container Registry" + # Step 5 : docker build and push container images to Azure Container Registry + Show-Banner -Title "Step 5 : docker build and push container images to Azure Container Registry" ###################################################################################################################### # $acrNamespace = "kmgs" # $acrAIServiceTag = "$($deploymentResult.AzContainerRegistryName).azurecr.io/$acrNamespace/aiservice" @@ -853,16 +794,16 @@ try { #====================================================================================================================================================================== - # 7.2. Deploy ClusterIssuer in Kubernetes for SSL/TLS certificate + # 5.2. Deploy ClusterIssuer in Kubernetes for SSL/TLS certificate kubectl apply -f "./kubernetes/deploy.certclusterissuer.yaml" - # 7.3. Deploy Deployment in Kubernetes + # 5.3. Deploy Deployment in Kubernetes kubectl apply -f "./kubernetes/deploy.deployment.yaml" -n $kubenamespace - # 7.4. Deploy Services in Kubernetes + # 5.4. Deploy Services in Kubernetes kubectl apply -f "./kubernetes/deploy.service.yaml" -n $kubenamespace - # 7.5. Deploy Ingress Controller in Kubernetes for external access + # 5.5. Deploy Ingress Controller in Kubernetes for external access kubectl apply -f "./kubernetes/deploy.ingress.yaml" -n $kubenamespace # ##################################################################### @@ -874,7 +815,7 @@ try { ##################################################################### - # Step 8 : Display the deployment result and following instructions + # Step 6 : Display the deployment result and following instructions ##################################################################### #Write-Host "Deployment has been completed successfully." -ForegroundColor Green successBanner diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 1f0323e2..2986e36c 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -42,21 +42,6 @@
ResourceProvider - -## Regional Availability - -*Due to model availability within various data center regions, the following services have been hard-coded to specific regions.* - -* **Azure Open AI (GPT 4o mini):**
-The solution relies on `GPT-4o mini` and `text-embedding-3-large` models which are all currently available in the 'WestUS3', 'EastUS', 'EastUS2', 'SwedenCentral' region. -Please check the -[model summary table and region availability](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#embeddings) if needed. - -* **Azure AI Document Intelligence (East US):**
-The solution relies on a `2023-10-31-preview` or later that is currently available in `East US` region. -The deployment region for this model is fixed in 'East US' - - ## Deployment The automated deployment process is very straightforward and simplified via a single [deployment script](../Deployment/resourcedeployment.ps1) that completes in approximately 10-15 minutes: @@ -89,61 +74,12 @@ powershell.exe -ExecutionPolicy Bypass -File ".\resourcedeployment.ps1" ``` You will be prompted for the following parameters with this Screen : - - -1. **Tenant ID** - The Azure Active Directory (AAD) tenant ID. This is used for authenticating against Azure resources. Copy this from the Azure portal. -Example: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - -2. **Subscription ID** - The Azure subscription ID where resources will be deployed. -Copy this from the Azure portal. - -3. **Environment Name** - A unique environment name (e.g., dev, test, prod). -This is used to scope resource names and group deployments logically. - -4. **Resource Group Name** - The Azure resource group to deploy resources into. -You may either: - - - Specify an existing resource group to reuse it, [see below](#configuring-a-new-or-existing-resource-group) for more details, or - - Leave blank to auto-generate a new name. - -5. **Location** - Azure data center where resources will be deployed. - - * Please [check Azure resource availability and note hardcoded regions](#regional-availability). The following locations are currently supported: - - ``` - 'EastUS', 'EastUS2', 'WestUS', 'WestUS2', 'WestUS3', 'CentralUS', 'NorthCentralUS', 'SouthCentralUS','WestEurope', 'NorthEurope', 'SoutheastAsia', 'EastAsia', 'JapanEast', 'JapanWest', 'AustraliaEast', 'AustraliaSoutheast', 'CentralIndia', 'SouthIndia', 'CanadaCentral','CanadaEast', 'UKSouth', 'UKWest', 'FranceCentral', 'FranceSouth', 'KoreaCentral','KoreaSouth', 'GermanyWestCentral', 'GermanyNorth', 'NorwayWest', 'NorwayEast', 'SwitzerlandNorth', 'SwitzerlandWest', 'UAENorth', 'UAECentral', 'SouthAfricaNorth','SouthAfricaWest', 'BrazilSouth','BrazilSoutheast', 'QatarCentral', 'ChinaNorth', 'ChinaEast', 'ChinaNorth2', 'ChinaEast2' - ``` -6. **ModelLocation** - Azure data center where GPT model will be deployed. - The following locations are currently available : - ``` - 'WestUS3', 'EastUS', 'EastUS2', 'SwedenCentral' - ``` - -7. **Email** - used for issuing certificates in Kubernetes clusters from the [Let's Encrypt](https://letsencrypt.org/) service. Email address should be valid. - -8. **GO !** - Deployment Script executes Azure deployment, Azure Infrastructure configuration, Application code compile and publish into Kubernetes Cluster. - -## Configuring a New or Existing Resource Group - -➕ Creating a New Resource Group - -You have two options: - -- Manually specify a resource group name (e.g., rg-myproject-dev) - -- Leave the input field blank — a new name will be auto-generated by the script - -🔁 Using an Existing Resource Group - -If reusing an existing Azure Resource Group: - -- Provide the exact name of the existing resource group - -- Ensure the environment name matches the original environment used for that resource group - - ⚠️ After deployment, please restart the AKS (Kubernetes) service to ensure updated configurations are applied when using a reused resource group. + +1. **Email** - used for issuing certificates in Kubernetes clusters from the [Let's Encrypt](https://letsencrypt.org/) service. Email address should be valid. +2. You will be prompted to Login, Select a account and proceed to Login. +3. **GO !** - Post Deployment Script executes Azure Infrastructure configuration, Application code compile and publish into Kubernetes Cluster. @@ -177,7 +113,7 @@ Don't miss this Url information. This is the application's endpoint URL and it s | Model Name | TPM Threshold | |------------------------|---------------| -| GPT-4o-mini | 100K TPM | +| GPT-4.1-mini | 100K TPM | | text-embedding-3-large | 200K TPM | diff --git a/docs/images/deployment/Deployment_Input_Param_01.png b/docs/images/deployment/Deployment_Input_Param_01.png new file mode 100644 index 0000000000000000000000000000000000000000..27f39f8fb4c3389e5b94993483b06699d91c1c72 GIT binary patch literal 32280 zcmb@u2|Uz$`#(G#=j5D|a^H7}63%JUL4=T9Ii(`9jD4vHS;jJ!##*PR+z~>^9+GwJ zj9qe&ZH6$GF(ze-s*usjad9 z=c7NvV6gqyuKunEgZ&TFuzV`b?gZq{=_sIci^4BY5;UUdI~lHy@0 zugAKdbsL|Vq5g?H&2yR*8OK93DV~&Sg?V=5oqcoQkYwR;wXpmE{|(qFT@4sY+!fbU z$y?CRb>#!&n2H~kB9Bw%6iT8E-yoDb`~LQW83xfgSYj8FFnJLdNLm(Sbukx=6kCE* z%bAXunLQg!Zt{nEC4H_vFxX=Ls6T@tL`-fECYUXoV_BIJwhnJ91Rl_+quj_Zy>|It6I8bIM7Tb1XMEq_eZ0!D&ixJ1$@&e0;E>K7m47!7O~@-y3-& zA*4E*{vl+LRm(76C2mN>;@0XbSvu=cccj3H!lKP?=rYMbYkSbu2Vma59u2~_X?A(v8DMkN;cL*m6$WqO*c;^`!C0_1_L*)#COi|sF&F>_CY^; zKr=^?@#fvC&)X&Hk0;1k>2VbdL8IBF_@Bm!b$It3ZMz)Y26IhTt!8w8I&FB5@Mum1 z@f}zI=NS(j<@dl#$w94zT~0;I0EQDXG71>rO!ee_rbv4 zV?8PB9+Se)jHINbBCS|WRvkjD2B9(ilvM*smI8kB9$uZle3$*`^{tE9{^FK;g6~3h z`9@yQ=;$bmSkLqhNg!x%u4&-Q-h!bI_!Mm>f8b}AG2VbtUiTIO?=g+QMU9Zra=p!= zC)_vI>Nn^tyvv%Qtn<(dZvP2&#=OwR{3mhe^i479;#mJ2$O>nC;;nyqlEFyXV6^?* zzO@(Sw1bcDZZ7VD;DXS`=fIA7hTO*K1X<^{m6iMJSQ10A$pZiT)-eT{ySJWVj0&xf z){O3Vc@Kt4p(v6V+HSEjx?Ah{XRNvVwSdXkfZzJfkvHlXoi5`>0c4{E;#>fxf)KwY zFN+WETx85v&tZp$&D}6ETgNitPT_q~KOgEc2K_JZdM2@Ejp~Av*jSLDl%%Z9I0ghT zVguz}0-1!6&KGH0+an(hy&~%}lDM%@ zLYpXftoR()EaSIrMIcK+SN&i=nm@LFB|?Z1 z!7A98-iVj0oxHIvRApAoG8SorzG1;t88}?)e!PV<>}F4frCcN9o$3}p zXnvi2sN9(_oop~m7Eyp!_m)w`xJKTX4`d0^Fza}d2%;{F*Yf_afvlFm#LQ%$DH#GT zQ9epI`Ti#vLhz<(0Y_{W(l!@Bucocj>s4bdyi6oZ9dwlDe}{yCtg}9*6P#+aC2;(v zn8VS5%XWdXa>K6bbu6+3MJ|vg7cyEyKI_s7H?n{a*W_JEk4RY?OzF&swT##C`*e`e zqq^4fNxWdde7K8v+TGPbgq!4VAqj)^>cRVGbB2Ip%nFB04s=Z{mY?K{lZ<6deNvRL zPRwdtLP9$^)+)K4)m~q7>!N(E(WZtB@a^T~qzxl+WZVV|7m^yYpNng3zsr5~bs$@5 zd_DqO>L`vRM;pBMmf2uRq z0&dibW+!gr>Uyrz7TF}RrkIlE&TW$DcQW&EoA}__IpvL1^ zlPT->fb|Y;Qy%Xao9{T!e`EydDE0?im}@6PUHq(*eP=6Kz?7mSUoX zNaK{E*j6YxV0H2nqToJQ7ZBMrKGT>q_X3l|j>VZvQWqlm(GS@}DO14uTVu|eEr2J- zB*6V!9%N>xtY)*O3f3LG=0=h>C>xBx1>E|eYP$=o*#$BO_d5e9z9@g&%jEqt@7FYF z_gAIQ%UVJx1_ra3by@GMkE^Sz6B8F72Zqlpa*SEO!KBrLi|`GY$_x;}sW6=amW%>r zAp0dHCuhX^Tu?H++QFNaH5`KVQ)#h_zsEJ#>a5=M3p8R^XFgEq#iga%@zzfmpera^ z0x6NiNFudzs;)G!X6M!s0YFEUgjgRrW7imQDU3K^6J#piuXeNp5!H?ADKU9}>xxVH zUbdNCjFhWqbQYN9f1(G>(WN9MBQ|IoELDJ~{3WiruPyc4k?Q>0*IADOzi@x(?~hG1 zdxXkZii=Ip8qN(Hj`)ncw$WwW9E;r#E$$U(4O^J|l}uRRf@}1&u$7*%s;k}MwqO(n zxW!4o8Ec_-VIX-%0l=lyv2QlNW}yx2=xntL?fRi;-+|P0>3gzIq!sFF`Pf=i)eCHw zrQdwhK5-+rfCxr4X$=vuK@U_F)=BmwQ^EmGSOC+F<>cnK@fexU$1lZ6T+yuzI;*X1 z=k@Dg5e0Aj{if(O@{?AYV`6eEoM0Ue_S{N>ds6drEWo+(b}BOr0J_i{t~m|w zGcKrGonnOq&Px>g`!Zu{{O6l^kt<(Dl@`0yo4Se*Maw;?lPwwTCM6?-XDG3?_~8fZ6RK4&41g)WYBL!J`NM$ zfaPW8tn0iPMV6vq- zG4(5rfs1pIC;M5mLBXT>Y)cK^vnkymFYN&EKoVGQrX3usU!v zI3{VMB%|KdrECj`R}BNW76mjn4*TXt>&8Mo3s<>3b%xIX8>|;7sI4-1lZLWfungH9^h=%f0@Q)Jl$(Ti^|xCjx&n_(|Mx z8?v2%@ETaoJcN0}l-5_`{N_Wr$0svW#AnM-Z)4MsWj~NrJ2|*nKG@{7kWUE5@^7G9 zIXz+m`|^E5HoyDQQyt2i>M)*1jyK`e=&T%Ws=ar1Xi`&W`v7Jx;`%ZWGZ_JOMGrH< zHoDx|GPCHM)#`OB{`NDoNp(@?n~ZhC48p?Ay87%a;#8QkJhkG$S-T9ccFM1gDzZRd z%sRg63lvrgt)~aZo~#`ARyE)%7zBoJokgu*EBBk!jAEp##-lP`vFPg~`F_)8BrI>; zmnJBZK!FdD0ETbqir2Fi`dJJRT*+>XsZ$B05gaxPWKOOytd*tGhMTCH2IR))PoR3cd|H>?w3%(txpD5wgmixSRmU~IN1{`SWWpBsxN`Rq1(2{%!xiRyz4d}=f`N@5C=YE!(}Jp zdHVz(dYOC@g3>Ydbox3%*2R+W$03Qjl{=7g52OK)Si)y&7|s(d?Cv`C#N7W=PvvC3 zb<@YA27L(8oGEoURZj>WTIBW6Yofez(0L62r?v@v+JcXX*-!kC0zcS)d3%9}qD_+O z=7;Ig*;>l3nv%tJS7tlZ_4QFJZ%6LDKI+-YktFSQPG4U?F5c%C%p%VtWvrj0g|K_M zCB;TK4ETiyrZ)|2;ZII4+f;OSbj-KjvG-4^5e`>QUgQqFTf@=xVrcxK=NI#2Gcm-p z+ql`ZLVU@AlqdmZaM3V)RGL(7p`2X!fz$g+;*w`dO;N zxAu!^t3>MWYkix~uTRz-@WgQld{Cm}9y2-)UE+Vpz*n^)%fl!(cLKhL#Pt}?WYMjTaMdhf3SbsTUv~|CrfZSIB~wSG?E5wNR2roq){9F|f9zwhgb z!%j5VxTXjBS}lH_MwWra>8RS+r@F&+DF6; z)C28vB-8%o#J;QSV|aTdGfV3?LW_p%xZLMD*WXZ7v{2h;`WdxEJx|)v9aQeQ@Hk^YHVP>`j}+Zf;|loENaUb6Kg& zcg1sD`~tnWPtr0EWb%{LtP&HkGnb03acGlv%>iOE=fw9kv?sOAL||zFI$bB5;5PH# zl?m6QopDO5l?eYmYgYx$eK-}Z9q)5iGik7-Ti`mB<#RJFA)E6cR8u!@AAhB^rpqgx z%mk8Jin7Of>CP)pG+IJZ-F;4Scl`up7oS*ibee1p(8fU4q3Ft2^chmp!wyRCsQBjX zY0llE%uWY4giOb63XGXG)&U(G4%Fn?s$(3UWi|&Fs3bIu3?%7C$BPJFe zElmY87uwJU{-jo}HOh_6EgGXXXI)k_x?XoO*3y#eqOaC5zcbv!vZlks@pVPRj4pL~ z*R`9HA7q9iIf1akjO`LKxrwpN0II0`Qth1|@d#F35V8^baFM>mqmCY|Q|5@>Z`04I z5>wrRAHXZvn+ZM`>6J@n)=5QYb0U7b0>*gdCFXPS?h^#_4&vA>j`b8b?oZd^ZA8Z< zbx^&PBYQd@&eScbovxaANG>1WGjUD*psy4pyDK)5--E}?(zCQ=uBgO^*EybxQ;p!9 zbJqXWbNS}%%7RvWXwsnX^zV|UI`FYQ6Oz72+QhBma|x5JO?UnQo^;UAjl2GAPN?(N zVfU#owQ&gRP@8!S0vFNKdEP6G{B~){>Uvs^60A1e#ZOFqt#x>w6Zx7C%=zG`p23?n zFaPttPxxCuYam;U2v*mWJ#1w6P2%!vKR4a1HAv)qCHE}+KxTJhYqxqy6Et0KWSW#S z#&PcE-l&ewfHFRZ@Do<;GnXO-8soC&8{48OdnN*Q%#&dqbTVeZ8hE7>IHNi!i}Qe< zOgPs5RQ2BJ!Q>?CCS!kt6wX(K(D3~ksz`a%z&l>>ga`NKyF*Urjkr&?`Xyhmd{%}_ z>A(r!d+k!lmmPuyhj54B(mLUKqBAbNl5QjUBY0#fBTFm6BBuFnlQY%Zp>B5WsrPAa zHF_1|j*gejuO&TEn~RT6ld`zyYZc0?zqjHg@IHmPs&R@(M6JLGUCzMFl~~KuevjlD zo9e!k=YM4{XQY_Tx`(75Z&E8qS5kR+xGm&sx|!Bwh)}HpfXdmM)nOj_aON1%lB=xs zs+Y6NNz`#WhZ;+_B^0cxXUEzU&uTKjBX6gF180#bsGP(&`KY=@T92!|@%;!F_sPz< zgYpKSeYD8nDLvB4`i$V!aUFrMXozH*xB?6|snyqg(L?&kKs0}W%f?X4LAg1snw(!H zya}3T+uq3UV{awtP;~a6shr_<;%kFt8|gl<9Zpuvg+UGm#p+N}r72f)B)H z-0flv>t1e6V;J0K@Oi8j^`(gMI?x-Td`|aSKDO)k12aO}=_mz_S!IJ~czqr)9o}~t zOg@gBa^+=N0h$D6-0%hjDzyf~6`sIWDf^aJRPNIn``Ej`a`n$lWRFElaD$9-HZg;$ zh$9q7`ZEQ8s|BP}-WTuMI&Mceyjt`0fq+wTGfp<^f7;RNs^6HknP72^T}$#`inwk1 z-}-6$EkCMstRjZ5^vQ7(y=PyjmiMOBJ7y415%IIv+fwQsQxf3WB6hVO5DZCejTT_{ zjCFPqeRRCB1U9P8V5&j8OItv*mTnoEA}Rikyx?O$aCmLZhyHF*a_^d!gP`F zCZ%oq8Sq*6w~g5U$aAAxg4+(tUyz9onybs_d$7Nw##j$7SX%v>Fob@T6LzDzZ>oG9 zSgBGg0G!yMEDPumG#3lWqp<9brgtGl{oq3e%tCK$SDFnjqM<51zFF{>Ki(vy-}G=C>6 zc`Wtrw-da|8h(_YFc2kR@_uH~MzU1aP9^2Dbm#de^s(~PJB0ybw|M{-nDEDvM(mz} z+jF?6TiL%DN_k=dOlfPWV^a|AhBnUj*GETcE63*q+Z%JfIiNh%%Vfb4f4}pMp|-YR zBPp`^D_?R>c#o}^e^5OiGhcV7P-X1){%>Z$C#AXT9BExCQ;d8(sHG!*_>nVmVgk5< z(Pv>U5>Fg13E~};!<}caK~gd@(b++>9WVd*XKrCKMIz}ov_gC=xRXhKQVNlym6hF? zeXA+RNNk6*=$$89ql~MU6w56JYmv7mtvK^L*Uh|nydQ*^*3)9OB_TPO)&xAqmf=rN z$+c++q8$S~#+;yC-V?A#b;Z2?qNKJpE0u`H$PJka|JVNrlEP23GLVp%zV*>#bC$OE znt~<`Dn2!e+HwnYYL!HccFzNl-l|RjPoLEm>YS;}Szrx2NpfZ6^)oane2|1J0S4&A zNJ*ggUa1uQpj>l6PlQCpS^Cf~cv~59*Vg*HP1t)cXc{EeW@pYYWI*4*$?* z5=4tVz!u_Z+a5qVTZ$lt0fmW8RyG?R{sEA>U*nYmLLIHbuOq{c`?d-%FgW;QrBb4|Feb5ukaqvbPIv53- z8P##46YJ442{B=4zrY^7YR59YYR?H5hKhk40zNrbRHiPlYF5X6FWVyKy#%Z)VLaJS zaX{RNY3Np7+J$#YPeT-t(>dX}R%^&qSRb*vo8+QAr}a;e`|L)#ZB^*fOcSX} zSwmZ{XFxuNqrYHbo+|hYSe^O5)7&pNYXtS!W5*^^2k+S5U@tCfhp;ZI*xOu1jGbAv zU*?jFYzZF^D=07egu<#$nX89wGkfrI7jvOq zc44&yaga7CW8N%+W!<+b{#!I=kH(H19OH#O2+=jjf4eepb5%MvDu}>5ZVv~TnSc0` z%^N%3$cBhvWB2{fmTDx95LI5ezB-556buY@Eo?aOe0Zj+y7j4;lg9nb$Zm7x=O<~XPxmG9DuGg}DX$~y z8t%OAha`$OGi%tk|He#fn3#>1Pcffq6qH)I8kxxO3&Wl7;Twq}JPB^-HzhG6+dov~ zRiM|jJH7AU2p@V5-IBI$WzuRhvN3<%X4YnC^)!1MiFWQjvipv7Omy-Zb67tbj&+(V z8E}aP+kRF3n+T?RdA^%VEX+&zil!`*) z73Asgfc#pCKMaAGzFi0#Ysio}CuL~NkuFO=1(d40r_<(^5uzsGv8Mw5)~9je5o3(R z@w`1-GDWyOh83&*Ng=7c?k7`-7UezXJ7I4dAIcRTR$oIdZu122S*PadnJ-;}H>0yYuGTlfv#2Qsy@-vK=3sh+Pg zKRN8tUv!79-&vVC<9@)DRpm6N<#j(U}0);l|2gb9Kh#&+UBsomJN`lP9Ly!a1hbg=N^jzDB%VW zeUi}|sin7Pn|_3;p?0MNi`R$efN|HkX@F=&q7X$DXysk3*pIMRHD6PIMoX%VMy+cH z02RLXGFeI+XO~)mMlX+#cQ@GFmhFk+fur|4*l)eC0>O=%>GFs>_5zipJ+KFssafV% zXv3?x}v`(nL=Ja${+kMCg4r8}rFpLUdQ)*D2f zm@^^;nN*C8M@7VVNB6LAF^GAM6Hd^DIzLb1x(4n`_R6z=M*+V}-^Eq!f#nguVXJ1i z=)ie)`5uby*W6~Poq^ZeJ23R+MS-tdCopTcyQbFs~z%*Jy_pi;Y{i3=Q9IH zM}@MjgSG%ayXEHrv`R~GEs+;2O#4D4XDt5hd*h(3b=3kq*8}*~4+*b&=1-r2b^TTn z6lM#-?g?-X0qk?g{7uArU@Nd|GkV1VOM(s+SQdAHDQ@|0uDg6P-Z9@dyTa}EsD%6S z%;2YS@pBQ6^yS8;4yIslcngT`_3iy2IVXm&Y`8Qz{KCbXRHt!$E+J<1X0$^X`T+CI zVFvmU#3CUL`|PeM4xJ5jLMJK${B#$IKhSr~EQac$*nCHxYLAIR8DC?lyv_hw9DuRL zQltI#2aC>T3Q(|DO7yU8DT?>*J?pP0`XttqnPB`vK=YrdM`tw_MbD|50wb;9@|BH( za@nz)At0o-uormiEe$ROsV4M))^_|=txY#6@2$FkE)Ia#Z>66L2T_*gac*?3h}|ZE z*5yp~O2}CNJer3vg#+f1D=4~UC{AtWf?%L9Xk_AB&AzltmGwe*1%t(PIc$ z;ZwP(?2L!HP)tvlWd3xKXS|YFwxi&XTYf)WF$|sm*-(z3oa|WZ^nw-d0#mzjMg)o7 zGzOQx!mPQsp&MO)CbK;vl%FhMuT|7ow%&6>ogd=x0ke0v?DjK9Unhan+?oyvL9&U{ zWqnVqbEy@5hAt!|x_$&(z}O4xeVpe0_({I%^B}=0MQeeI2J=eNLTMv)A#t0t*5-8U=|f;rx>6q5yr8=5m>Ch8^sRD#)eww5slp%&?c#r; zNeDl_%E3*xo*Rm2ey4SSG@t6XZsW!sb8I`*&Hduv@C}F0KGy=zQE1LwqpdlemaNS; ztf#raoEu7c>*fXZeL`kyEup7!g{<2qv;sQiV6U`+chUAwucSUK7~`nk`yo*=9tV<6 zad}K-(l=((^KD$~=eT>aQZ*F3!NR+b+eTA6iS(~LXvfUpe?iA=n6Y`xFA8%cXCtk1 z6P3g!BbD-|%;)}vIov|2G9DNB!d9M{NbO{tgb zL9B=)c4Y~`c8gLM?Zkz-`&p+N*t?~?Xmb%*E&LhM1ck-a7w~6p{m0Cj#%^Eh2O%`Y zr{B1#$g{Je$cD#SYoExb5QV0>^Dk1XPI13K6>wbFrgw#It4c$XM42K*WHZ2zV}PjQ{};k#~s?<*COiEl)#vp(8-x@DV*tt zcYNN&BKYruF&-9zrWg9FwfD8)JwG=`9&UaK&pJ~-&JVk(5!~incz=qc7e}oP(bUa*(gXK2C)jEs#EL~N&fBgAxpWXYRpTWe zlNWZ}&CqNq_)qNj58qfpnd2M^}>yoN}$2Kil9% zPR+8Q=AQ!R+4y)%ILa!z_`I1#Wg$TFmzB&X%;&s#70MApaxv!i&rGVl*wQhj1!BM! zRXPX{3v})V%s=hG9!?jVgEBW(=RoGh%5~epnq%rt4kRnpSo#iGm_b4q4d1kzWhSU+ z)f)pt>Of8c1r1gDt4VDw_%(2l(Je2%>~+}sjXe}CdY;**xKKwYM&EPEjh5Yk-n{DM zXTxzBnC~-tI$eIsm**BWMpiq&hm7*=;5HqoTb}wCK98#Yt#D5`Gnr_8TFBY9; z7kwBNb`=Ex57nhT?#Aq08hAGUFO6dDiFGa)3<;~@mpK*!Nd3z5&dxjBohX+WZysImNLg_j@y2;b4#y9+4hQs~kS88{KWoNq zlP16|X5>0Myxf$nb>jy4bdhp!fZ}b=dgO|#cCP> zUpd$`mKreRpU+YHX!-|O*G~|x+!YD@dr4}c+i%h0Q7h}I(tVc%M4cTsIZOi71rEg3 zVzdvX=YE()QZU+`r>>QA*Jko5P*%+qd#}1H2n*v5J#y}5sLXVBrax+Z=k!LHH{>Ed zuakkkgYY_m%1V_@nTAn6`s{&u{5x$y!Fb1;H(%PjbZ$54Qugp$(uH*rVM0~$#ZNExBhm-^K+ncr`OfdfJ6Z=QyvcYu4NFz7KQ)IjLc^yix3!E8ptz~tG zAo}dQ^fNHm(YQ|WU%6#^$B_cB1R$Z*)&nKPfJ05TxVl%v-C7YZd8_XVhWm6JZt^qL zxWHS!#r=~`{IzmBim_z*&(7d5{gw=31P_Uesk$KspN>y{j^@3g&=<7r@qJBdf2Lwx zmXIZi@732zndY4Koc;S?ucAPF)V*C6kSEZ7yig&%(&v|JU`P&M{BsY_zu{@YEQZ_?Y$=0_{Hbt>OTy=gS9Va27D4CG% zoJEPz^%UAg^Z(9Ys#>2Em#`$v?b)-weN(A*eOO(gmT&zG5ft%zV=+FLr+o1Q71V&h7`e( z^B{XlWyuOzU$b3jrb@Q@XzRO~{^D^TL`cpAAQZMzo|Z_n8p~(~+7IFN6Dli61h;$U z1LTO7@z{0G+oy%Y4u7V)p5zusTR$(7DX>;+*B*&V&rASSH^cY+nKV^5+ANpBa)!3E zlDDO0!0~Iv&j&TaYh_1%;#u6%PY^7}_T}EZ7a*FPV4*JPkosX;4i4xUh~HjKrr!nS zrGS>8#wDJ;La<)$>V`^$1pz-h@j#th5NEzx;<%w9jc@k-vYR5~Q69nrdFj$vKASYn zM6_8r7Eto9n=#LUWb24wN*Jb3wCf?uP)4pgR4K3IK;pLP3r=&l5Yx8xxhL>w7*wBM zC2Nl)a0(^pta?-M90!{wrnqj5yDLYc_15%nf}<^^xV#pJbW>F10@1#{iYfsdxI z2IRg>AYK*3!!>qp4DfVU!iL@SeUq^^@Gv5+PRYG0t{Ka}&_%SSlxu@5ZSMzOnV1#S z3OQ$%opR+bE|E+*NGC<8+hI2nhH#Z7HKeLF;Sv`T(;fH3->jC!Ahgx#Q?N_gWDFWv z64KiE_yrCW*lByNY|G}sL(!v3K`IMTsIf)AaZ2);C#FbS$xcAWtqEZ*dPbt3lsOQF zXWSQHYDd^!?(aK$l*M(%I<@;M{ zKZw%A#4cUM?xO0l5)V*r53Yllk@%)jemcuirQGm+s;dFB;?NnLL5cMuGxukBsh-*n0*&@^tai{Qyq_)P#tL#Cv$xh0P3q@s@ z1Y_QFynW0_{2m5@W#_Nyj3t51F_KDl#lQ}_b_=efn|qO#HxmL$SM>9sQwuQH+bY6) z9;|`@CS5wJMY_ky<~|O6kD}=S!k_jkS-YL=Fx9vzbwTOH1OUKY&LX%$g(!b=ENAo1F3H)q0v>cln!ASsrp~#^!OPCH7s~l)OKv-~YyWnlP zF5M7bU7P3Vj+aa32RTlpuzr)s0cTvJ$mW%lQ}V9-brtDZ=8t#M%H;vNNJvD*TL-@; zKtxQMQae$`@IVl=AbQ3V`>y|G5w>6v?zRt|4V3=N-LILUod$fPnDEA7$*n zIYwP#AI?XRFp$K37htu7mm^?c{SXF~hfHr;-V7lH%AR+dZw;WH1y|=~QdMq+x&{b)mfAVgyp8K|tv(h2 z+V}ugs3StDVlm9Ld1y6^J**GKX(v?c3%O7#%6z=8*w(HTF&bx$b2>x6h#a1^G9GtS@mniQ-)O{4viuYntHPWXddzEGs93S({<6 zQ}L;QJS_x0V9|hp4M+F1QGShHf+9ogb;_VAvs$kjeTEcdh;21oz7Z>6hMk`C^j1x@ z8YTuJ9Ho&Ob2uDH(SIbF%zU1VZ8Qa?Ie^G*YrZcpT4tugJ!v0kR-`AC17dAQC|i!n zS@$ygnjg_Q849Z=yrzxlV>5ekG_LD}x!2m2AbA_Le*9I{62-^R~6`x!$%xaGnO1{`N}QH>p^dYZwUFZoYz^==D74TYO?)TvD>wlg%mA zBEV{$e*X$oZT8-}3{5QsP0cZ+q7pxPMOs>8E?h2dkMUD5QsFTW`VP46Bq*XnK$lSB zXQ0bsI^aNMLFL@qpz`yM6oq3hQXGZ<0%~*X6gaBFhMk%b+HtTCB-n=``$$^fCz<#} zp%g~8=l%io>%HB(unk{kF5AKk$H4^V@Bdm!hI+)V{Vy)5KpkN&D}R79Pq)6?>I0A~ zNn4<(k9qS{ovkP0t*Z1Hw7k0h2N~8CRn~z*9wiG%6@jFdUMmI?x*c)E--C(H&EomD zPsaa`>b-90q8bx+vU+d#>cc@j2!>z~9h5WlSle~|U9(lE72WdT!#p@sR16q72P&RQFG4P$!t^bus zcy%DB3L)M7313qnvp>#(h~a-}iX zcl_P&HE7-5`%lw^7e3ZHik+!<`nGiSXN%W4)8lPaZNU^MZ|+T$U5KZ`dI#G;+^5>_G}J^PiD2d!bR#;^P3 zbHIqs>b%h{TP18WkHv1aPAoT^q{;iUv?O~^?+U*6E*bWDmKap;#OmoX=U@)#UWx*& zt4|i8i(;NVB7gv~)OmWc*xrYs_x`p(_Tkaosj9(C)!S8XlI<<9Uc-ItgB~ipOx~U8 z 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
y)$ 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 literal 0 HcmV?d00001 diff --git a/infra/main.bicep b/infra/main.bicep index 92ef4365..37caa02a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -23,23 +23,13 @@ var solutionSuffix= toLower(trim(replace( '' ))) -@description(''' -gpt-35-turbo-16k 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 - -@description(''' -text-embedding-ada-002 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 = 1 +@description('Optional. Capacity of the GPT model deployment:') +@minValue(10) +param gptModelCapacity int = 150 + +@description('Optional. Capacity of the Text Embedding model deployment:') +@minValue(10) +param embeddingModelCapacity int = 100 @description('Optional. The tags to apply to all deployed Azure resources.') param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} @@ -73,18 +63,17 @@ param enableRedundancy bool = false @description('Optional. Enable scalability for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false.') param enableScalability bool = false -@description('Optional. Enable purge protection for the Key Vault') -param enablePurgeProtection bool = false - -@minLength(1) -@description('Optional. Name of the Text Embedding model to deploy:') -@allowed([ - 'text-embedding-ada-002' -]) -param embeddingModel string = 'text-embedding-ada-002' - -@description('Optional. Contains Azure GPT 40 Model Name.') -param azureGpt40ModelName string = '' +@metadata({ + azd: { + type: 'location' + usageName: [ + 'OpenAI.GlobalStandard.gpt4.1-mini,150' + 'OpenAI.GlobalStandard.text-embedding-3-large,100' + ] + } +}) +@description('Required. Location for AI Foundry deployment. This is the location where the AI Foundry resources will be deployed.') +param aiDeploymentsLocation string var solutionLocation = empty(location) ? resourceGroup().location : location @@ -105,88 +94,90 @@ var replicaRegionPairs = { } var replicaLocation = replicaRegionPairs[solutionLocation] -@description('Optional. The Container Registry hostname where the docker images for the container app are located.') -param containerRegistryHostname string = 'biabcontainerreg.azurecr.io' - -@description('Optional. The Container Image Name to deploy on the container app.') -param containerImageName string = 'macaebackend' +// Region pairs list based on article in [Azure Database for MySQL Flexible Server - Azure Regions](https://learn.microsoft.com/azure/mysql/flexible-server/overview#azure-regions) for supported high availability regions for CosmosDB. +var cosmosDbZoneRedundantHaRegionPairs = { + australiaeast: 'uksouth' //'southeastasia' + centralus: 'eastus2' + eastasia: 'southeastasia' + eastus: 'centralus' + eastus2: 'centralus' + japaneast: 'australiaeast' + northeurope: 'westeurope' + southeastasia: 'eastasia' + uksouth: 'westeurope' + westeurope: 'northeurope' +} -@description('Optional. The Container Image Tag to deploy on the container app.') -param containerImageTag string = 'latest_2025-07-22_895' +// Paired location calculated based on 'location' parameter. This location will be used by applicable resources if `enableScalability` is set to `true` +var cosmosDbHaLocation = cosmosDbZoneRedundantHaRegionPairs[resourceGroup().location] // Extracts subscription, resource group, and workspace name from the resource ID when using an existing Log Analytics workspace var useExistingLogAnalytics = !empty(existingLogAnalyticsWorkspaceId) -var chatGpt = { +var gptModelDeployment = { modelName: 'gpt-4.1-mini' deploymentName: 'gpt-4.1-mini' deploymentVersion: '2025-04-14' - deploymentCapacity: chatGptDeploymentCapacity + deploymentCapacity: gptModelCapacity } -var embedding = { +var embeddingModelDeployment = { modelName: 'text-embedding-3-large' deploymentName: 'text-embedding-3-large' deploymentVersion: '1' - deploymentCapacity: embeddingDeploymentCapacity + deploymentCapacity: embeddingModelCapacity } var openAiDeployments = [ { - name: chatGpt.deploymentName + name: gptModelDeployment.deploymentName model: { format: 'OpenAI' - name: chatGpt.modelName - version: chatGpt.deploymentVersion + name: gptModelDeployment.modelName + version: gptModelDeployment.deploymentVersion } sku: { name: 'GlobalStandard' - capacity: chatGpt.deploymentCapacity + capacity: gptModelDeployment.deploymentCapacity } } { - name: embedding.deploymentName + name: embeddingModelDeployment.deploymentName model: { format: 'OpenAI' - name: embedding.modelName - version: embedding.deploymentVersion + name: embeddingModelDeployment.modelName + version: embeddingModelDeployment.deploymentVersion } sku: { name: 'GlobalStandard' - capacity: embedding.deploymentCapacity + capacity: embeddingModelDeployment.deploymentCapacity } } ] // ========== Private DNS Zones ========== // var privateDnsZones = [ + 'privatelink.mongo.cosmos.azure.com' + 'privatelink.search.windows.net' 'privatelink.cognitiveservices.azure.com' 'privatelink.openai.azure.com' - 'privatelink.services.ai.azure.com' // Todo: to be deleted 'privatelink.blob.${environment().suffixes.storage}' 'privatelink.queue.${environment().suffixes.storage}' - 'privatelink.file.${environment().suffixes.storage}' // Todo: to be deleted 'privatelink.api.azureml.ms' - 'privatelink.mongo.cosmos.azure.com' 'privatelink.azconfig.io' - 'privatelink.vaultcore.azure.net' // Todo: to be deleted 'privatelink.azurecr.io' // Todo: to be deleted - 'privatelink.search.windows.net' ] // DNS Zone Index Constants var dnsZoneIndex = { - cognitiveServices: 0 - openAI: 1 - aiServices: 2 - storageBlob: 3 - storageQueue: 4 - storageFile: 5 + cosmosDB: 0 + search: 1 + cognitiveServices: 2 + openAI: 3 + storageBlob: 4 + storageQueue: 5 aiFoundry: 6 - cosmosDB: 7 - appConfig: 8 - keyVault: 9 - containerRegistry: 10 - search: 11 + appConfig: 7 + containerRegistry: 8 } @batchSize(5) module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.7.1' = [ @@ -201,18 +192,6 @@ module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.7.1' = [ } ] -@metadata({ - azd: { - type: 'location' - usageName: [ - 'OpenAI.GlobalStandard.gpt-4o-mini,150' - 'OpenAI.GlobalStandard.text-embedding-ada-002,80' - ] - } -}) -@description('Required. Location for AI Foundry deployment. This is the location where the AI Foundry resources will be deployed.') -param aiDeploymentsLocation string - // ========== Log Analytics Workspace ========== // // WAF best practices for Log Analytics: https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-log-analytics // WAF PSRules for Log Analytics: https://azure.github.io/PSRule.Rules.Azure/en/rules/resource/#azure-monitor-logs @@ -277,6 +256,7 @@ module logAnalyticsWorkspace 'br/public:avm/res/operational-insights/workspace:0 } var logAnalyticsWorkspaceResourceId = useExistingLogAnalytics ? existingLogAnalyticsWorkspaceId : logAnalyticsWorkspace!.outputs.resourceId +// ========== Network Module ========== // module network 'modules/network.bicep' = if (enablePrivateNetworking) { name: take('network-${solutionSuffix}-deployment', 64) params: { @@ -339,16 +319,11 @@ module avmCosmosDB 'br/public:avm/res/document-db/database-account:0.15.0' = { tags: tags enableTelemetry: enableTelemetry databaseAccountOfferType: 'Standard' - automaticFailover: false serverVersion: '7.0' - capabilitiesToAdd: [ - 'EnableMongo' - ] enableAnalyticalStorage: true defaultConsistencyLevel: 'Session' maxIntervalInSeconds: 5 maxStalenessPrefix: 100 - zoneRedundant: false // WAF related parameters networkRestrictions: { @@ -373,7 +348,34 @@ module avmCosmosDB 'br/public:avm/res/document-db/database-account:0.15.0' = { } ] : [] - } + // WAF aligned configuration for Redundancy + zoneRedundant: enableRedundancy ? true : false + capabilitiesToAdd: [ + 'EnableMongo' + ] + //capabilitiesToAdd: enableRedundancy ? null : ['EnableServerless'] + automaticFailover: enableRedundancy ? true : false + failoverLocations: enableRedundancy + ? [ + { + failoverPriority: 0 + isZoneRedundant: true + locationName: solutionLocation + } + { + failoverPriority: 1 + isZoneRedundant: true + locationName: cosmosDbHaLocation + } + ] + : [ + { + locationName: solutionLocation + failoverPriority: 0 + isZoneRedundant: enableRedundancy + } + ] + } } // ========== App Configuration store ========== // @@ -403,7 +405,7 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 } { name: 'Application:AIServices:GPT-4o-mini:ModelName' - value: chatGpt.modelName + value: gptModelDeployment.modelName } { name: 'Application:Services:KernelMemory:Endpoint' @@ -467,7 +469,7 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 } { name: 'KernelMemory:Services:AzureOpenAIEmbedding:Deployment' - value: embedding.deploymentName + value: embeddingModelDeployment.deploymentName } { name: 'KernelMemory:Services:AzureOpenAIEmbedding:Endpoint' @@ -479,7 +481,7 @@ module avmAppConfig 'br/public:avm/res/app-configuration/configuration-store:0.6 } { name: 'KernelMemory:Services:AzureOpenAIText:Deployment' - value: chatGpt.deploymentName + value: gptModelDeployment.deploymentName } { name: 'KernelMemory:Services:AzureOpenAIText:Endpoint' @@ -610,9 +612,6 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { // ========== AI Foundry: AI Search ========== // var aiSearchName = 'srch-${solutionSuffix}' -// var aiSearchConnectionName = 'myCon-${solutionSuffix}' -// var varKvSecretNameAzureSearchKey = 'AZURE-SEARCH-KEY' -// AI Foundry: AI Search module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = { name: take('avm.res.cognitive-search-services.${aiSearchName}', 64) params: { @@ -661,13 +660,13 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = } } -// // ========== Cognitive Services - OpenAI module ========== // +// ========== Cognitive Services - OpenAI module ========== // var openAiAccountName = 'oai-${solutionSuffix}' module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { name: take('avm.res.cognitiveservices.account.${openAiAccountName}', 64) params: { name: openAiAccountName - location: solutionLocation + location: aiDeploymentsLocation kind: 'OpenAI' sku: 'S0' tags: tags @@ -788,9 +787,6 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10. publicNetworkAccess: 'Enabled' managedIdentities: { systemAssigned: true - // userAssignedResourceIds: [ - // userAssignedIdentity.outputs.resourceId - // ] } serviceCidr: '10.20.0.0/16' dnsServiceIP: '10.20.0.10' @@ -803,9 +799,50 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10. osType: 'Linux' mode: 'System' type: 'VirtualMachineScaleSets' - vnetSubnetResourceId: enableMonitoring ? network!.outputs.subnetWebResourceId : null + minCount: 1 + maxCount: 2 + + // WAF aligned configuration for Private Networking + enableAutoScaling: true + scaleSetEvictionPolicy: 'Delete' + scaleSetPriority: 'Regular' + vnetSubnetResourceId: enablePrivateNetworking ? network!.outputs.subnetWebResourceId : null } ] + autoNodeOsUpgradeProfileUpgradeChannel: 'Unmanaged' + autoUpgradeProfileUpgradeChannel: 'stable' + enableAzureDefender: enablePrivateNetworking + networkPlugin: 'azure' + networkPolicy: 'azure' + omsAgentEnabled: true + // WAF aligned configuration for Monitoring + diagnosticSettings: enableMonitoring ? [ + { + logCategoriesAndGroups: [ + { + category: 'kube-apiserver' + } + { + category: 'kube-controller-manager' + } + { + category: 'kube-scheduler' + } + { + category: 'cluster-autoscaler' + } + ] + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + workspaceResourceId: logAnalyticsWorkspaceResourceId + } + ] : [] + monitoringWorkspaceResourceId: enableMonitoring ? logAnalyticsWorkspaceResourceId : null + roleAssignments: [ { principalId: userAssignedIdentity.outputs.principalId @@ -813,9 +850,6 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10. principalType: 'ServicePrincipal' } ] - // WAF aligned configuration for Monitoring - monitoringWorkspaceResourceId: enableMonitoring ? logAnalyticsWorkspaceResourceId : null - // WAF aligned configuration for Private Networking } } @@ -841,6 +875,7 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en /* Outputs */ +@description('Contains Azure Tenant ID.') output AZURE_TENANT_ID string = subscription().tenantId @description('Contains Solution Name.') @@ -852,13 +887,16 @@ output RESOURCE_GROUP_NAME string = resourceGroup().name @description('Contains Resource Group Location.') output RESOURCE_GROUP_LOCATION string = solutionLocation +@description('Contains Resource Group ID.') output AZURE_RESOURCE_GROUP_ID string = resourceGroup().id +@description('Contains Azure App Configuration Name.') output AZURE_APP_CONFIG_NAME string = avmAppConfig.outputs.name +@description('Contains Azure App Configuration Endpoint.') output AZURE_APP_CONFIG_ENDPOINT string = avmAppConfig.outputs.endpoint -@description('Contains Resource Group Name.') +@description('Contains Storage Account Name.') output STORAGE_ACCOUNT_NAME string = avmStorageAccount.outputs.name @description('Contains Cosmos DB Name.') @@ -873,16 +911,16 @@ output AZURE_COGNITIVE_SERVICE_ENDPOINT string = documentIntelligence.outputs.en @description('Contains Azure Search Service Name.') output AZURE_SEARCH_SERVICE_NAME string = avmSearchSearchServices.outputs.name -@description('Contains Azure Search Service Name.') +@description('Contains Azure AKS Name.') output AZURE_AKS_NAME string = managedCluster.outputs.name -@description('Contains Azure Search Service Name.') +@description('Contains Azure AKS Managed Identity ID.') output AZURE_AKS_MI_ID string = managedCluster.outputs.systemAssignedMIPrincipalId -@description('Contains Azure Search Service Name.') +@description('Contains Azure Container Registry Name.') output AZURE_CONTAINER_REGISTRY_NAME string = avmContainerRegistry.outputs.name -@description('Contains Azure OpenAI Search Service Name.') +@description('Contains Azure OpenAI Service Name.') output AZURE_OPENAI_SERVICE_NAME string = avmOpenAi.outputs.name @description('Contains Azure OpenAI Service Endpoint.') @@ -891,17 +929,17 @@ output AZURE_OPENAI_SERVICE_ENDPOINT string = avmOpenAi.outputs.endpoint @description('Contains Azure Search Service Endpoint.') output AZ_SEARCH_SERVICE_ENDPOINT string = avmSearchSearchServices.outputs.name -@description('Contains Azure GPT40 Model ID.') -output AZ_GPT4O_MODEL_ID string = chatGpt.deploymentName +@description('Contains Azure GPT-4o Model Deployment Name.') +output AZ_GPT4O_MODEL_ID string = gptModelDeployment.deploymentName -@description('Contains Azure OpenAI embedding model name.') -output AZ_GPT4O_MODEL_NAME string = chatGpt.modelName +@description('Contains Azure GPT-4o Model Name.') +output AZ_GPT4O_MODEL_NAME string = gptModelDeployment.modelName -@description('Contains Azure OpenAI embedding model name.') -output AZ_GPT_EMBEDDING_MODEL_NAME string = embedding.modelName +@description('Contains Azure OpenAI Embedding Model Name.') +output AZ_GPT_EMBEDDING_MODEL_NAME string = embeddingModelDeployment.modelName -@description('Contains Azure OpenAI embedding model name.') -output AZ_GPT_EMBEDDING_MODEL_ID string = embedding.deploymentName +@description('Contains Azure OpenAI Embedding Model Deployment Name.') +output AZ_GPT_EMBEDDING_MODEL_ID string = embeddingModelDeployment.deploymentName // @description('The FQDN of the frontend web app service.') // output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN diff --git a/infra/modules/network.bicep b/infra/modules/network.bicep index 18c28944..a4a3b7ba 100644 --- a/infra/modules/network.bicep +++ b/infra/modules/network.bicep @@ -103,7 +103,7 @@ module network 'network/main.bicep' = { sourcePortRange: '*' destinationPortRange: '80' sourceAddressPrefixes: ['0.0.0.0/0'] - destinationAddressPrefixes: ['10.0.0.0/23'] + destinationAddressPrefix: '*' } } { From f996a4e932242034386468fa5d24d298776b83ee Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Thu, 11 Sep 2025 13:57:16 +0530 Subject: [PATCH 17/29] deployment guide azd changes --- docs/DeploymentGuide.md | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 2986e36c..a67832b7 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -56,6 +56,48 @@ The automated deployment process is very straightforward and simplified via a si 6. Update Kubernetes configuration files. 7. Deploy certificates, ingress controller and then application images from Azure Container Registry. +### Deploying with AZD + +Once you've opened the project in [Codespaces](#github-codespaces), [Dev Containers](#vs-code-dev-containers), or [locally](#local-environment), you can deploy it to Azure by following these steps: + +1. Clone the repository or download the project code via command-line: + + ```cmd + git clone https://github.com/microsoft/Document-Knowledge-Mining-Solution-Accelerator + ``` + + Open the cloned repository in Visual Studio Code and connect to the development container. + + ```cmd + code . + ``` + +2. Login to Azure: + + ```shell + azd auth login + ``` + + #### To authenticate with Azure Developer CLI (`azd`), use the following command with your **Tenant ID**: + + ```sh + azd auth login --tenant-id + ``` + +3. Provision and deploy all the resources: + + ```shell + azd up + ``` + +4. Provide an `azd` environment name (e.g., "ckmapp"). +5. Select a subscription from your Azure account and choose a location that has quota for all the resources. + -- This deployment will take *7-10 minutes* to provision the resources in your account and set up the solution with sample data. + - If you encounter an error or timeout during deployment, changing the location may help, as there could be availability constraints for the resources. + +6. Once the deployment has completed successfully, open the [Azure Portal](https://portal.azure.com/), go to the deployed resource group, find the App Service, and get the app URL from `Default domain`. + +7. If you are done trying out the application, you can delete the resources by running `azd down`. ### Execute Deployment Script: Open PowerShell, change directory where you code cloned, then run the deploy script: @@ -137,3 +179,9 @@ Execute uploadfiles.ps1 file with **-EndpointUrl** parameter as URL in console m ``` .\uploadfiles.ps1 -EndpointUrl https://kmgs..cloudapp.azure.com ``` + +If you run into issue with PowerShell script file not being digitally signed, you can execute below command: + +``` +powershell.exe -ExecutionPolicy Bypass -File ".\uploadfiles.ps1" -EndpointUrl https://kmgs..cloudapp.azure.com +``` \ No newline at end of file From e13f9accdd67e8e706b822b41d9c6fe58e12d3f9 Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Thu, 11 Sep 2025 14:04:39 +0530 Subject: [PATCH 18/29] deployment steps altered --- docs/DeploymentGuide.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index a67832b7..bc729a96 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -44,18 +44,6 @@ ## Deployment -The automated deployment process is very straightforward and simplified via a single [deployment script](../Deployment/resourcedeployment.ps1) that completes in approximately 10-15 minutes: - -### Automated Deployment Steps: -1. Deploy Azure resources. -2. Get secret information from Azure resources. -3. Update application configuration files with secrets. -4. Set Application Configuration in Azure App Configuration. -4. Compile application, build image, and push to Azure Container Registry. -5. Configure Kubernetes cluster infrastructure. -6. Update Kubernetes configuration files. -7. Deploy certificates, ingress controller and then application images from Azure Container Registry. - ### Deploying with AZD Once you've opened the project in [Codespaces](#github-codespaces), [Dev Containers](#vs-code-dev-containers), or [locally](#local-environment), you can deploy it to Azure by following these steps: @@ -99,7 +87,20 @@ Once you've opened the project in [Codespaces](#github-codespaces), [Dev Contain 7. If you are done trying out the application, you can delete the resources by running `azd down`. -### Execute Deployment Script: +### Post Deployment Script: + +The post deployment process is very straightforward and simplified via a single [deployment script](../Deployment/resourcedeployment.ps1) that completes in approximately 20-30 minutes: + +### Automated Deployment Steps: +1. Deploy Azure resources. +2. Get secret information from Azure resources. +3. Update application configuration files with secrets. +4. Set Application Configuration in Azure App Configuration. +4. Compile application, build image, and push to Azure Container Registry. +5. Configure Kubernetes cluster infrastructure. +6. Update Kubernetes configuration files. +7. Deploy certificates, ingress controller and then application images from Azure Container Registry. + Open PowerShell, change directory where you code cloned, then run the deploy script: ``` From cd9ce22cc44306d6aacd724abb17bdc863ed5a8c Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Thu, 11 Sep 2025 15:30:08 +0530 Subject: [PATCH 19/29] feat: Added parameters.json & waf version of it, fixed bicep warnings --- infra/main.bicep | 91 +- infra/main.json | 52091 ++++++++++++++++++- infra/main.parameters.json | 32 + infra/main.waf.parameters.json | 59 + infra/modules/network.bicep | 1 - infra/modules/network/bastionHost.bicep | 6 +- infra/modules/network/jumpbox.bicep | 8 +- infra/modules/network/main.bicep | 18 +- infra/modules/network/virtualNetwork.bicep | 2 +- 9 files changed, 51175 insertions(+), 1133 deletions(-) create mode 100644 infra/main.waf.parameters.json diff --git a/infra/main.bicep b/infra/main.bicep index 37caa02a..e8d197ee 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -23,23 +23,42 @@ var solutionSuffix= toLower(trim(replace( '' ))) +@minLength(1) +@description('Optional. GPT model deployment type:') +@allowed([ + 'Standard' + 'GlobalStandard' +]) +param gptModelDeploymentType string = 'GlobalStandard' + +@minLength(1) +@description('Optional. Name of the GPT model to deploy:') +@allowed([ + 'gpt-4.1-mini' +]) +param gptModelName string = 'gpt-4.1-mini' + +@description('Optional. Version of the GPT model to deploy.') +param gptModelVersion string = '2025-04-14' + @description('Optional. Capacity of the GPT model deployment:') @minValue(10) param gptModelCapacity int = 150 +@minLength(1) +@description('Optional. Name of the Text Embedding model to deploy:') +@allowed([ + 'text-embedding-3-large' +]) +param embeddingModelName string = 'text-embedding-3-large' + +@description('Optional. Version of the Text Embedding model to deploy.') +param embeddingModelVersion string = '1' + @description('Optional. Capacity of the Text Embedding model deployment:') @minValue(10) param embeddingModelCapacity int = 100 -@description('Optional. The tags to apply to all deployed Azure resources.') -param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} - -@description('Optional. Enable/Disable usage telemetry for module.') -param enableTelemetry bool = true - -@description('Optional. Enable private networking for applicable resources, aligned with the WAF recommendations. Defaults to false.') -param enablePrivateNetworking bool = true - @description('Optional: Existing Log Analytics Workspace Resource ID') param existingLogAnalyticsWorkspaceId string = '' @@ -54,8 +73,17 @@ param vmAdminPassword string? @description('Optional. Size of the Jumpbox Virtual Machine when created. Set to custom value if enablePrivateNetworking is true.') param vmSize string = 'Standard_DS2_v2' +@description('Optional. The tags to apply to all deployed Azure resources.') +param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Enable private networking for applicable resources, aligned with the WAF recommendations. Defaults to false.') +param enablePrivateNetworking bool = false + @description('Optional. Enable monitoring applicable resources, aligned with the Well Architected Framework recommendations. This setting enables Application Insights and Log Analytics and configures all the resources applicable resources to send logs. Defaults to false.') -param enableMonitoring bool = true +param enableMonitoring bool = false @description('Optional. Enable redundancy for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false.') param enableRedundancy bool = false @@ -115,16 +143,16 @@ var cosmosDbHaLocation = cosmosDbZoneRedundantHaRegionPairs[resourceGroup().loca var useExistingLogAnalytics = !empty(existingLogAnalyticsWorkspaceId) var gptModelDeployment = { - modelName: 'gpt-4.1-mini' - deploymentName: 'gpt-4.1-mini' - deploymentVersion: '2025-04-14' + modelName: gptModelName + deploymentName: gptModelName + deploymentVersion: gptModelVersion deploymentCapacity: gptModelCapacity } var embeddingModelDeployment = { - modelName: 'text-embedding-3-large' - deploymentName: 'text-embedding-3-large' - deploymentVersion: '1' + modelName: embeddingModelName + deploymentName: embeddingModelName + deploymentVersion: embeddingModelVersion deploymentCapacity: embeddingModelCapacity } @@ -137,7 +165,7 @@ var openAiDeployments = [ version: gptModelDeployment.deploymentVersion } sku: { - name: 'GlobalStandard' + name: gptModelDeploymentType capacity: gptModelDeployment.deploymentCapacity } } @@ -149,7 +177,7 @@ var openAiDeployments = [ version: embeddingModelDeployment.deploymentVersion } sku: { - name: 'GlobalStandard' + name: gptModelDeploymentType capacity: embeddingModelDeployment.deploymentCapacity } } @@ -522,7 +550,7 @@ module avmAppConfigUpdated 'br/public:avm/res/app-configuration/configuration-st privateDnsZoneGroupConfigs: [ { name: 'appconfig-dns-zone-group' - privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.appConfig].outputs.resourceId + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.appConfig]!.outputs.resourceId } ] } @@ -578,7 +606,7 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { } ] } - subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId service: 'blob' } { @@ -591,7 +619,7 @@ module avmStorageAccount 'br/public:avm/res/storage/storage-account:0.20.0' = { } ] } - subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId service: 'queue' } ] @@ -653,7 +681,7 @@ module avmSearchSearchServices 'br/public:avm/res/search/search-service:0.9.1' = { privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.search]!.outputs.resourceId } ] } - subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId } ] : [] @@ -687,7 +715,7 @@ module avmOpenAi 'br/public:avm/res/cognitive-services/account:0.13.2' = { ? [ { name: 'pep-openai-${solutionSuffix}' - subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId service: 'account' privateDnsZoneGroup: { privateDnsZoneGroupConfigs: [ @@ -747,7 +775,7 @@ module documentIntelligence 'br/public:avm/res/cognitive-services/account:0.13.2 ? [ { name: 'pep-docintel-${solutionSuffix}' - subnetResourceId: network.outputs.subnetPrivateEndpointsResourceId + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId service: 'account' privateDnsZoneGroup: { privateDnsZoneGroupConfigs: [ @@ -801,7 +829,7 @@ module managedCluster 'br/public:avm/res/container-service/managed-cluster:0.10. type: 'VirtualMachineScaleSets' minCount: 1 maxCount: 2 - + // WAF aligned configuration for Private Networking enableAutoScaling: true scaleSetEvictionPolicy: 'Delete' @@ -867,8 +895,8 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en disableIpMasking: false flowType: 'Bluefield' // WAF aligned configuration for Monitoring - workspaceResourceId: enableMonitoring ? logAnalyticsWorkspace.outputs.resourceId : '' - diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspace.outputs.resourceId }] : null + workspaceResourceId: enableMonitoring ? logAnalyticsWorkspace!.outputs.resourceId : '' + diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspace!.outputs.resourceId }] : null } } @@ -940,12 +968,3 @@ output AZ_GPT_EMBEDDING_MODEL_NAME string = embeddingModelDeployment.modelName @description('Contains Azure OpenAI Embedding Model Deployment Name.') output AZ_GPT_EMBEDDING_MODEL_ID string = embeddingModelDeployment.deploymentName - -// @description('The FQDN of the frontend web app service.') -// output kmServiceEndpoint string = containerAppService.outputs.kmServiceFQDN - -// @description('Service Access Key 1.') -// output kmServiceAccessKey1 string = containerAppService.outputs.kmServiceAccessKey1 - -// @description('Service Access Key 2.') -// output kmServiceAccessKey2 string = containerAppService.outputs.kmServiceAccessKey2 diff --git a/infra/main.json b/infra/main.json index e14f79f0..c2c5cf3c 100644 --- a/infra/main.json +++ b/infra/main.json @@ -1,1402 +1,51335 @@ { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "14745838150782572756" + "version": "0.37.4.10188", + "templateHash": "8418820476800736272" } }, "parameters": { - "suffix": { + "solutionName": { "type": "string", - "defaultValue": "[substring(newGuid(), 0, 6)]", - "minLength": 4, - "maxLength": 6, + "defaultValue": "kmgs", + "minLength": 3, + "maxLength": 20, "metadata": { - "description": "Suffix to create unique resource names; 4-6 characters. Default is a random 6 characters." + "description": "Required. A unique prefix for all resources in this deployment. This should be 3-20 characters long:" } }, - "chatGptDeploymentCapacity": { + "location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Azure location for the solution. If not provided, it defaults to the resource group location." + } + }, + "solutionUniqueToken": { + "type": "string", + "defaultValue": "[substring(uniqueString(subscription().id, resourceGroup().name, parameters('solutionName')), 0, 5)]", + "maxLength": 5, + "metadata": { + "description": "Optional. A unique token for the solution. This is used to ensure resource names are unique for global resources. Defaults to a 5-character substring of the unique string generated from the subscription ID, resource group name, and solution name." + } + }, + "gptModelDeploymentType": { + "type": "string", + "defaultValue": "GlobalStandard", + "allowedValues": [ + "Standard", + "GlobalStandard" + ], + "minLength": 1, + "metadata": { + "description": "Optional. GPT model deployment type:" + } + }, + "gptModelName": { + "type": "string", + "defaultValue": "gpt-4.1-mini", + "allowedValues": [ + "gpt-4.1-mini" + ], + "minLength": 1, + "metadata": { + "description": "Optional. Name of the GPT model to deploy:" + } + }, + "gptModelVersion": { + "type": "string", + "defaultValue": "2025-04-14", + "metadata": { + "description": "Optional. Version of the GPT model to deploy." + } + }, + "gptModelCapacity": { "type": "int", - "defaultValue": 30, - "minValue": 1, - "maxValue": 40, + "defaultValue": 150, + "minValue": 10, + "metadata": { + "description": "Optional. Capacity of the GPT model deployment:" + } + }, + "embeddingModelName": { + "type": "string", + "defaultValue": "text-embedding-3-large", + "allowedValues": [ + "text-embedding-3-large" + ], + "minLength": 1, "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": "Optional. Name of the Text Embedding model to deploy:" } }, - "embeddingDeploymentCapacity": { + "embeddingModelVersion": { + "type": "string", + "defaultValue": "1", + "metadata": { + "description": "Optional. Version of the Text Embedding model to deploy." + } + }, + "embeddingModelCapacity": { "type": "int", - "defaultValue": 30, - "minValue": 1, - "maxValue": 40, + "defaultValue": 100, + "minValue": 10, + "metadata": { + "description": "Optional. Capacity of the Text Embedding model deployment:" + } + }, + "existingLogAnalyticsWorkspaceId": { + "type": "string", + "defaultValue": "", "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": "Optional: Existing Log Analytics Workspace Resource ID" } }, - "WebServiceAuthorizationKey1": { + "vmAdminUsername": { "type": "securestring", - "minLength": 32, - "maxLength": 128, + "nullable": true, "metadata": { - "description": "PLEASE CHOOSE A SECURE AND SECRET KEY ! -\nKernel Memory Service Authorization AccessKey 1.\nThe value is stored as an environment variable and is required by the web service to authenticate HTTP requests.\n" + "description": "Optional. Admin username for the Jumpbox Virtual Machine. Set to custom value if enablePrivateNetworking is true." } }, - "WebServiceAuthorizationKey2": { + "vmAdminPassword": { "type": "securestring", - "minLength": 32, - "maxLength": 128, + "nullable": true, "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" + "description": "Optional. Admin password for the Jumpbox Virtual Machine. Set to custom value if enablePrivateNetworking is true." + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_DS2_v2", + "metadata": { + "description": "Optional. Size of the Jumpbox Virtual Machine when created. Set to custom value if enablePrivateNetworking is true." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Resources/resourceGroups@2025-04-01#properties/tags" + }, + "description": "Optional. The tags to apply to all deployed Azure resources." + }, + "defaultValue": {} + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "enablePrivateNetworking": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable private networking for applicable resources, aligned with the WAF recommendations. Defaults to false." + } + }, + "enableMonitoring": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable monitoring applicable resources, aligned with the Well Architected Framework recommendations. This setting enables Application Insights and Log Analytics and configures all the resources applicable resources to send logs. Defaults to false." + } + }, + "enableRedundancy": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable redundancy for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false." + } + }, + "enableScalability": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable scalability for applicable resources, aligned with the Well Architected Framework recommendations. Defaults to false." + } + }, + "aiDeploymentsLocation": { + "type": "string", + "metadata": { + "azd": { + "type": "location", + "usageName": [ + "OpenAI.GlobalStandard.gpt4.1-mini,150", + "OpenAI.GlobalStandard.text-embedding-3-large,100" + ] + }, + "description": "Required. Location for AI Foundry deployment. This is the location where the AI Foundry resources will be deployed." } } }, "variables": { - "rg": "[resourceGroup()]", - "location": "[resourceGroup().location]", - "chatGpt": { - "modelName": "gpt-35-turbo-16k", - "deploymentName": "chat", - "deploymentVersion": "0613", - "deploymentCapacity": "[parameters('chatGptDeploymentCapacity')]" + "solutionSuffix": "[toLower(trim(replace(replace(replace(replace(replace(replace(format('{0}{1}', parameters('solutionName'), parameters('solutionUniqueToken')), '-', ''), '_', ''), '.', ''), '/', ''), ' ', ''), '*', '')))]", + "solutionLocation": "[if(empty(parameters('location')), resourceGroup().location, parameters('location'))]", + "replicaRegionPairs": { + "australiaeast": "australiasoutheast", + "centralus": "westus", + "eastasia": "japaneast", + "eastus": "centralus", + "eastus2": "centralus", + "japaneast": "eastasia", + "northeurope": "westeurope", + "southeastasia": "eastasia", + "uksouth": "westeurope", + "westeurope": "northeurope" }, - "embedding": { - "modelName": "text-embedding-ada-002", - "deploymentName": "embedding", - "deploymentVersion": "2", - "deploymentCapacity": "[parameters('embeddingDeploymentCapacity')]" + "replicaLocation": "[variables('replicaRegionPairs')[variables('solutionLocation')]]", + "cosmosDbZoneRedundantHaRegionPairs": { + "australiaeast": "uksouth", + "centralus": "eastus2", + "eastasia": "southeastasia", + "eastus": "centralus", + "eastus2": "centralus", + "japaneast": "australiaeast", + "northeurope": "westeurope", + "southeastasia": "eastasia", + "uksouth": "westeurope", + "westeurope": "northeurope" + }, + "cosmosDbHaLocation": "[variables('cosmosDbZoneRedundantHaRegionPairs')[resourceGroup().location]]", + "useExistingLogAnalytics": "[not(empty(parameters('existingLogAnalyticsWorkspaceId')))]", + "gptModelDeployment": { + "modelName": "[parameters('gptModelName')]", + "deploymentName": "[parameters('gptModelName')]", + "deploymentVersion": "[parameters('gptModelVersion')]", + "deploymentCapacity": "[parameters('gptModelCapacity')]" + }, + "embeddingModelDeployment": { + "modelName": "[parameters('embeddingModelName')]", + "deploymentName": "[parameters('embeddingModelName')]", + "deploymentVersion": "[parameters('embeddingModelVersion')]", + "deploymentCapacity": "[parameters('embeddingModelCapacity')]" }, "openAiDeployments": [ { - "name": "[variables('chatGpt').deploymentName]", + "name": "[variables('gptModelDeployment').deploymentName]", "model": { "format": "OpenAI", - "name": "[variables('chatGpt').modelName]", - "version": "[variables('chatGpt').deploymentVersion]" + "name": "[variables('gptModelDeployment').modelName]", + "version": "[variables('gptModelDeployment').deploymentVersion]" }, "sku": { - "name": "Standard", - "capacity": "[variables('chatGpt').deploymentCapacity]" + "name": "[parameters('gptModelDeploymentType')]", + "capacity": "[variables('gptModelDeployment').deploymentCapacity]" } }, { - "name": "[variables('embedding').deploymentName]", + "name": "[variables('embeddingModelDeployment').deploymentName]", "model": { "format": "OpenAI", - "name": "[variables('embedding').modelName]", - "version": "[variables('embedding').deploymentVersion]" + "name": "[variables('embeddingModelDeployment').modelName]", + "version": "[variables('embeddingModelDeployment').deploymentVersion]" }, "sku": { - "name": "Standard", - "capacity": "[variables('embedding').deploymentCapacity]" + "name": "[parameters('gptModelDeploymentType')]", + "capacity": "[variables('embeddingModelDeployment').deploymentCapacity]" } } - ] + ], + "privateDnsZones": [ + "privatelink.mongo.cosmos.azure.com", + "privatelink.search.windows.net", + "privatelink.cognitiveservices.azure.com", + "privatelink.openai.azure.com", + "[format('privatelink.blob.{0}', environment().suffixes.storage)]", + "[format('privatelink.queue.{0}', environment().suffixes.storage)]", + "privatelink.api.azureml.ms", + "privatelink.azconfig.io", + "privatelink.azurecr.io" + ], + "dnsZoneIndex": { + "cosmosDB": 0, + "search": 1, + "cognitiveServices": 2, + "openAI": 3, + "storageBlob": 4, + "storageQueue": 5, + "aiFoundry": 6, + "appConfig": 7, + "containerRegistry": 8 + }, + "logAnalyticsWorkspaceResourceName": "[format('log-{0}', variables('solutionSuffix'))]", + "userAssignedIdentityResourceName": "[format('id-{0}', variables('solutionSuffix'))]", + "appConfigName": "[format('appcs-{0}', variables('solutionSuffix'))]", + "storageAccountName": "[format('st{0}', variables('solutionSuffix'))]", + "aiSearchName": "[format('srch-{0}', variables('solutionSuffix'))]", + "openAiAccountName": "[format('oai-{0}', variables('solutionSuffix'))]", + "docIntelAccountName": "[format('di-{0}', variables('solutionSuffix'))]", + "applicationInsightsResourceName": "[format('appi-{0}', variables('solutionSuffix'))]" }, - "resources": [ - { + "resources": { + "avmPrivateDnsZones": { + "copy": { + "name": "avmPrivateDnsZones", + "count": "[length(variables('privateDnsZones'))]", + "mode": "serial", + "batchSize": 5 + }, + "condition": "[parameters('enablePrivateNetworking')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('managedidentity-{0}', parameters('suffix'))]", + "name": "[format('dns-zone-{0}', copyIndex())]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "location": { - "value": "[variables('location')]" - }, - "suffix": { - "value": "[parameters('suffix')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "10309523882387137109" - } - }, - "parameters": { - "suffix": { - "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id)]" - }, - "name": { - "type": "string", - "defaultValue": "[format('km-UAidentity-{0}', parameters('suffix'))]", - "minLength": 2, - "maxLength": 60, - "metadata": { - "description": "Managed Identity name." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "Location for all resources." - } - } + "name": { + "value": "[variables('privateDnsZones')[copyIndex()]]" }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[parameters('name')]", - "location": "[parameters('location')]" - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[guid(format('{0}contributor', resourceGroup().id))]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').principalId]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" - ] - } - ], - "outputs": { - "managedIdentityId": { - "type": "string", - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" - }, - "managedIdentityPrincipalId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').principalId]" - }, - "managedIdentityClientId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2023-01-31').clientId]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('storage-{0}', parameters('suffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[variables('location')]" + "tags": { + "value": "[parameters('tags')]" }, - "suffix": { - "value": "[parameters('suffix')]" + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" }, - "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[reference('network').outputs.vnetResourceId.value]" + } + ] } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "6577135357063255350" - } - }, - "parameters": { - "suffix": { - "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id)]" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" + "version": "0.34.44.8038", + "templateHash": "4533956061065498344" }, - "tags": { + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone." + }, + "definitions": { + "aType": { "type": "object", - "defaultValue": {}, + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + }, "metadata": { - "description": "Optional. The tags to be assigned to the created resources." + "__bicep_export!": true, + "description": "The type for the A record." } }, - "storageAccountName": { - "type": "string", - "defaultValue": "[format('kmstorage{0}', parameters('suffix'))]", + "aaaaType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + }, "metadata": { - "description": "The name of the Azure Storage Account." + "__bicep_export!": true, + "description": "The type for the AAAA record." } }, - "storageBlobContainerName": { - "type": "string", - "defaultValue": "smemory", + "cnameType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + }, "metadata": { - "description": "The name of the Container in Azure Storage." + "__bicep_export!": true, + "description": "The type for the CNAME record." } }, - "externalTasksQueueName": { - "type": "string", - "defaultValue": "[format('km-queue-{0}', parameters('suffix'))]", + "mxType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + }, "metadata": { - "description": "The name of the Queue in Azure Storage." + "__bicep_export!": true, + "description": "The type for the MX record." } }, - "managedIdentityPrincipalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2021-09-01", - "name": "[parameters('storageAccountName')]", - "tags": "[parameters('tags')]", - "location": "[parameters('location')]", - "sku": { - "name": "Standard_LRS" - }, - "kind": "StorageV2", + "ptrType": { + "type": "object", "properties": { - "allowBlobPublicAccess": false, - "allowSharedKeyAccess": false + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the PTR record." } }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(format('Storage Queue Data Contributor-{0}', parameters('suffix')))]", + "soaType": { + "type": "object", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type for the SOA record." + } }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(format('Storage Blob Data Contributor-{0}', parameters('suffix')))]", + "srvType": { + "type": "object", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(format('Storage Blob Data Owner-{0}', parameters('suffix')))]", + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the SRV record." + } + }, + "txtType": { + "type": "object", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type for the TXT record." + } }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(format('Storage Queue Data Message Sender-{0}', parameters('suffix')))]", + "virtualNetworkLinkType": { + "type": "object", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "resolutionPolicy": { + "type": "string", + "allowedValues": [ + "Default", + "NxDomainRedirect" + ], + "nullable": true, + "metadata": { + "description": "Optional. The resolution type of the private-dns-zone fallback machanism." + } + } }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] + "metadata": { + "__bicep_export!": true, + "description": "The type for the virtual network link." + } }, - { - "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')))]", + "lockType": { + "type": "object", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, - { - "type": "Microsoft.Storage/storageAccounts/blobServices", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } }, - { - "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')]" - ] + "a": { + "type": "array", + "items": { + "$ref": "#/definitions/aType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of A records." + } }, - { - "type": "Microsoft.Storage/storageAccounts/queueServices", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]" - ] + "aaaa": { + "type": "array", + "items": { + "$ref": "#/definitions/aaaaType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of AAAA records." + } }, - { - "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": { - "storageAccountName": { - "type": "string", + "cname": { + "type": "array", + "items": { + "$ref": "#/definitions/cnameType" + }, + "nullable": true, "metadata": { - "description": "The storage account name." + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "type": "array", + "items": { + "$ref": "#/definitions/mxType" }, - "value": "[parameters('storageAccountName')]" + "nullable": true, + "metadata": { + "description": "Optional. Array of MX records." + } }, - "blobContainerName": { - "type": "string", + "ptr": { + "type": "array", + "items": { + "$ref": "#/definitions/ptrType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "type": "array", + "items": { + "$ref": "#/definitions/soaType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "type": "array", + "items": { + "$ref": "#/definitions/srvType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "type": "array", + "items": { + "$ref": "#/definitions/txtType" + }, + "nullable": true, "metadata": { - "description": "The storage account name." + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "type": "array", + "items": { + "$ref": "#/definitions/virtualNetworkLinkType" }, - "value": "[parameters('storageBlobContainerName')]" + "nullable": true, + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' and 'registrationEnabled'. The 'vnetResourceId' is a resource ID of a vNet to link, 'registrationEnabled' (bool) enables automatic DNS registration in the zone for the linked vNet." + } }, - "queueName": { + "location": { "type": "string", + "defaultValue": "global", "metadata": { - "description": "The storage account name." + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" }, - "value": "[parameters('externalTasksQueueName')]" + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('search-{0}', parameters('suffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[variables('location')]" - }, - "name": { - "value": "[format('km-search-{0}', parameters('suffix'))]" - }, - "suffix": { - "value": "[parameters('suffix')]" }, - "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.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.26.170.59819", - "templateHash": "16481486745226370443" + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, - "parameters": { - "managedIdentityPrincipalId": { - "type": "string" + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + }, + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "18243374258187942664" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "A" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed A record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed A record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed A record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + }, + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "7322684246075092047" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "AAAA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed AAAA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed AAAA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed AAAA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "5264706240021075859" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + } + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "CNAME" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "13758189936483275969" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "MX" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "11955164584650609753" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "14626715835033259725" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" + } + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "SOA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "6510442308165042737" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + } + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "170623042781622569" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" + } + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VNetLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "resolutionPolicy": { + "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "725891200086243555" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link." + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + }, + "resolutionPolicy": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resolution policy on the virtual network link. Only applicable for virtual network links to privatelink zones, and for A,AAAA,CNAME queries. When set to `NxDomainRedirect`, Azure DNS resolver falls back to public resolution if private dns query resolution results in non-existent domain response. `Default` is configured as the default option." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2024-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + }, + "resolutionPolicy": "[parameters('resolutionPolicy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2024-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "network" + ] + }, + "logAnalyticsWorkspace": { + "condition": "[and(parameters('enableMonitoring'), not(variables('useExistingLogAnalytics')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.operational-insights.workspace.{0}', variables('logAnalyticsWorkspaceResourceName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('logAnalyticsWorkspaceResourceName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "skuName": { + "value": "PerGB2018" + }, + "dataRetention": { + "value": 365 + }, + "features": { + "value": { + "enableLogAccessUsingOnlyResourcePermissions": true + } + }, + "diagnosticSettings": { + "value": [ + { + "useThisWorkspace": true + } + ] + }, + "dailyQuotaGb": "[if(parameters('enableRedundancy'), createObject('value', 10), createObject('value', null()))]", + "replication": "[if(parameters('enableRedundancy'), createObject('value', createObject('enabled', true(), 'location', variables('replicaLocation'))), createObject('value', null()))]", + "publicNetworkAccessForIngestion": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "publicNetworkAccessForQuery": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "dataSources": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('tags', parameters('tags'), 'eventLogName', 'Application', 'eventTypes', createArray(createObject('eventType', 'Error'), createObject('eventType', 'Warning'), createObject('eventType', 'Information')), 'kind', 'WindowsEvent', 'name', 'applicationEvent'), createObject('counterName', '% Processor Time', 'instanceName', '*', 'intervalSeconds', 60, 'kind', 'WindowsPerformanceCounter', 'name', 'windowsPerfCounter1', 'objectName', 'Processor'), createObject('kind', 'IISLogs', 'name', 'sampleIISLog1', 'state', 'OnPremiseEnabled'))), createObject('value', null()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "1749032521457140145" + }, + "name": "Log Analytics Workspaces", + "description": "This module deploys a Log Analytics Workspace." + }, + "definitions": { + "diagnosticSettingType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "useThisWorkspace": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings. If set to `true`, the `workspaceResourceId` property is ignored." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "gallerySolutionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the solution.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, the name should be in the pattern: `SolutionType[WorkspaceName]`, for example `MySolution[contoso-Logs]`.\nThe solution type is case-sensitive." + } + }, + "plan": { + "$ref": "#/definitions/solutionPlanType", + "metadata": { + "description": "Required. Plan for solution object supported by the OperationsManagement resource provider." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the gallery solutions to be created in the log analytics workspace." + } + }, + "storageInsightsConfigType": { + "type": "object", + "properties": { + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the storage account to be linked." + } + }, + "containers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The names of the blob containers that the workspace should read." + } + }, + "tables": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of tables to be read by the workspace." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the storage insights configuration." + } + }, + "linkedServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the linked service." + } + }, + "resourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource id of the resource that will be linked to the workspace. This should be used for linking resources which require read access." + } + }, + "writeAccessResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource id of the resource that will be linked to the workspace. This should be used for linking resources which require write access." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the linked service." + } + }, + "linkedStorageAccountType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the link." + } + }, + "storageAccountIds": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "metadata": { + "description": "Required. Linked storage accounts resources Ids." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the linked storage account." + } + }, + "savedSearchType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the saved search." + } + }, + "etag": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The ETag of the saved search. To override an existing saved search, use \"*\" or specify the current Etag." + } + }, + "category": { + "type": "string", + "metadata": { + "description": "Required. The category of the saved search. This helps the user to find a saved search faster." + } + }, + "displayName": { + "type": "string", + "metadata": { + "description": "Required. Display name for the search." + } + }, + "functionAlias": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The function alias if query serves as a function." + } + }, + "functionParameters": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The optional function parameters if query serves as a function. Value should be in the following format: 'param-name1:type1 = default_value1, param-name2:type2 = default_value2'. For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions." + } + }, + "query": { + "type": "string", + "metadata": { + "description": "Required. The query expression for the saved search." + } + }, + "tags": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The tags attached to the saved search." + } + }, + "version": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The version number of the query language. The current version is 2 and is the default." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the saved search." + } + }, + "dataExportType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the data export." + } + }, + "destination": { + "$ref": "#/definitions/destinationType", + "nullable": true, + "metadata": { + "description": "Optional. The destination of the data export." + } + }, + "enable": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the data export." + } + }, + "tableNames": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of table names to export." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the data export." + } + }, + "dataSourceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the data source." + } + }, + "kind": { + "type": "string", + "metadata": { + "description": "Required. The kind of data source." + } + }, + "linkedResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource id of the resource that will be linked to the workspace." + } + }, + "eventLogName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the event log to configure when kind is WindowsEvent." + } + }, + "eventTypes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The event types to configure when kind is WindowsEvent." + } + }, + "objectName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "instanceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "intervalSeconds": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "performanceCounters": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of counters to configure when the kind is LinuxPerformanceObject." + } + }, + "counterName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Counter name to configure when kind is WindowsPerformanceCounter." + } + }, + "state": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection." + } + }, + "syslogName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. System log to configure when kind is LinuxSyslog." + } + }, + "syslogSeverities": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Severities to configure when kind is LinuxSyslog." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.OperationalInsights/workspaces/dataSources@2025-02-01#properties/tags" + }, + "description": "Optional. Tags to configure in the resource." + }, + "nullable": true + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the data source." + } + }, + "tableType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the table." + } + }, + "plan": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The plan for the table." + } + }, + "restoredLogs": { + "$ref": "#/definitions/restoredLogsType", + "nullable": true, + "metadata": { + "description": "Optional. The restored logs for the table." + } + }, + "schema": { + "$ref": "#/definitions/schemaType", + "nullable": true, + "metadata": { + "description": "Optional. The schema for the table." + } + }, + "searchResults": { + "$ref": "#/definitions/searchResultsType", + "nullable": true, + "metadata": { + "description": "Optional. The search results for the table." + } + }, + "retentionInDays": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The retention in days for the table." + } + }, + "totalRetentionInDays": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The total retention in days for the table." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The role assignments for the table." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Properties of the custom table." + } + }, + "workspaceFeaturesType": { + "type": "object", + "properties": { + "disableLocalAuth": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Disable Non-EntraID based Auth. Default is true." + } + }, + "enableDataExport": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Flag that indicate if data should be exported." + } + }, + "enableLogAccessUsingOnlyResourcePermissions": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable log access using only resource permissions. Default is false." + } + }, + "immediatePurgeDataOn30Days": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Flag that describes if we want to remove the data after 30 days." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Features of the workspace." + } + }, + "workspaceReplicationType": { + "type": "object", + "properties": { + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether the replication is enabled or not. When true, workspace configuration and data is replicated to the specified location." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The location to which the workspace is replicated. Required if replication is enabled." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Replication properties of the workspace." + } + }, + "_1.columnType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The column name." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "boolean", + "dateTime", + "dynamic", + "guid", + "int", + "long", + "real", + "string" + ], + "metadata": { + "description": "Required. The column type." + } + }, + "dataTypeHint": { + "type": "string", + "allowedValues": [ + "armPath", + "guid", + "ip", + "uri" + ], + "nullable": true, + "metadata": { + "description": "Optional. The column data type logical hint." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The column description." + } + }, + "displayName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Column display name." + } + } + }, + "metadata": { + "description": "The parameters of the table column.", + "__bicep_imported_from!": { + "sourceTemplate": "table/main.bicep" + } + } + }, + "destinationType": { + "type": "object", + "properties": { + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The destination resource ID." + } + }, + "metaData": { + "type": "object", + "properties": { + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Allows to define an Event Hub name. Not applicable when destination is Storage Account." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination metadata." + } + } + }, + "metadata": { + "description": "The data export destination properties.", + "__bicep_imported_from!": { + "sourceTemplate": "data-export/main.bicep" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "restoredLogsType": { + "type": "object", + "properties": { + "sourceTable": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The table to restore data from." + } + }, + "startRestoreTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to start the restore from (UTC)." + } + }, + "endRestoreTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to end the restore by (UTC)." + } + } + }, + "metadata": { + "description": "The parameters of the restore operation that initiated the table.", + "__bicep_imported_from!": { + "sourceTemplate": "table/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "schemaType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The table name." + } + }, + "columns": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.columnType" + }, + "metadata": { + "description": "Required. A list of table custom columns." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The table description." + } + }, + "displayName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The table display name." + } + } + }, + "metadata": { + "description": "The table schema.", + "__bicep_imported_from!": { + "sourceTemplate": "table/main.bicep" + } + } + }, + "searchResultsType": { + "type": "object", + "properties": { + "query": { + "type": "string", + "metadata": { + "description": "Required. The search job query." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The search description." + } + }, + "limit": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Limit the search job to return up to specified number of rows." + } + }, + "startSearchTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to start the search from (UTC)." + } + }, + "endSearchTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to end the search by (UTC)." + } + } + }, + "metadata": { + "description": "The parameters of the search job that initiated the table.", + "__bicep_imported_from!": { + "sourceTemplate": "table/main.bicep" + } + } + }, + "solutionPlanType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the solution to be created.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, it can be anything.\nThe solution type is case-sensitive.\nIf not provided, the value of the `name` parameter will be used." + } + }, + "product": { + "type": "string", + "metadata": { + "description": "Required. The product name of the deployed solution.\nFor Microsoft published gallery solution it should be `OMSGallery/{solutionType}`, for example `OMSGallery/AntiMalware`.\nFor a third party solution, it can be anything.\nThis is case sensitive." + } + }, + "publisher": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`, which is the default value." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/operations-management/solution:0.3.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Log Analytics workspace." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "skuName": { + "type": "string", + "defaultValue": "PerGB2018", + "allowedValues": [ + "CapacityReservation", + "Free", + "LACluster", + "PerGB2018", + "PerNode", + "Premium", + "Standalone", + "Standard" + ], + "metadata": { + "description": "Optional. The name of the SKU." + } + }, + "skuCapacityReservationLevel": { + "type": "int", + "defaultValue": 100, + "minValue": 100, + "maxValue": 5000, + "metadata": { + "description": "Optional. The capacity reservation level in GB for this workspace, when CapacityReservation sku is selected. Must be in increments of 100 between 100 and 5000." + } + }, + "storageInsightsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/storageInsightsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of storage accounts to be read by the workspace." + } + }, + "linkedServices": { + "type": "array", + "items": { + "$ref": "#/definitions/linkedServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of services to be linked." + } + }, + "linkedStorageAccounts": { + "type": "array", + "items": { + "$ref": "#/definitions/linkedStorageAccountType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of Storage Accounts to be linked. Required if 'forceCmkForQuery' is set to 'true' and 'savedSearches' is not empty." + } + }, + "savedSearches": { + "type": "array", + "items": { + "$ref": "#/definitions/savedSearchType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Kusto Query Language searches to save." + } + }, + "dataExports": { + "type": "array", + "items": { + "$ref": "#/definitions/dataExportType" + }, + "nullable": true, + "metadata": { + "description": "Optional. LAW data export instances to be deployed." + } + }, + "dataSources": { + "type": "array", + "items": { + "$ref": "#/definitions/dataSourceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. LAW data sources to configure." + } + }, + "tables": { + "type": "array", + "items": { + "$ref": "#/definitions/tableType" + }, + "nullable": true, + "metadata": { + "description": "Optional. LAW custom tables to be deployed." + } + }, + "gallerySolutions": { + "type": "array", + "items": { + "$ref": "#/definitions/gallerySolutionType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of gallerySolutions to be created in the log analytics workspace." + } + }, + "onboardWorkspaceToSentinel": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Onboard the Log Analytics Workspace to Sentinel. Requires 'SecurityInsights' solution to be in gallerySolutions." + } + }, + "dataRetention": { + "type": "int", + "defaultValue": 365, + "minValue": 0, + "maxValue": 730, + "metadata": { + "description": "Optional. Number of days data will be retained for." + } + }, + "dailyQuotaGb": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "metadata": { + "description": "Optional. The workspace daily quota for ingestion." + } + }, + "publicNetworkAccessForIngestion": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The network access type for accessing Log Analytics ingestion." + } + }, + "publicNetworkAccessForQuery": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The network access type for accessing Log Analytics query." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource. Only one type of identity is supported: system-assigned or user-assigned, but not both." + } + }, + "features": { + "$ref": "#/definitions/workspaceFeaturesType", + "nullable": true, + "metadata": { + "description": "Optional. The workspace features." + } + }, + "replication": { + "$ref": "#/definitions/workspaceReplicationType", + "nullable": true, + "metadata": { + "description": "Optional. The workspace replication properties." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "forceCmkForQuery": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether customer managed storage is mandatory for query management." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.OperationalInsights/workspaces@2025-02-01#properties/tags" + }, + "description": "Optional. Tags of the resource." + }, + "nullable": true + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Security Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb1c8493-542b-48eb-b624-b4c8fea62acd')]", + "Security Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '39bc4728-0917-49c7-9d2c-d95423bc2eb4')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.operationalinsights-workspace.{0}.{1}', replace('0.12.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "logAnalyticsWorkspace": { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "features": { + "searchVersion": 1, + "enableLogAccessUsingOnlyResourcePermissions": "[coalesce(tryGet(parameters('features'), 'enableLogAccessUsingOnlyResourcePermissions'), false())]", + "disableLocalAuth": "[coalesce(tryGet(parameters('features'), 'disableLocalAuth'), true())]", + "enableDataExport": "[tryGet(parameters('features'), 'enableDataExport')]", + "immediatePurgeDataOn30Days": "[tryGet(parameters('features'), 'immediatePurgeDataOn30Days')]" + }, + "sku": { + "name": "[parameters('skuName')]", + "capacityReservationLevel": "[if(equals(parameters('skuName'), 'CapacityReservation'), parameters('skuCapacityReservationLevel'), null())]" + }, + "retentionInDays": "[parameters('dataRetention')]", + "workspaceCapping": { + "dailyQuotaGb": "[parameters('dailyQuotaGb')]" + }, + "publicNetworkAccessForIngestion": "[parameters('publicNetworkAccessForIngestion')]", + "publicNetworkAccessForQuery": "[parameters('publicNetworkAccessForQuery')]", + "forceCmkForQuery": "[parameters('forceCmkForQuery')]", + "replication": "[parameters('replication')]" + }, + "identity": "[variables('identity')]" + }, + "logAnalyticsWorkspace_diagnosticSettings": { + "copy": { + "name": "logAnalyticsWorkspace_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[if(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'useThisWorkspace'), false()), resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId'))]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_sentinelOnboarding": { + "condition": "[and(not(empty(filter(coalesce(parameters('gallerySolutions'), createArray()), lambda('item', startsWith(lambdaVariables('item').name, 'SecurityInsights'))))), parameters('onboardWorkspaceToSentinel'))]", + "type": "Microsoft.SecurityInsights/onboardingStates", + "apiVersion": "2024-03-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "default", + "properties": {}, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_roleAssignments": { + "copy": { + "name": "logAnalyticsWorkspace_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_storageInsightConfigs": { + "copy": { + "name": "logAnalyticsWorkspace_storageInsightConfigs", + "count": "[length(coalesce(parameters('storageInsightsConfigs'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-StorageInsightsConfig-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "containers": { + "value": "[tryGet(coalesce(parameters('storageInsightsConfigs'), createArray())[copyIndex()], 'containers')]" + }, + "tables": { + "value": "[tryGet(coalesce(parameters('storageInsightsConfigs'), createArray())[copyIndex()], 'tables')]" + }, + "storageAccountResourceId": { + "value": "[coalesce(parameters('storageInsightsConfigs'), createArray())[copyIndex()].storageAccountResourceId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "1306323182548882150" + }, + "name": "Log Analytics Workspace Storage Insight Configs", + "description": "This module deploys a Log Analytics Workspace Storage Insight Config." + }, + "parameters": { + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-stinsconfig', last(split(parameters('storageAccountResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the storage insights config." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Azure Resource Manager ID of the storage account resource." + } + }, + "containers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The names of the blob containers that the workspace should read." + } + }, + "tables": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The names of the Azure tables that the workspace should read." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs@2025-02-01#properties/tags" + }, + "description": "Optional. Tags to configure in the resource." + }, + "nullable": true + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "storageinsightconfig": { + "type": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs", + "apiVersion": "2025-02-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "containers": "[parameters('containers')]", + "tables": "[parameters('tables')]", + "storageAccount": { + "id": "[parameters('storageAccountResourceId')]", + "key": "[listKeys('storageAccount', '2024-01-01').keys[0].value]" + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage insights configuration." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/storageInsightConfigs', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the storage insight configuration is deployed." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the storage insights configuration." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_linkedServices": { + "copy": { + "name": "logAnalyticsWorkspace_linkedServices", + "count": "[length(coalesce(parameters('linkedServices'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-LinkedService-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('linkedServices'), createArray())[copyIndex()].name]" + }, + "resourceId": { + "value": "[tryGet(coalesce(parameters('linkedServices'), createArray())[copyIndex()], 'resourceId')]" + }, + "writeAccessResourceId": { + "value": "[tryGet(coalesce(parameters('linkedServices'), createArray())[copyIndex()], 'writeAccessResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "5230241501765697269" + }, + "name": "Log Analytics Workspace Linked Services", + "description": "This module deploys a Log Analytics Workspace Linked Service." + }, + "parameters": { + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the link." + } + }, + "resourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require read access." + } + }, + "writeAccessResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the resource that will be linked to the workspace. This should be used for linking resources which require write access." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.OperationalInsights/workspaces/linkedServices@2025-02-01#properties/tags" + }, + "description": "Optional. Tags to configure in the resource." + }, + "nullable": true + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "linkedService": { + "type": "Microsoft.OperationalInsights/workspaces/linkedServices", + "apiVersion": "2025-02-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resourceId": "[parameters('resourceId')]", + "writeAccessResourceId": "[parameters('writeAccessResourceId')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed linked service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed linked service." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/linkedServices', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the linked service is deployed." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_linkedStorageAccounts": { + "copy": { + "name": "logAnalyticsWorkspace_linkedStorageAccounts", + "count": "[length(coalesce(parameters('linkedStorageAccounts'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-LinkedStorageAccount-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('linkedStorageAccounts'), createArray())[copyIndex()].name]" + }, + "storageAccountIds": { + "value": "[coalesce(parameters('linkedStorageAccounts'), createArray())[copyIndex()].storageAccountIds]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "10372135754202496594" + }, + "name": "Log Analytics Workspace Linked Storage Accounts", + "description": "This module deploys a Log Analytics Workspace Linked Storage Account." + }, + "parameters": { + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "allowedValues": [ + "Query", + "Alerts", + "CustomLogs", + "AzureWatson" + ], + "metadata": { + "description": "Required. Name of the link." + } + }, + "storageAccountIds": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "metadata": { + "description": "Required. Linked storage accounts resources Ids." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "linkedStorageAccount": { + "type": "Microsoft.OperationalInsights/workspaces/linkedStorageAccounts", + "apiVersion": "2025-02-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "properties": { + "storageAccountIds": "[parameters('storageAccountIds')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed linked storage account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed linked storage account." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/linkedStorageAccounts', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the linked storage account is deployed." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_savedSearches": { + "copy": { + "name": "logAnalyticsWorkspace_savedSearches", + "count": "[length(coalesce(parameters('savedSearches'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-SavedSearch-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[format('{0}{1}', coalesce(parameters('savedSearches'), createArray())[copyIndex()].name, uniqueString(deployment().name))]" + }, + "etag": { + "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'etag')]" + }, + "displayName": { + "value": "[coalesce(parameters('savedSearches'), createArray())[copyIndex()].displayName]" + }, + "category": { + "value": "[coalesce(parameters('savedSearches'), createArray())[copyIndex()].category]" + }, + "query": { + "value": "[coalesce(parameters('savedSearches'), createArray())[copyIndex()].query]" + }, + "functionAlias": { + "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'functionAlias')]" + }, + "functionParameters": { + "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'functionParameters')]" + }, + "tags": { + "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'tags')]" + }, + "version": { + "value": "[tryGet(coalesce(parameters('savedSearches'), createArray())[copyIndex()], 'version')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "9015459905306126128" + }, + "name": "Log Analytics Workspace Saved Searches", + "description": "This module deploys a Log Analytics Workspace Saved Search." + }, + "parameters": { + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the saved search." + } + }, + "displayName": { + "type": "string", + "metadata": { + "description": "Required. Display name for the search." + } + }, + "category": { + "type": "string", + "metadata": { + "description": "Required. Query category." + } + }, + "query": { + "type": "string", + "metadata": { + "description": "Required. Kusto Query to be stored." + } + }, + "tags": { + "type": "array", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.OperationalInsights/workspaces/savedSearches@2025-02-01#properties/properties/properties/tags" + }, + "description": "Optional. Tags to configure in the resource." + }, + "nullable": true + }, + "functionAlias": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The function alias if query serves as a function." + } + }, + "functionParameters": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The optional function parameters if query serves as a function. Value should be in the following format: \"param-name1:type1 = default_value1, param-name2:type2 = default_value2\". For more examples and proper syntax please refer to /azure/kusto/query/functions/user-defined-functions." + } + }, + "version": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The version number of the query language." + } + }, + "etag": { + "type": "string", + "defaultValue": "*", + "metadata": { + "description": "Optional. The ETag of the saved search. To override an existing saved search, use \"*\" or specify the current Etag." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "savedSearch": { + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2025-02-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "properties": { + "etag": "[parameters('etag')]", + "tags": "[coalesce(parameters('tags'), createArray())]", + "displayName": "[parameters('displayName')]", + "category": "[parameters('category')]", + "query": "[parameters('query')]", + "functionAlias": "[parameters('functionAlias')]", + "functionParameters": "[parameters('functionParameters')]", + "version": "[parameters('version')]" + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed saved search." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the saved search is deployed." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed saved search." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace", + "logAnalyticsWorkspace_linkedStorageAccounts" + ] + }, + "logAnalyticsWorkspace_dataExports": { + "copy": { + "name": "logAnalyticsWorkspace_dataExports", + "count": "[length(coalesce(parameters('dataExports'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-DataExport-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "workspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('dataExports'), createArray())[copyIndex()].name]" + }, + "destination": { + "value": "[tryGet(coalesce(parameters('dataExports'), createArray())[copyIndex()], 'destination')]" + }, + "enable": { + "value": "[tryGet(coalesce(parameters('dataExports'), createArray())[copyIndex()], 'enable')]" + }, + "tableNames": { + "value": "[tryGet(coalesce(parameters('dataExports'), createArray())[copyIndex()], 'tableNames')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "8586520532175356447" + }, + "name": "Log Analytics Workspace Data Exports", + "description": "This module deploys a Log Analytics Workspace Data Export." + }, + "definitions": { + "destinationType": { + "type": "object", + "properties": { + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The destination resource ID." + } + }, + "metaData": { + "type": "object", + "properties": { + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Allows to define an Event Hub name. Not applicable when destination is Storage Account." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination metadata." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The data export destination properties." + } + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 4, + "maxLength": 63, + "metadata": { + "description": "Required. The data export rule name." + } + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment." + } + }, + "destination": { + "$ref": "#/definitions/destinationType", + "nullable": true, + "metadata": { + "description": "Optional. Destination properties." + } + }, + "enable": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Active when enabled." + } + }, + "tableNames": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "metadata": { + "description": "Required. An array of tables to export, for example: ['Heartbeat', 'SecurityEvent']." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('workspaceName')]" + }, + "dataExport": { + "type": "Microsoft.OperationalInsights/workspaces/dataExports", + "apiVersion": "2025-02-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "destination": "[parameters('destination')]", + "enable": "[parameters('enable')]", + "tableNames": "[parameters('tableNames')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the data export." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the data export." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/dataExports', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the data export was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_dataSources": { + "copy": { + "name": "logAnalyticsWorkspace_dataSources", + "count": "[length(coalesce(parameters('dataSources'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-DataSource-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('dataSources'), createArray())[copyIndex()].name]" + }, + "kind": { + "value": "[coalesce(parameters('dataSources'), createArray())[copyIndex()].kind]" + }, + "linkedResourceId": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'linkedResourceId')]" + }, + "eventLogName": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'eventLogName')]" + }, + "eventTypes": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'eventTypes')]" + }, + "objectName": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'objectName')]" + }, + "instanceName": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'instanceName')]" + }, + "intervalSeconds": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'intervalSeconds')]" + }, + "counterName": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'counterName')]" + }, + "state": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'state')]" + }, + "syslogName": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'syslogName')]" + }, + "syslogSeverities": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'syslogSeverities')]" + }, + "performanceCounters": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'performanceCounters')]" + }, + "tags": { + "value": "[tryGet(coalesce(parameters('dataSources'), createArray())[copyIndex()], 'tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "8336916453932906250" + }, + "name": "Log Analytics Workspace Datasources", + "description": "This module deploys a Log Analytics Workspace Data Source." + }, + "parameters": { + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Log Analytics workspace. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the data source." + } + }, + "kind": { + "type": "string", + "defaultValue": "AzureActivityLog", + "allowedValues": [ + "AzureActivityLog", + "WindowsEvent", + "WindowsPerformanceCounter", + "IISLogs", + "LinuxSyslog", + "LinuxSyslogCollection", + "LinuxPerformanceObject", + "LinuxPerformanceCollection" + ], + "metadata": { + "description": "Optional. The kind of the data source." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.OperationalInsights/workspaces/dataSources@2025-02-01#properties/tags" + }, + "description": "Optional. Tags to configure in the resource." + }, + "nullable": true + }, + "linkedResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the resource to be linked." + } + }, + "eventLogName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Windows event log name to configure when kind is WindowsEvent." + } + }, + "eventTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Windows event types to configure when kind is WindowsEvent." + } + }, + "objectName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "instanceName": { + "type": "string", + "defaultValue": "*", + "metadata": { + "description": "Optional. Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "intervalSeconds": { + "type": "int", + "defaultValue": 60, + "metadata": { + "description": "Optional. Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "performanceCounters": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of counters to configure when the kind is LinuxPerformanceObject." + } + }, + "counterName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Counter name to configure when kind is WindowsPerformanceCounter." + } + }, + "state": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection." + } + }, + "syslogName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. System log to configure when kind is LinuxSyslog." + } + }, + "syslogSeverities": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Severities to configure when kind is LinuxSyslog." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "dataSource": { + "type": "Microsoft.OperationalInsights/workspaces/dataSources", + "apiVersion": "2025-02-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "kind": "[parameters('kind')]", + "tags": "[parameters('tags')]", + "properties": { + "linkedResourceId": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'AzureActivityLog')), parameters('linkedResourceId'), null())]", + "eventLogName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsEvent')), parameters('eventLogName'), null())]", + "eventTypes": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsEvent')), parameters('eventTypes'), null())]", + "objectName": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('objectName'), null())]", + "instanceName": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('instanceName'), null())]", + "intervalSeconds": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('intervalSeconds'), null())]", + "counterName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsPerformanceCounter')), parameters('counterName'), null())]", + "state": "[if(and(not(empty(parameters('kind'))), or(or(equals(parameters('kind'), 'IISLogs'), equals(parameters('kind'), 'LinuxSyslogCollection')), equals(parameters('kind'), 'LinuxPerformanceCollection'))), parameters('state'), null())]", + "syslogName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'LinuxSyslog')), parameters('syslogName'), null())]", + "syslogSeverities": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'LinuxSyslog'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('syslogSeverities'), null())]", + "performanceCounters": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'LinuxPerformanceObject')), parameters('performanceCounters'), null())]" + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed data source." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/dataSources', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the data source is deployed." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed data source." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_tables": { + "copy": { + "name": "logAnalyticsWorkspace_tables", + "count": "[length(coalesce(parameters('tables'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-Table-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "workspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('tables'), createArray())[copyIndex()].name]" + }, + "plan": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'plan')]" + }, + "schema": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'schema')]" + }, + "retentionInDays": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'retentionInDays')]" + }, + "totalRetentionInDays": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'totalRetentionInDays')]" + }, + "restoredLogs": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'restoredLogs')]" + }, + "searchResults": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'searchResults')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.36.1.42791", + "templateHash": "315390662258960765" + }, + "name": "Log Analytics Workspace Tables", + "description": "This module deploys a Log Analytics Workspace Table." + }, + "definitions": { + "restoredLogsType": { + "type": "object", + "properties": { + "sourceTable": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The table to restore data from." + } + }, + "startRestoreTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to start the restore from (UTC)." + } + }, + "endRestoreTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to end the restore by (UTC)." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The parameters of the restore operation that initiated the table." + } + }, + "schemaType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The table name." + } + }, + "columns": { + "type": "array", + "items": { + "$ref": "#/definitions/columnType" + }, + "metadata": { + "description": "Required. A list of table custom columns." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The table description." + } + }, + "displayName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The table display name." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The table schema." + } + }, + "columnType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The column name." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "boolean", + "dateTime", + "dynamic", + "guid", + "int", + "long", + "real", + "string" + ], + "metadata": { + "description": "Required. The column type." + } + }, + "dataTypeHint": { + "type": "string", + "allowedValues": [ + "armPath", + "guid", + "ip", + "uri" + ], + "nullable": true, + "metadata": { + "description": "Optional. The column data type logical hint." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The column description." + } + }, + "displayName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Column display name." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The parameters of the table column." + } + }, + "searchResultsType": { + "type": "object", + "properties": { + "query": { + "type": "string", + "metadata": { + "description": "Required. The search job query." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The search description." + } + }, + "limit": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Limit the search job to return up to specified number of rows." + } + }, + "startSearchTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to start the search from (UTC)." + } + }, + "endSearchTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The timestamp to end the search by (UTC)." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The parameters of the search job that initiated the table." + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the table." + } + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment." + } + }, + "plan": { + "type": "string", + "defaultValue": "Analytics", + "allowedValues": [ + "Basic", + "Analytics" + ], + "metadata": { + "description": "Optional. Instruct the system how to handle and charge the logs ingested to this table." + } + }, + "restoredLogs": { + "$ref": "#/definitions/restoredLogsType", + "nullable": true, + "metadata": { + "description": "Optional. Restore parameters." + } + }, + "retentionInDays": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "maxValue": 730, + "metadata": { + "description": "Optional. The table retention in days, between 4 and 730. Setting this property to -1 will default to the workspace retention." + } + }, + "schema": { + "$ref": "#/definitions/schemaType", + "nullable": true, + "metadata": { + "description": "Optional. Table's schema." + } + }, + "searchResults": { + "$ref": "#/definitions/searchResultsType", + "nullable": true, + "metadata": { + "description": "Optional. Parameters of the search job that initiated this table." + } + }, + "totalRetentionInDays": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "maxValue": 2555, + "metadata": { + "description": "Optional. The table total retention in days, between 4 and 2555. Setting this property to -1 will default to table retention." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]", + "Monitoring Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2025-02-01", + "name": "[parameters('workspaceName')]" + }, + "table": { + "type": "Microsoft.OperationalInsights/workspaces/tables", + "apiVersion": "2025-02-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "plan": "[parameters('plan')]", + "restoredLogs": "[parameters('restoredLogs')]", + "retentionInDays": "[parameters('retentionInDays')]", + "schema": "[parameters('schema')]", + "searchResults": "[parameters('searchResults')]", + "totalRetentionInDays": "[parameters('totalRetentionInDays')]" + } + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}/tables/{1}', parameters('workspaceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "table" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the table." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the table." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the table was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_solutions": { + "copy": { + "name": "logAnalyticsWorkspace_solutions", + "count": "[length(coalesce(parameters('gallerySolutions'), createArray()))]" + }, + "condition": "[not(empty(parameters('gallerySolutions')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-Solution-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('gallerySolutions'), createArray())[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "plan": { + "value": "[coalesce(parameters('gallerySolutions'), createArray())[copyIndex()].plan]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.32.4.45862", + "templateHash": "10255889523646649592" + }, + "name": "Operations Management Solutions", + "description": "This module deploys an Operations Management Solution.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "solutionPlanType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the solution to be created.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, it can be anything.\nThe solution type is case-sensitive.\nIf not provided, the value of the `name` parameter will be used." + } + }, + "product": { + "type": "string", + "metadata": { + "description": "Required. The product name of the deployed solution.\nFor Microsoft published gallery solution it should be `OMSGallery/{solutionType}`, for example `OMSGallery/AntiMalware`.\nFor a third party solution, it can be anything.\nThis is case sensitive." + } + }, + "publisher": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The publisher name of the deployed solution. For Microsoft published gallery solution, it is `Microsoft`, which is the default value." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the solution.\nFor solutions authored by Microsoft, the name must be in the pattern: `SolutionType(WorkspaceName)`, for example: `AntiMalware(contoso-Logs)`.\nFor solutions authored by third parties, the name should be in the pattern: `SolutionType[WorkspaceName]`, for example `MySolution[contoso-Logs]`.\nThe solution type is case-sensitive." + } + }, + "plan": { + "$ref": "#/definitions/solutionPlanType", + "metadata": { + "description": "Required. Plan for solution object supported by the OperationsManagement resource provider." + } + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Log Analytics workspace where the solution will be deployed/enabled." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.operationsmanagement-solution.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "logAnalyticsWorkspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-06-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "solution": { + "type": "Microsoft.OperationsManagement/solutions", + "apiVersion": "2015-11-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" + }, + "plan": { + "name": "[coalesce(tryGet(parameters('plan'), 'name'), parameters('name'))]", + "promotionCode": "", + "product": "[parameters('plan').product]", + "publisher": "[coalesce(tryGet(parameters('plan'), 'publisher'), 'Microsoft')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed solution." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed solution." + }, + "value": "[resourceId('Microsoft.OperationsManagement/solutions', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the solution is deployed." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('solution', '2015-11-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed log analytics workspace." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed log analytics workspace." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed log analytics workspace." + }, + "value": "[parameters('name')]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "The ID associated with the workspace." + }, + "value": "[reference('logAnalyticsWorkspace').customerId]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('logAnalyticsWorkspace', '2025-02-01', 'full').location]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('logAnalyticsWorkspace', '2025-02-01', 'full'), 'identity'), 'principalId')]" + }, + "primarySharedKey": { + "type": "securestring", + "metadata": { + "description": "The primary shared key of the log analytics workspace." + }, + "value": "[listKeys('logAnalyticsWorkspace', '2025-02-01').primarySharedKey]" + }, + "secondarySharedKey": { + "type": "securestring", + "metadata": { + "description": "The secondary shared key of the log analytics workspace." + }, + "value": "[listKeys('logAnalyticsWorkspace', '2025-02-01').secondarySharedKey]" + } + } + } + } + }, + "network": { + "condition": "[parameters('enablePrivateNetworking')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('network-{0}-deployment', variables('solutionSuffix')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourcesName": { + "value": "[variables('solutionSuffix')]" + }, + "logAnalyticsWorkSpaceResourceId": "[if(variables('useExistingLogAnalytics'), createObject('value', parameters('existingLogAnalyticsWorkspaceId')), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value))]", + "vmAdminUsername": { + "value": "[coalesce(parameters('vmAdminUsername'), 'JumpboxAdminUser')]" + }, + "vmAdminPassword": { + "value": "[coalesce(parameters('vmAdminPassword'), 'JumpboxAdminP@ssw0rd1234!')]" + }, + "vmSize": { + "value": "[coalesce(parameters('vmSize'), 'Standard_DS2_v2')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "2843576779006428867" + } + }, + "parameters": { + "resourcesName": { + "type": "string", + "metadata": { + "description": "Required. Named used for all resource naming." + } + }, + "logAnalyticsWorkSpaceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the Log Analytics Workspace for monitoring and diagnostics." + } + }, + "location": { + "type": "string", + "minLength": 3, + "metadata": { + "description": "Required. Azure region for all services." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "vmAdminUsername": { + "type": "securestring", + "metadata": { + "description": "Required. Admin username for the VM." + } + }, + "vmAdminPassword": { + "type": "securestring", + "metadata": { + "description": "Required. Admin password for the VM." + } + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "Required. VM size for the Jumpbox VM." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('network-{0}-create', parameters('resourcesName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "resourcesName": { + "value": "[parameters('resourcesName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkSpaceResourceId": { + "value": "[parameters('logAnalyticsWorkSpaceResourceId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "addressPrefixes": { + "value": [ + "10.0.0.0/20" + ] + }, + "subnets": { + "value": [ + { + "name": "web", + "addressPrefixes": [ + "10.0.0.0/23" + ], + "networkSecurityGroup": { + "name": "nsg-web", + "securityRules": [ + { + "name": "AllowHttpsInbound", + "properties": { + "access": "Allow", + "direction": "Inbound", + "priority": 100, + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "443", + "sourceAddressPrefixes": [ + "0.0.0.0/0" + ], + "destinationAddressPrefix": "*" + } + }, + { + "name": "AllowHttpInbound", + "properties": { + "access": "Allow", + "direction": "Inbound", + "priority": 110, + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "80", + "sourceAddressPrefixes": [ + "0.0.0.0/0" + ], + "destinationAddressPrefix": "*" + } + }, + { + "name": "AllowIntraSubnetTraffic", + "properties": { + "access": "Allow", + "direction": "Inbound", + "priority": 200, + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "*", + "sourceAddressPrefixes": [ + "10.0.0.0/23" + ], + "destinationAddressPrefixes": [ + "10.0.0.0/23" + ] + } + }, + { + "name": "AllowAzureLoadBalancer", + "properties": { + "access": "Allow", + "direction": "Inbound", + "priority": 300, + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "*", + "sourceAddressPrefix": "AzureLoadBalancer", + "destinationAddressPrefix": "10.0.0.0/23" + } + } + ] + } + }, + { + "name": "peps", + "addressPrefixes": [ + "10.0.2.0/23" + ], + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Disabled", + "networkSecurityGroup": { + "name": "nsg-peps", + "securityRules": [] + } + } + ] + }, + "bastionConfiguration": { + "value": { + "name": "[format('bas-{0}', parameters('resourcesName'))]", + "subnet": { + "name": "AzureBastionSubnet", + "addressPrefixes": [ + "10.0.10.0/26" + ], + "networkSecurityGroup": { + "name": "nsg-AzureBastionSubnet", + "securityRules": [ + { + "name": "AllowGatewayManager", + "properties": { + "access": "Allow", + "direction": "Inbound", + "priority": 2702, + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "443", + "sourceAddressPrefix": "GatewayManager", + "destinationAddressPrefix": "*" + } + }, + { + "name": "AllowHttpsInBound", + "properties": { + "access": "Allow", + "direction": "Inbound", + "priority": 2703, + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRange": "443", + "sourceAddressPrefix": "Internet", + "destinationAddressPrefix": "*" + } + }, + { + "name": "AllowSshRdpOutbound", + "properties": { + "access": "Allow", + "direction": "Outbound", + "priority": 100, + "protocol": "*", + "sourcePortRange": "*", + "destinationPortRanges": [ + "22", + "3389" + ], + "sourceAddressPrefix": "*", + "destinationAddressPrefix": "VirtualNetwork" + } + }, + { + "name": "AllowAzureCloudOutbound", + "properties": { + "access": "Allow", + "direction": "Outbound", + "priority": 110, + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "443", + "sourceAddressPrefix": "*", + "destinationAddressPrefix": "AzureCloud" + } + } + ] + } + } + } + }, + "jumpboxConfiguration": { + "value": { + "name": "[format('vm-jumpbox-{0}', parameters('resourcesName'))]", + "size": "[parameters('vmSize')]", + "username": "[parameters('vmAdminUsername')]", + "password": "[parameters('vmAdminPassword')]", + "subnet": { + "name": "jumpbox", + "addressPrefixes": [ + "10.0.12.0/23" + ], + "networkSecurityGroup": { + "name": "nsg-jumbox", + "securityRules": [ + { + "name": "AllowRdpFromBastion", + "properties": { + "access": "Allow", + "direction": "Inbound", + "priority": 100, + "protocol": "Tcp", + "sourcePortRange": "*", + "destinationPortRange": "3389", + "sourceAddressPrefixes": [ + "10.0.10.0/26" + ], + "destinationAddressPrefixes": [ + "10.0.12.0/23" + ] + } + } + ] + } + } + } + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "10645092392603021381" + } + }, + "definitions": { + "_1.networkSecurityGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the network security group." + } + }, + "securityRules": { + "type": "array", + "items": { + "type": "object" + }, + "metadata": { + "description": "Required. The security rules for the network security group." + } + } + }, + "metadata": { + "description": "Custom type definition for network security group configuration", + "__bicep_imported_from!": { + "sourceTemplate": "virtualNetwork.bicep" + } + } + }, + "bastionHostConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Bastion Host resource." + } + }, + "subnet": { + "$ref": "#/definitions/subnetType", + "nullable": true, + "metadata": { + "description": "Optional. Subnet configuration for the Jumpbox VM." + } + } + }, + "metadata": { + "description": "Custom type definition for establishing Bastion Host for remote connection.", + "__bicep_imported_from!": { + "sourceTemplate": "bastionHost.bicep" + } + } + }, + "jumpBoxConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Virtual Machine." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The size of the VM." + } + }, + "username": { + "type": "string", + "metadata": { + "description": "Username to access VM." + } + }, + "password": { + "type": "securestring", + "metadata": { + "description": "Password to access VM." + } + }, + "subnet": { + "$ref": "#/definitions/subnetType", + "nullable": true, + "metadata": { + "description": "Optional. Subnet configuration for the Jumpbox VM." + } + } + }, + "metadata": { + "description": "Custom type definition for establishing Jumpbox Virtual Machine and its associated resources.", + "__bicep_imported_from!": { + "sourceTemplate": "jumpbox.bicep" + } + } + }, + "subnetOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the subnet." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the subnet." + } + }, + "nsgName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The name of the associated network security group, if any." + } + }, + "nsgResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The resource ID of the associated network security group, if any." + } + } + }, + "metadata": { + "description": "Custom type definition for subnet resource information as output", + "__bicep_imported_from!": { + "sourceTemplate": "virtualNetwork.bicep" + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. Prefixes for the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "networkSecurityGroup": { + "$ref": "#/definitions/_1.networkSecurityGroupType", + "nullable": true, + "metadata": { + "description": "Optional. Network Security Group configuration for the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + } + }, + "metadata": { + "description": "Custom type definition for subnet configuration", + "__bicep_imported_from!": { + "sourceTemplate": "virtualNetwork.bicep" + } + } + } + }, + "parameters": { + "resourcesName": { + "type": "string", + "minLength": 6, + "maxLength": 25, + "metadata": { + "description": "Name used for naming all network resources." + } + }, + "location": { + "type": "string", + "minLength": 3, + "metadata": { + "description": "Azure region for all services." + } + }, + "logAnalyticsWorkSpaceResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics Workspace for monitoring and diagnostics." + } + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Networking address prefix for the VNET." + } + }, + "subnets": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetType" + }, + "metadata": { + "description": "Array of subnets to be created within the VNET." + } + }, + "jumpboxConfiguration": { + "$ref": "#/definitions/jumpBoxConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Configuration for the Jumpbox VM. Leave null to omit Jumpbox creation." + } + }, + "bastionConfiguration": { + "$ref": "#/definitions/bastionHostConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Configuration for the Azure Bastion Host. Leave null to omit Bastion creation." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "virtualNetwork": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetwork', parameters('resourcesName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('vnet-{0}', parameters('resourcesName'))]" + }, + "addressPrefixes": { + "value": "[parameters('addressPrefixes')]" + }, + "subnets": { + "value": "[parameters('subnets')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[parameters('logAnalyticsWorkSpaceResourceId')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "1936468723755149871" + } + }, + "definitions": { + "subnetOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the subnet." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the subnet." + } + }, + "nsgName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The name of the associated network security group, if any." + } + }, + "nsgResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The resource ID of the associated network security group, if any." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Custom type definition for subnet resource information as output" + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. Prefixes for the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "networkSecurityGroup": { + "$ref": "#/definitions/networkSecurityGroupType", + "nullable": true, + "metadata": { + "description": "Optional. Network Security Group configuration for the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Custom type definition for subnet configuration" + } + }, + "networkSecurityGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the network security group." + } + }, + "securityRules": { + "type": "array", + "items": { + "type": "object" + }, + "metadata": { + "description": "Required. The security rules for the network security group." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Custom type definition for network security group configuration" + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the virtual network." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Azure region to deploy resources." + } + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. An Array of 1 or more IP Address Prefixes OR the resource ID of the IPAM pool to be used for the Virtual Network. When specifying an IPAM pool resource ID you must also set a value for the parameter called `ipamPoolNumberOfIpAddresses`." + } + }, + "subnets": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetType" + }, + "metadata": { + "description": "An array of subnets to be created within the virtual network. Each subnet can have its own configuration and associated Network Security Group (NSG)." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to be applied to the resources." + } + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "Optional. The resource ID of the Log Analytics Workspace to send diagnostic logs to." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "resources": { + "nsgs": { + "copy": { + "name": "nsgs", + "count": "[length(parameters('subnets'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[not(empty(tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-{1}-networksecuritygroup', parameters('name'), tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}', tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'name'), parameters('name'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "securityRules": { + "value": "[tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'securityRules')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "2305747478751645177" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG)." + }, + "definitions": { + "securityRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "properties": { + "type": "object", + "properties": { + "access": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the security rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Optional. The destination address prefix. CIDR or destination IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used." + } + }, + "destinationAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "destinationPortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination port ranges." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 4096, + "metadata": { + "description": "Required. Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "*", + "Ah", + "Esp", + "Icmp", + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Network protocol this rule applies to." + } + }, + "sourceAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used. If this is an ingress rule, specifies where network traffic originates from." + } + }, + "sourceAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The source port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "sourcePortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "metadata": { + "description": "Required. The properties of the security rule." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of a security rule." + } + }, + "diagnosticSettingLogsOnlyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Network Security Group." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "securityRules": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed." + } + }, + "flushConnection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(coalesce(parameters('securityRules'), createArray()))]", + "input": { + "name": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].name]", + "properties": { + "access": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.access]", + "description": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'description'), '')]", + "destinationAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), '')]", + "destinationAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), createArray())]", + "destinationApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroupResourceIds'), createArray()), lambda('destinationApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('destinationApplicationSecurityGroupResourceId'))))]", + "destinationPortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRange'), '')]", + "destinationPortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRanges'), createArray())]", + "direction": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.direction]", + "priority": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.priority]", + "protocol": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.protocol]", + "sourceAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), '')]", + "sourceAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), createArray())]", + "sourceApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroupResourceIds'), createArray()), lambda('sourceApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('sourceApplicationSecurityGroupResourceId'))))]", + "sourcePortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRange'), '')]", + "sourcePortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRanges'), createArray())]" + } + } + } + ], + "flushConnection": "[parameters('flushConnection')]" + } + }, + "networkSecurityGroup_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_diagnosticSettings": { + "copy": { + "name": "networkSecurityGroup_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_roleAssignments": { + "copy": { + "name": "networkSecurityGroup_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + "virtualNetwork": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-virtualNetwork', parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "addressPrefixes": { + "value": "[parameters('addressPrefixes')]" + }, + "subnets": { + "copy": [ + { + "name": "value", + "count": "[length(parameters('subnets'))]", + "input": "[createObject('name', parameters('subnets')[copyIndex('value')].name, 'addressPrefixes', tryGet(parameters('subnets')[copyIndex('value')], 'addressPrefixes'), 'networkSecurityGroupResourceId', if(not(empty(tryGet(parameters('subnets')[copyIndex('value')], 'networkSecurityGroup'))), reference(format('nsgs[{0}]', copyIndex('value'))).outputs.resourceId.value, null()), 'privateEndpointNetworkPolicies', tryGet(parameters('subnets')[copyIndex('value')], 'privateEndpointNetworkPolicies'), 'privateLinkServiceNetworkPolicies', tryGet(parameters('subnets')[copyIndex('value')], 'privateLinkServiceNetworkPolicies'), 'delegation', tryGet(parameters('subnets')[copyIndex('value')], 'delegation'))]" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "name": "vnetDiagnostics", + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs", + "enabled": true + } + ], + "metricCategories": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "16195883788906927531" + }, + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network (vNet)." + }, + "definitions": { + "peeringType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." + } + }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." + } + }, + "remotePeeringEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Deploy the outbound and the inbound peering." + } + }, + "remotePeeringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName." + } + }, + "remotePeeringAllowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." + } + }, + "remotePeeringAllowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "remotePeeringAllowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." + } + }, + "remotePeeringDoNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "remotePeeringUseRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." + } + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "ipamPoolPrefixAllocations": { + "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "pool": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the IPAM pool." + } + } + }, + "metadata": { + "description": "Required. The Resource ID of the IPAM pool." + } + }, + "numberOfIpAddresses": { + "type": "string", + "metadata": { + "description": "Required. Number of IP addresses allocated from the pool." + } + } + } + } + ], + "items": false, + "nullable": true, + "metadata": { + "description": "Conditional. The address space for the subnet, deployed from IPAM Pool. Required if `addressPrefixes` and `addressPrefix` is empty and the VNet address space configured to use IPAM Pool." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Virtual Network (vNet)." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. An Array of 1 or more IP Address Prefixes OR the resource ID of the IPAM pool to be used for the Virtual Network. When specifying an IPAM pool resource ID you must also set a value for the parameter called `ipamPoolNumberOfIpAddresses`." + } + }, + "ipamPoolNumberOfIpAddresses": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Number of IP addresses allocated from the pool. To be used only when the addressPrefix param is defined with a resource ID of an IPAM pool." + } + }, + "virtualNetworkBgpCommunity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The BGP community associated with the virtual network." + } + }, + "subnets": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetType" + }, + "nullable": true, + "metadata": { + "description": "Optional. An Array of subnets to deploy to the Virtual Network." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. DNS Servers associated to the Virtual Network." + } + }, + "ddosProtectionPlanResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the DDoS protection plan to assign the VNET to. If it's left blank, DDoS protection will not be configured. If it's provided, the VNET created by this template will be attached to the referenced DDoS protection plan. The DDoS protection plan can exist in the same or in a different subscription." + } + }, + "peerings": { + "type": "array", + "items": { + "$ref": "#/definitions/peeringType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Virtual Network Peering configurations." + } + }, + "vnetEncryption": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if encryption is enabled on virtual network and if VM without encryption is allowed in encrypted VNet. Requires the EnableVNetEncryption feature to be registered for the subscription and a supported region to use this property." + } + }, + "vnetEncryptionEnforcement": { + "type": "string", + "defaultValue": "AllowUnencrypted", + "allowedValues": [ + "AllowUnencrypted", + "DropUnencrypted" + ], + "metadata": { + "description": "Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled." + } + }, + "flowTimeoutInMinutes": { + "type": "int", + "defaultValue": 0, + "maxValue": 30, + "metadata": { + "description": "Optional. The flow timeout in minutes for the Virtual Network, which is used to enable connection tracking for intra-VM flows. Possible values are between 4 and 30 minutes. Default value 0 will set the property to null." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "enableVmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.7.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addressSpace": "[if(contains(parameters('addressPrefixes')[0], '/Microsoft.Network/networkManagers/'), createObject('ipamPoolPrefixAllocations', createArray(createObject('pool', createObject('id', parameters('addressPrefixes')[0]), 'numberOfIpAddresses', parameters('ipamPoolNumberOfIpAddresses')))), createObject('addressPrefixes', parameters('addressPrefixes')))]", + "bgpCommunities": "[if(not(empty(parameters('virtualNetworkBgpCommunity'))), createObject('virtualNetworkCommunity', parameters('virtualNetworkBgpCommunity')), null())]", + "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", + "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", + "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", + "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", + "enableVmProtection": "[parameters('enableVmProtection')]" + } + }, + "virtualNetwork_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_diagnosticSettings": { + "copy": { + "name": "virtualNetwork_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_roleAssignments": { + "copy": { + "name": "virtualNetwork_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_subnets": { + "copy": { + "name": "virtualNetwork_subnets", + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-subnet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]" + }, + "addressPrefix": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]" + }, + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "ipamPoolPrefixAllocations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'ipamPoolPrefixAllocations')]" + }, + "applicationGatewayIPConfigurations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]" + }, + "delegation": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]" + }, + "natGatewayResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]" + }, + "networkSecurityGroupResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]" + }, + "privateEndpointNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]" + }, + "privateLinkServiceNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "routeTableResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]" + }, + "serviceEndpointPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]" + }, + "serviceEndpoints": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpoints')]" + }, + "defaultOutboundAccess": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'defaultOutboundAccess')]" + }, + "sharingScope": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'sharingScope')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "9728353654559466189" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "ipamPoolPrefixAllocations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Conditional. The address space for the subnet, deployed from IPAM Pool. Required if `addressPrefixes` and `addressPrefix` is empty." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing the subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if the subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetworksubnet.{0}.{1}', replace('0.1.2', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], + "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "ipamPoolPrefixAllocations": "[parameters('ipamPoolPrefixAllocations')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", + "privateEndpointNetworkPolicies": "[parameters('privateEndpointNetworkPolicies')]", + "privateLinkServiceNetworkPolicies": "[parameters('privateLinkServiceNetworkPolicies')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" + } + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" + }, + "ipamPoolPrefixAllocations": { + "type": "array", + "metadata": { + "description": "The IPAM pool prefix allocations for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'ipamPoolPrefixAllocations'), createArray())]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_local": { + "copy": { + "name": "virtualNetwork_peering_local", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-local-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[parameters('name')]" + }, + "remoteVirtualNetworkResourceId": { + "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "11179987886456111827" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork", + "virtualNetwork_subnets" + ] + }, + "virtualNetwork_peering_remote": { + "copy": { + "name": "virtualNetwork_peering_remote", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" + }, + "condition": "[coalesce(tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringEnabled'), false())]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[2]]", + "resourceGroup": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]" + }, + "remoteVirtualNetworkResourceId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "11179987886456111827" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering." + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the VNet that is this Local VNet is being peered to. Should be in the format of a Resource ID." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether the forwarded traffic from the VMs in the local virtual network will be allowed/disallowed in remote virtual network. Default is true." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether the VMs in the local virtual network space would be able to access the VMs in remote virtual network space. Default is true." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If remote gateways can be used on this virtual network. If the flag is set to true, and allowGatewayTransit on remote peering is also true, virtual network will use gateways of remote virtual network for transit. Only one peering can have this flag set to true. This flag cannot be set if virtual network already has a gateway. Default is false." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork", + "virtualNetwork_subnets" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network." + }, + "value": "[parameters('name')]" + }, + "subnetNames": { + "type": "array", + "metadata": { + "description": "The names of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.name.value]" + } + }, + "subnetResourceIds": { + "type": "array", + "metadata": { + "description": "The resource IDs of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.resourceId.value]" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetwork', '2024-05-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "nsgs" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "value": "[reference('virtualNetwork').outputs.name.value]" + }, + "resourceId": { + "type": "string", + "value": "[reference('virtualNetwork').outputs.resourceId.value]" + }, + "subnets": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetOutputType" + }, + "copy": { + "count": "[length(parameters('subnets'))]", + "input": { + "name": "[parameters('subnets')[copyIndex()].name]", + "resourceId": "[reference('virtualNetwork').outputs.subnetResourceIds.value[copyIndex()]]", + "nsgName": "[if(not(empty(tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup'))), tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup', 'name'), null())]", + "nsgResourceId": "[if(not(empty(tryGet(parameters('subnets')[copyIndex()], 'networkSecurityGroup'))), reference(format('nsgs[{0}]', copyIndex())).outputs.resourceId.value, null())]" + } + } + } + } + } + } + }, + "bastionHost": { + "condition": "[not(empty(parameters('bastionConfiguration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-bastionHost', parameters('resourcesName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('bastionConfiguration'), 'name'), format('bas-{0}', parameters('resourcesName')))]" + }, + "vnetId": { + "value": "[reference('virtualNetwork').outputs.resourceId.value]" + }, + "vnetName": { + "value": "[reference('virtualNetwork').outputs.name.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[parameters('logAnalyticsWorkSpaceResourceId')]" + }, + "subnet": { + "value": "[tryGet(parameters('bastionConfiguration'), 'subnet')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "3082168335446205769" + } + }, + "definitions": { + "bastionHostConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Bastion Host resource." + } + }, + "subnet": { + "$ref": "#/definitions/subnetType", + "nullable": true, + "metadata": { + "description": "Optional. Subnet configuration for the Jumpbox VM." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Custom type definition for establishing Bastion Host for remote connection." + } + }, + "_1.networkSecurityGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the network security group." + } + }, + "securityRules": { + "type": "array", + "items": { + "type": "object" + }, + "metadata": { + "description": "Required. The security rules for the network security group." + } + } + }, + "metadata": { + "description": "Custom type definition for network security group configuration", + "__bicep_imported_from!": { + "sourceTemplate": "virtualNetwork.bicep" + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. Prefixes for the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "networkSecurityGroup": { + "$ref": "#/definitions/_1.networkSecurityGroupType", + "nullable": true, + "metadata": { + "description": "Optional. Network Security Group configuration for the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + } + }, + "metadata": { + "description": "Custom type definition for subnet configuration", + "__bicep_imported_from!": { + "sourceTemplate": "virtualNetwork.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Azure Bastion Host resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Azure region to deploy resources." + } + }, + "vnetId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Virtual Network where the Azure Bastion Host will be deployed." + } + }, + "vnetName": { + "type": "string", + "metadata": { + "description": "Name of the Virtual Network where the Azure Bastion Host will be deployed." + } + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Log Analytics Workspace for monitoring and diagnostics." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to the resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "subnet": { + "$ref": "#/definitions/subnetType", + "nullable": true, + "metadata": { + "description": "Optional. Subnet configuration for the Jumpbox VM." + } + } + }, + "resources": { + "nsg": { + "condition": "[not(empty(parameters('subnet')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}', parameters('vnetName'), tryGet(parameters('subnet'), 'networkSecurityGroup', 'name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}', tryGet(parameters('subnet'), 'networkSecurityGroup', 'name'), parameters('vnetName'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "securityRules": { + "value": "[tryGet(parameters('subnet'), 'networkSecurityGroup', 'securityRules')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "2305747478751645177" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG)." + }, + "definitions": { + "securityRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "properties": { + "type": "object", + "properties": { + "access": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the security rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Optional. The destination address prefix. CIDR or destination IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used." + } + }, + "destinationAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "destinationPortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination port ranges." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 4096, + "metadata": { + "description": "Required. Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "*", + "Ah", + "Esp", + "Icmp", + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Network protocol this rule applies to." + } + }, + "sourceAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used. If this is an ingress rule, specifies where network traffic originates from." + } + }, + "sourceAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The source port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "sourcePortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "metadata": { + "description": "Required. The properties of the security rule." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of a security rule." + } + }, + "diagnosticSettingLogsOnlyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Network Security Group." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "securityRules": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed." + } + }, + "flushConnection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(coalesce(parameters('securityRules'), createArray()))]", + "input": { + "name": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].name]", + "properties": { + "access": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.access]", + "description": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'description'), '')]", + "destinationAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), '')]", + "destinationAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), createArray())]", + "destinationApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroupResourceIds'), createArray()), lambda('destinationApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('destinationApplicationSecurityGroupResourceId'))))]", + "destinationPortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRange'), '')]", + "destinationPortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRanges'), createArray())]", + "direction": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.direction]", + "priority": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.priority]", + "protocol": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.protocol]", + "sourceAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), '')]", + "sourceAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), createArray())]", + "sourceApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroupResourceIds'), createArray()), lambda('sourceApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('sourceApplicationSecurityGroupResourceId'))))]", + "sourcePortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRange'), '')]", + "sourcePortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRanges'), createArray())]" + } + } + } + ], + "flushConnection": "[parameters('flushConnection')]" + } + }, + "networkSecurityGroup_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_diagnosticSettings": { + "copy": { + "name": "networkSecurityGroup_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_roleAssignments": { + "copy": { + "name": "networkSecurityGroup_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + "bastionSubnet": { + "condition": "[not(empty(parameters('subnet')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('bastionSubnet-{0}', parameters('vnetName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkName": { + "value": "[parameters('vnetName')]" + }, + "name": { + "value": "AzureBastionSubnet" + }, + "addressPrefixes": { + "value": "[tryGet(parameters('subnet'), 'addressPrefixes')]" + }, + "networkSecurityGroupResourceId": { + "value": "[reference('nsg').outputs.resourceId.value]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "9728353654559466189" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "ipamPoolPrefixAllocations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Conditional. The address space for the subnet, deployed from IPAM Pool. Required if `addressPrefixes` and `addressPrefix` is empty." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing the subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if the subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetworksubnet.{0}.{1}', replace('0.1.2', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], + "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "ipamPoolPrefixAllocations": "[parameters('ipamPoolPrefixAllocations')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", + "privateEndpointNetworkPolicies": "[parameters('privateEndpointNetworkPolicies')]", + "privateLinkServiceNetworkPolicies": "[parameters('privateLinkServiceNetworkPolicies')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" + } + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" + }, + "ipamPoolPrefixAllocations": { + "type": "array", + "metadata": { + "description": "The IPAM pool prefix allocations for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'ipamPoolPrefixAllocations'), createArray())]" + } + } + } + }, + "dependsOn": [ + "nsg" + ] + }, + "bastionHost": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('bastionHost-{0}-{1}', parameters('vnetName'), parameters('name')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "skuName": { + "value": "Standard" + }, + "location": { + "value": "[parameters('location')]" + }, + "virtualNetworkResourceId": { + "value": "[parameters('vnetId')]" + }, + "diagnosticSettings": { + "value": [ + { + "name": "bastionDiagnostics", + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs", + "enabled": true + } + ] + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "publicIPAddressObject": { + "value": { + "name": "[format('pip-{0}', parameters('name'))]", + "zones": [] + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "2586599138991803385" + }, + "name": "Bastion Hosts", + "description": "This module deploys a Bastion Host." + }, + "definitions": { + "diagnosticSettingLogsOnlyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Bastion resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Shared services Virtual Network resource Id." + } + }, + "bastionSubnetPublicIpResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Public IP resource ID to associate to the azureBastionSubnet. If empty, then the Public IP that is created as part of this module will be applied to the azureBastionSubnet. This parameter is ignored when enablePrivateOnlyBastion is true." + } + }, + "publicIPAddressObject": { + "type": "object", + "defaultValue": { + "name": "[format('{0}-pip', parameters('name'))]" + }, + "metadata": { + "description": "Optional. Specifies the properties of the Public IP to create and be used by Azure Bastion, if no existing public IP was provided. This parameter is ignored when enablePrivateOnlyBastion is true." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Developer", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. The SKU of this Bastion Host." + } + }, + "disableCopyPaste": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Copy Paste. For Basic and Developer SKU Copy/Paste is always enabled." + } + }, + "enableFileCopy": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Choose to disable or enable File Copy. Not supported for Basic and Developer SKU." + } + }, + "enableIpConnect": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable IP Connect. Not supported for Basic and Developer SKU." + } + }, + "enableKerberos": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Kerberos authentication. Not supported for Developer SKU." + } + }, + "enableShareableLink": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Shareable Link. Not supported for Basic and Developer SKU." + } + }, + "enableSessionRecording": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Session Recording feature. The Premium SKU is required for this feature. If Session Recording is enabled, the Native client support will be disabled." + } + }, + "enablePrivateOnlyBastion": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Private-only Bastion deployment. The Premium SKU is required for this feature." + } + }, + "scaleUnits": { + "type": "int", + "defaultValue": 2, + "metadata": { + "description": "Optional. The scale units for the Bastion Host resource. The Basic and Developer SKU only support 2 scale units." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting where the Bastion Host resource needs to come from. This is not supported for the Developer SKU." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-bastionhost.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "azureBastion": { + "type": "Microsoft.Network/bastionHosts", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[coalesce(parameters('tags'), createObject())]", + "sku": { + "name": "[parameters('skuName')]" + }, + "zones": "[if(equals(parameters('skuName'), 'Developer'), createArray(), map(parameters('zones'), lambda('zone', string(lambdaVariables('zone')))))]", + "properties": "[union(createObject('scaleUnits', if(or(equals(parameters('skuName'), 'Basic'), equals(parameters('skuName'), 'Developer')), 2, parameters('scaleUnits')), 'ipConfigurations', if(equals(parameters('skuName'), 'Developer'), createArray(), createArray(createObject('name', 'IpConfAzureBastionSubnet', 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureBastionSubnet', parameters('virtualNetworkResourceId')))), if(not(parameters('enablePrivateOnlyBastion')), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('bastionSubnetPublicIpResourceId'))), parameters('bastionSubnetPublicIpResourceId'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))))), if(equals(parameters('skuName'), 'Developer'), createObject('virtualNetwork', createObject('id', parameters('virtualNetworkResourceId'))), createObject()), if(or(or(equals(parameters('skuName'), 'Basic'), equals(parameters('skuName'), 'Standard')), equals(parameters('skuName'), 'Premium')), createObject('enableKerberos', parameters('enableKerberos')), createObject()), if(or(equals(parameters('skuName'), 'Standard'), equals(parameters('skuName'), 'Premium')), createObject('enableTunneling', if(equals(parameters('skuName'), 'Standard'), true(), if(parameters('enableSessionRecording'), false(), true())), 'disableCopyPaste', parameters('disableCopyPaste'), 'enableFileCopy', parameters('enableFileCopy'), 'enableIpConnect', parameters('enableIpConnect'), 'enableShareableLink', parameters('enableShareableLink')), createObject()), if(equals(parameters('skuName'), 'Premium'), createObject('enableSessionRecording', parameters('enableSessionRecording'), 'enablePrivateOnlyBastion', parameters('enablePrivateOnlyBastion')), createObject()))]", + "dependsOn": [ + "publicIPAddress" + ] + }, + "azureBastion_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "azureBastion_diagnosticSettings": { + "copy": { + "name": "azureBastion_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "azureBastion_roleAssignments": { + "copy": { + "name": "azureBastion_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/bastionHosts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "publicIPAddress": { + "condition": "[and(and(empty(parameters('bastionSubnetPublicIpResourceId')), not(equals(parameters('skuName'), 'Developer'))), not(parameters('enablePrivateOnlyBastion')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Bastion-PIP', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('publicIPAddressObject').name]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'diagnosticSettings')]" + }, + "publicIPAddressVersion": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPAddressVersion')]" + }, + "publicIPAllocationMethod": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPAllocationMethod')]" + }, + "publicIpPrefixResourceId": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPPrefixResourceId')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'roleAssignments')]" + }, + "skuName": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'skuName')]" + }, + "skuTier": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'skuTier')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'tags'), parameters('tags'))]" + }, + "zones": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'zones'), if(greater(length(parameters('zones')), 0), parameters('zones'), null()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "5168739580767459761" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address." + }, + "definitions": { + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "nullable": true, + "metadata": { + "description": "Optional. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipTagType": { + "type": "object", + "properties": { + "ipTagType": { + "type": "string", + "metadata": { + "description": "Required. The IP tag type." + } + }, + "tag": { + "type": "string", + "metadata": { + "description": "Required. The IP tag." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "ipTags": { + "type": "array", + "items": { + "$ref": "#/definitions/ipTagType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of tags associated with the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.8.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": "[parameters('ipTags')]" + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2024-05-01', 'full').location]" + } + } + } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the Azure Bastion was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name the Azure Bastion." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID the Azure Bastion." + }, + "value": "[resourceId('Microsoft.Network/bastionHosts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('azureBastion', '2024-05-01', 'full').location]" + }, + "ipConfAzureBastionSubnet": { + "type": "object", + "metadata": { + "description": "The Public IPconfiguration object for the AzureBastionSubnet." + }, + "value": "[if(equals(parameters('skuName'), 'Developer'), createObject(), reference('azureBastion').ipConfigurations[0])]" + } + } + } + }, + "dependsOn": [ + "bastionSubnet" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('bastionHost').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('bastionHost').outputs.name.value]" + }, + "subnetId": { + "type": "string", + "value": "[reference('bastionSubnet').outputs.resourceId.value]" + }, + "subnetName": { + "type": "string", + "value": "[reference('bastionSubnet').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "jumpbox": { + "condition": "[not(empty(parameters('jumpboxConfiguration')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-jumpbox', parameters('resourcesName'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('jumpboxConfiguration'), 'name'), format('vm-jumpbox-{0}', parameters('resourcesName')))]" + }, + "vnetName": { + "value": "[reference('virtualNetwork').outputs.name.value]" + }, + "size": { + "value": "[coalesce(tryGet(parameters('jumpboxConfiguration'), 'size'), 'Standard_D2s_v3')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[parameters('logAnalyticsWorkSpaceResourceId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "subnet": { + "value": "[tryGet(parameters('jumpboxConfiguration'), 'subnet')]" + }, + "username": { + "value": "[coalesce(tryGet(parameters('jumpboxConfiguration'), 'username'), '')]" + }, + "password": { + "value": "[coalesce(tryGet(parameters('jumpboxConfiguration'), 'password'), '')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "40464970328764907" + } + }, + "definitions": { + "jumpBoxConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Virtual Machine." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The size of the VM." + } + }, + "username": { + "type": "string", + "metadata": { + "description": "Username to access VM." + } + }, + "password": { + "type": "securestring", + "metadata": { + "description": "Password to access VM." + } + }, + "subnet": { + "$ref": "#/definitions/subnetType", + "nullable": true, + "metadata": { + "description": "Optional. Subnet configuration for the Jumpbox VM." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Custom type definition for establishing Jumpbox Virtual Machine and its associated resources." + } + }, + "_1.networkSecurityGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the network security group." + } + }, + "securityRules": { + "type": "array", + "items": { + "type": "object" + }, + "metadata": { + "description": "Required. The security rules for the network security group." + } + } + }, + "metadata": { + "description": "Custom type definition for network security group configuration", + "__bicep_imported_from!": { + "sourceTemplate": "virtualNetwork.bicep" + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. Prefixes for the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "networkSecurityGroup": { + "$ref": "#/definitions/_1.networkSecurityGroupType", + "nullable": true, + "metadata": { + "description": "Optional. Network Security Group configuration for the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + } + }, + "metadata": { + "description": "Custom type definition for subnet configuration", + "__bicep_imported_from!": { + "sourceTemplate": "virtualNetwork.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Name of the Jumpbox Virtual Machine." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Azure region to deploy resources." + } + }, + "vnetName": { + "type": "string", + "metadata": { + "description": "Name of the Virtual Network where the Jumpbox VM will be deployed." + } + }, + "size": { + "type": "string", + "metadata": { + "description": "Size of the Jumpbox Virtual Machine." + } + }, + "subnet": { + "$ref": "#/definitions/subnetType", + "nullable": true, + "metadata": { + "description": "Optional. Subnet configuration for the Jumpbox VM." + } + }, + "username": { + "type": "string", + "metadata": { + "description": "Username to access the Jumpbox VM." + } + }, + "password": { + "type": "securestring", + "metadata": { + "description": "Password to access the Jumpbox VM." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags to apply to the resources." + } + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "Log Analytics Workspace Resource ID for VM diagnostics." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "vmName": "[take(parameters('name'), 15)]" + }, + "resources": { + "nsg": { + "condition": "[not(empty(parameters('subnet')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}', parameters('vnetName'), tryGet(parameters('subnet'), 'networkSecurityGroup', 'name'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}', tryGet(parameters('subnet'), 'networkSecurityGroup', 'name'), parameters('vnetName'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "securityRules": { + "value": "[tryGet(parameters('subnet'), 'networkSecurityGroup', 'securityRules')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "2305747478751645177" + }, + "name": "Network Security Groups", + "description": "This module deploys a Network security Group (NSG)." + }, + "definitions": { + "securityRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the security rule." + } + }, + "properties": { + "type": "object", + "properties": { + "access": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. Whether network traffic is allowed or denied." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the security rule." + } + }, + "destinationAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Optional. The destination address prefix. CIDR or destination IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used." + } + }, + "destinationAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination address prefixes. CIDR or destination IP ranges." + } + }, + "destinationApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as destination." + } + }, + "destinationPortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The destination port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "destinationPortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The destination port ranges." + } + }, + "direction": { + "type": "string", + "allowedValues": [ + "Inbound", + "Outbound" + ], + "metadata": { + "description": "Required. The direction of the rule. The direction specifies if rule will be evaluated on incoming or outgoing traffic." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 4096, + "metadata": { + "description": "Required. Required. The priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "*", + "Ah", + "Esp", + "Icmp", + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Network protocol this rule applies to." + } + }, + "sourceAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP range. Asterisk \"*\" can also be used to match all source IPs. Default tags such as \"VirtualNetwork\", \"AzureLoadBalancer\" and \"Internet\" can also be used. If this is an ingress rule, specifies where network traffic originates from." + } + }, + "sourceAddressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The CIDR or source IP ranges." + } + }, + "sourceApplicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource IDs of the application security groups specified as source." + } + }, + "sourcePortRange": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The source port or range. Integer or range between 0 and 65535. Asterisk \"*\" can also be used to match all ports." + } + }, + "sourcePortRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The source port ranges." + } + } + }, + "metadata": { + "description": "Required. The properties of the security rule." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of a security rule." + } + }, + "diagnosticSettingLogsOnlyType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Network Security Group." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "securityRules": { + "type": "array", + "items": { + "$ref": "#/definitions/securityRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Security Rules to deploy to the Network Security Group. When not provided, an NSG including only the built-in roles will be deployed." + } + }, + "flushConnection": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation. Network Security Group connection flushing is not available in all regions." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingLogsOnlyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the NSG resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "networkSecurityGroup": { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "securityRules", + "count": "[length(coalesce(parameters('securityRules'), createArray()))]", + "input": { + "name": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].name]", + "properties": { + "access": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.access]", + "description": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'description'), '')]", + "destinationAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefix'), '')]", + "destinationAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationAddressPrefixes'), createArray())]", + "destinationApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationApplicationSecurityGroupResourceIds'), createArray()), lambda('destinationApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('destinationApplicationSecurityGroupResourceId'))))]", + "destinationPortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRange'), '')]", + "destinationPortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'destinationPortRanges'), createArray())]", + "direction": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.direction]", + "priority": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.priority]", + "protocol": "[coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties.protocol]", + "sourceAddressPrefix": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefix'), '')]", + "sourceAddressPrefixes": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceAddressPrefixes'), createArray())]", + "sourceApplicationSecurityGroups": "[map(coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourceApplicationSecurityGroupResourceIds'), createArray()), lambda('sourceApplicationSecurityGroupResourceId', createObject('id', lambdaVariables('sourceApplicationSecurityGroupResourceId'))))]", + "sourcePortRange": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRange'), '')]", + "sourcePortRanges": "[coalesce(tryGet(coalesce(parameters('securityRules'), createArray())[copyIndex('securityRules')].properties, 'sourcePortRanges'), createArray())]" + } + } + } + ], + "flushConnection": "[parameters('flushConnection')]" + } + }, + "networkSecurityGroup_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_diagnosticSettings": { + "copy": { + "name": "networkSecurityGroup_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_roleAssignments": { + "copy": { + "name": "networkSecurityGroup_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + "subnetResource": { + "condition": "[not(empty(parameters('subnet')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[coalesce(tryGet(parameters('subnet'), 'name'), format('{0}-jumpbox-subnet', parameters('vnetName')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkName": { + "value": "[parameters('vnetName')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('subnet'), 'name'), '')]" + }, + "addressPrefixes": { + "value": "[tryGet(parameters('subnet'), 'addressPrefixes')]" + }, + "networkSecurityGroupResourceId": { + "value": "[reference('nsg').outputs.resourceId.value]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "9728353654559466189" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "ipamPoolPrefixAllocations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Conditional. The address space for the subnet, deployed from IPAM Pool. Required if `addressPrefixes` and `addressPrefix` is empty." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled", + "NetworkSecurityGroupEnabled", + "RouteTableEnabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing the subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if the subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetworksubnet.{0}.{1}', replace('0.1.2', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], + "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "ipamPoolPrefixAllocations": "[parameters('ipamPoolPrefixAllocations')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", + "privateEndpointNetworkPolicies": "[parameters('privateEndpointNetworkPolicies')]", + "privateLinkServiceNetworkPolicies": "[parameters('privateLinkServiceNetworkPolicies')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" + } + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" + }, + "ipamPoolPrefixAllocations": { + "type": "array", + "metadata": { + "description": "The IPAM pool prefix allocations for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'ipamPoolPrefixAllocations'), createArray())]" + } + } + } + }, + "dependsOn": [ + "nsg" + ] + }, + "vm": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('{0}-jumpbox', variables('vmName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('vmName')]" + }, + "vmSize": { + "value": "[parameters('size')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "adminUsername": { + "value": "[parameters('username')]" + }, + "adminPassword": { + "value": "[parameters('password')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "zone": { + "value": 0 + }, + "imageReference": { + "value": { + "offer": "WindowsServer", + "publisher": "MicrosoftWindowsServer", + "sku": "2019-datacenter", + "version": "latest" + } + }, + "osType": { + "value": "Windows" + }, + "osDisk": { + "value": { + "name": "[format('osdisk-{0}', variables('vmName'))]", + "managedDisk": { + "storageAccountType": "Standard_LRS" + } + } + }, + "encryptionAtHost": { + "value": false + }, + "nicConfigurations": { + "value": [ + { + "name": "[format('nic-{0}', variables('vmName'))]", + "ipConfigurations": [ + { + "name": "ipconfig1", + "subnetResourceId": "[reference('subnetResource').outputs.resourceId.value]" + } + ], + "networkSecurityGroupResourceId": "[reference('nsg').outputs.resourceId.value]", + "diagnosticSettings": [ + { + "name": "jumpboxDiagnostics", + "workspaceResourceId": "[parameters('logAnalyticsWorkspaceId')]", + "logCategoriesAndGroups": [ + { + "categoryGroup": "allLogs", + "enabled": true + } + ], + "metricCategories": [ + { + "category": "AllMetrics", + "enabled": true + } + ] + } + ] + } + ] + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "1057634180502804806" + }, + "name": "Virtual Machines", + "description": "This module deploys a Virtual Machine with one or multiple NICs and optionally one or multiple public IPs." + }, + "definitions": { + "osDiskType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name." + } + }, + "diskSizeGB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the size of an empty data disk in gigabytes." + } + }, + "createOption": { + "type": "string", + "allowedValues": [ + "Attach", + "Empty", + "FromImage" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies how the virtual machine should be created." + } + }, + "deleteOption": { + "type": "string", + "allowedValues": [ + "Delete", + "Detach" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether data disk should be deleted or detached upon VM deletion." + } + }, + "caching": { + "type": "string", + "allowedValues": [ + "None", + "ReadOnly", + "ReadWrite" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the caching requirements." + } + }, + "diffDiskSettings": { + "type": "object", + "properties": { + "placement": { + "type": "string", + "allowedValues": [ + "CacheDisk", + "NvmeDisk", + "ResourceDisk" + ], + "metadata": { + "description": "Required. Specifies the ephemeral disk placement for the operating system disk." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies the ephemeral Disk Settings for the operating system disk." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the storage account type for the managed disk." + } + }, + "diskEncryptionSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the customer managed disk encryption set resource id for the managed disk." + } + } + }, + "metadata": { + "description": "Required. The managed disk parameters." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing an OS disk." + } + }, + "dataDiskType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The disk name. When attaching a pre-existing disk, this name is ignored and the name of the existing disk is used." + } + }, + "lun": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the logical unit number of the data disk." + } + }, + "diskSizeGB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the size of an empty data disk in gigabytes. This property is ignored when attaching a pre-existing disk." + } + }, + "createOption": { + "type": "string", + "allowedValues": [ + "Attach", + "Empty", + "FromImage" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies how the virtual machine should be created. This property is automatically set to 'Attach' when attaching a pre-existing disk." + } + }, + "deleteOption": { + "type": "string", + "allowedValues": [ + "Delete", + "Detach" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether data disk should be deleted or detached upon VM deletion. This property is automatically set to 'Detach' when attaching a pre-existing disk." + } + }, + "caching": { + "type": "string", + "allowedValues": [ + "None", + "ReadOnly", + "ReadWrite" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the caching requirements. This property is automatically set to 'None' when attaching a pre-existing disk." + } + }, + "diskIOPSReadWrite": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of IOPS allowed for this disk; only settable for UltraSSD disks. One operation can transfer between 4k and 256k bytes. Ignored when attaching a pre-existing disk." + } + }, + "diskMBpsReadWrite": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The bandwidth allowed for this disk; only settable for UltraSSD disks. MBps means millions of bytes per second - MB here uses the ISO notation, of powers of 10. Ignored when attaching a pre-existing disk." + } + }, + "managedDisk": { + "type": "object", + "properties": { + "storageAccountType": { + "type": "string", + "allowedValues": [ + "PremiumV2_LRS", + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS", + "UltraSSD_LRS" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the storage account type for the managed disk. Ignored when attaching a pre-existing disk." + } + }, + "diskEncryptionSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the customer managed disk encryption set resource id for the managed disk." + } + }, + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the resource id of a pre-existing managed disk. If the disk should be created, this property should be empty." + } + } + }, + "metadata": { + "description": "Required. The managed disk parameters." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the public IP address. Valid only when creating a new managed disk." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing a data disk." + } + }, + "publicKeyType": { + "type": "object", + "properties": { + "keyData": { + "type": "string", + "metadata": { + "description": "Required. Specifies the SSH public key data used to authenticate through ssh." + } + }, + "path": { + "type": "string", + "metadata": { + "description": "Required. Specifies the full path on the created VM where ssh public key is stored. If the file already exists, the specified key is appended to the file." + } + } + } + }, + "nicConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the NIC configuration." + } + }, + "nicSuffix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The suffix to append to the NIC name." + } + }, + "enableIPForwarding": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether IP forwarding is enabled on this network interface." + } + }, + "enableAcceleratedNetworking": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If the network interface is accelerated networking enabled." + } + }, + "deleteOption": { + "type": "string", + "allowedValues": [ + "Delete", + "Detach" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify what happens to the network interface when the VM is deleted." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of DNS servers IP addresses. Use 'AzureProvidedDNS' to switch to azure provided DNS resolution. 'AzureProvidedDNS' value cannot be combined with other IPs, it must be the only value in dnsServers collection." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The network security group (NSG) to attach to the network interface." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "metadata": { + "description": "Required. The IP configurations of the network interface." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the public IP address." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for the module." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the IP configuration." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the NIC configuration." + } + }, + "imageReferenceType": { + "type": "object", + "properties": { + "communityGalleryImageId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specified the community gallery image unique id for vm deployment. This can be fetched from community gallery image GET call." + } + }, + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource Id of the image reference." + } + }, + "offer": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the offer of the platform image or marketplace image used to create the virtual machine." + } + }, + "publisher": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The image publisher." + } + }, + "sku": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The SKU of the image." + } + }, + "version": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the version of the platform image or marketplace image used to create the virtual machine. The allowed formats are Major.Minor.Build or 'latest'. Even if you use 'latest', the VM image will not automatically update after deploy time even if a new version becomes available." + } + }, + "sharedGalleryImageId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specified the shared gallery image unique id for vm deployment. This can be fetched from shared gallery image GET call." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing the image reference." + } + }, + "planType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the plan." + } + }, + "product": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the product of the image from the marketplace." + } + }, + "publisher": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The publisher ID." + } + }, + "promotionCode": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The promotion code." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Specifies information about the marketplace image used to create the virtual machine." + } + }, + "autoShutDownConfigType": { + "type": "object", + "properties": { + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the auto shutdown configuration." + } + }, + "timeZone": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The time zone ID (e.g. China Standard Time, Greenland Standard Time, Pacific Standard time, etc.)." + } + }, + "dailyRecurrenceTime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The time of day the schedule will occur." + } + }, + "notificationSettings": { + "type": "object", + "properties": { + "status": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the notification settings." + } + }, + "emailRecipient": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The email address to send notifications to (can be a list of semi-colon separated email addresses)." + } + }, + "notificationLocale": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The locale to use when sending a notification (fallback for unsupported languages is EN)." + } + }, + "webhookUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The webhook URL to which the notification will be sent." + } + }, + "timeInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The time in minutes before shutdown to send notifications." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the schedule." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing the configuration profile." + } + }, + "vaultSecretGroupType": { + "type": "object", + "properties": { + "sourceVault": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. The relative URL of the Key Vault containing all of the certificates in VaultCertificates." + } + }, + "vaultCertificates": { + "type": "array", + "items": { + "type": "object", + "properties": { + "certificateStore": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. For Windows VMs, specifies the certificate store on the Virtual Machine to which the certificate should be added. The specified certificate store is implicitly in the LocalMachine account. For Linux VMs, the certificate file is placed under the /var/lib/waagent directory, with the file name .crt for the X509 certificate file and .prv for private key. Both of these files are .pem formatted." + } + }, + "certificateUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. This is the URL of a certificate that has been uploaded to Key Vault as a secret." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of key vault references in SourceVault which contain certificates." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing the set of certificates that should be installed onto the virtual machine." + } + }, + "vmGalleryApplicationType": { + "type": "object", + "properties": { + "packageReferenceId": { + "type": "string", + "metadata": { + "description": "Required. Specifies the GalleryApplicationVersion resource id on the form of /subscriptions/{SubscriptionId}/resourceGroups/{ResourceGroupName}/providers/Microsoft.Compute/galleries/{galleryName}/applications/{application}/versions/{version}." + } + }, + "configurationReference": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the uri to an azure blob that will replace the default configuration for the package if provided." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If set to true, when a new Gallery Application version is available in PIR/SIG, it will be automatically updated for the VM/VMSS." + } + }, + "order": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the order in which the packages have to be installed." + } + }, + "tags": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies a passthrough value for more generic context." + } + }, + "treatFailureAsDeploymentFailure": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If true, any failure for any operation in the VmApplication will fail the deployment." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing the gallery application that should be made available to the VM/VMSS." + } + }, + "additionalUnattendContentType": { + "type": "object", + "properties": { + "settingName": { + "type": "string", + "allowedValues": [ + "AutoLogon", + "FirstLogonCommands" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the name of the setting to which the content applies." + } + }, + "content": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the XML formatted content that is added to the unattend.xml file for the specified path and component. The XML must be less than 4KB and must include the root element for the setting or feature that is being inserted." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing additional base-64 encoded XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup." + } + }, + "winRMListenerType": { + "type": "object", + "properties": { + "certificateUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The URL of a certificate that has been uploaded to Key Vault as a secret." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "Http", + "Https" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the protocol of WinRM listener." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing a Windows Remote Management listener." + } + }, + "nicConfigurationOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the NIC configuration." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/networkInterfaceIPConfigurationOutputType" + }, + "metadata": { + "description": "Required. List of IP configurations of the NIC configuration." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type describing the network interface configuration output." + } + }, + "_1.applicationGatewayBackendAddressPoolsType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the backend address pool." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the backend address pool that is unique within an Application Gateway." + } + }, + "properties": { + "type": "object", + "properties": { + "backendAddresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. IP address of the backend address." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN of the backend address." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Backend addresses." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Properties of the application gateway backend address pool." + } + } + }, + "metadata": { + "description": "The type for the application gateway backend address pool.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "_1.applicationSecurityGroupType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the application security group." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location of the application security group." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Properties of the application security group." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the application security group." + } + } + }, + "metadata": { + "description": "The type for the application security group.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "_1.backendAddressPoolType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the backend address pool." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the backend address pool." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The properties of the backend address pool." + } + } + }, + "metadata": { + "description": "The type for a backend address pool.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "_1.inboundNatRuleType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the inbound NAT rule." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the resource that is unique within the set of inbound NAT rules used by the load balancer. This name can be used to access the resource." + } + }, + "properties": { + "type": "object", + "properties": { + "backendAddressPool": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. A reference to backendAddressPool resource." + } + }, + "backendPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port used for the internal endpoint. Acceptable values range from 1 to 65535." + } + }, + "enableFloatingIP": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint." + } + }, + "enableTcpReset": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP." + } + }, + "frontendIPConfiguration": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. A reference to frontend IP addresses." + } + }, + "frontendPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. Acceptable values range from 1 to 65534." + } + }, + "frontendPortRangeStart": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534." + } + }, + "frontendPortRangeEnd": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "All", + "Tcp", + "Udp" + ], + "nullable": true, + "metadata": { + "description": "Optional. The reference to the transport protocol used by the load balancing rule." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Properties of the inbound NAT rule." + } + } + }, + "metadata": { + "description": "The type for the inbound NAT rule.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "_1.virtualNetworkTapType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the virtual network tap." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location of the virtual network tap." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Properties of the virtual network tap." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the virtual network tap." + } + } + }, + "metadata": { + "description": "The type for the virtual network tap.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "_2.ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0" + } + } + }, + "_2.dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "nullable": true, + "metadata": { + "description": "Optional. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0" + } + } + }, + "_3.publicIPConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Public IP Address." + } + }, + "publicIPAddressResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the public IP address." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Diagnostic settings for the public IP address." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The idle timeout in minutes." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the public IP address." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "ddosSettings": { + "$ref": "#/definitions/_2.ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "dnsSettings": { + "$ref": "#/definitions/_2.dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "publicIPAddressVersion": { + "type": "string", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "nullable": true, + "metadata": { + "description": "Optional. The public IP address version." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "allowedValues": [ + "Dynamic", + "Static" + ], + "nullable": true, + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix." + } + }, + "publicIpNameSuffix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name suffix of the public IP address resource." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "skuName": { + "type": "string", + "allowedValues": [ + "Basic", + "Standard" + ], + "nullable": true, + "metadata": { + "description": "Optional. The SKU name of the public IP address." + } + }, + "skuTier": { + "type": "string", + "allowedValues": [ + "Global", + "Regional" + ], + "nullable": true, + "metadata": { + "description": "Optional. The SKU tier of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the public IP address." + } + }, + "zones": { + "type": "array", + "allowedValues": [ + 1, + 2, + 3 + ], + "nullable": true, + "metadata": { + "description": "Optional. The zones of the public IP address." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for the module." + } + } + }, + "metadata": { + "description": "The type for the public IP address configuration.", + "__bicep_imported_from!": { + "sourceTemplate": "modules/nic-configuration.bicep" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the IP configuration." + } + }, + "privateIPAllocationMethod": { + "type": "string", + "allowedValues": [ + "Dynamic", + "Static" + ], + "nullable": true, + "metadata": { + "description": "Optional. The private IP address allocation method." + } + }, + "privateIPAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The private IP address." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "loadBalancerBackendAddressPools": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.backendAddressPoolType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The load balancer backend address pools." + } + }, + "applicationSecurityGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.applicationSecurityGroupType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The application security groups." + } + }, + "applicationGatewayBackendAddressPools": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.applicationGatewayBackendAddressPoolsType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The application gateway backend address pools." + } + }, + "gatewayLoadBalancer": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. The gateway load balancer settings." + } + }, + "loadBalancerInboundNatRules": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.inboundNatRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The load balancer inbound NAT rules." + } + }, + "privateIPAddressVersion": { + "type": "string", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "nullable": true, + "metadata": { + "description": "Optional. The private IP address version." + } + }, + "virtualNetworkTaps": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.virtualNetworkTapType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The virtual network taps." + } + }, + "pipConfiguration": { + "$ref": "#/definitions/_3.publicIPConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. The public IP address configuration." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the IP configuration." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the public IP address." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for the module." + } + } + }, + "metadata": { + "description": "The type for the IP configuration.", + "__bicep_imported_from!": { + "sourceTemplate": "modules/nic-configuration.bicep" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "networkInterfaceIPConfigurationOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the IP configuration." + } + }, + "privateIP": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The private IP address." + } + }, + "publicIP": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The public IP address." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "subResourceType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the sub resource." + } + } + }, + "metadata": { + "description": "The type for the sub resource.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine to be created. You should use a unique prefix to reduce name collisions in Active Directory." + } + }, + "computerName": { + "type": "string", + "defaultValue": "[parameters('name')]", + "metadata": { + "description": "Optional. Can be used if the computer name needs to be different from the Azure VM resource name. If not used, the resource name will be used as computer name." + } + }, + "vmSize": { + "type": "string", + "metadata": { + "description": "Required. Specifies the size for the VMs." + } + }, + "encryptionAtHost": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can be used by user in the request to enable or disable the Host Encryption for the virtual machine. This will enable the encryption for all the disks including Resource/Temp disk at host itself. For security reasons, it is recommended to set encryptionAtHost to True. Restrictions: Cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs." + } + }, + "securityType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "ConfidentialVM", + "TrustedLaunch" + ], + "metadata": { + "description": "Optional. Specifies the SecurityType of the virtual machine. It has to be set to any specified value to enable UefiSettings. The default behavior is: UefiSettings will not be enabled unless this property is set." + } + }, + "secureBootEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether secure boot should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings." + } + }, + "vTpmEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether vTPM should be enabled on the virtual machine. This parameter is part of the UefiSettings. SecurityType should be set to TrustedLaunch to enable UefiSettings." + } + }, + "imageReference": { + "$ref": "#/definitions/imageReferenceType", + "metadata": { + "description": "Required. OS image reference. In case of marketplace images, it's the combination of the publisher, offer, sku, version attributes. In case of custom images it's the resource ID of the custom image." + } + }, + "plan": { + "$ref": "#/definitions/planType", + "nullable": true, + "metadata": { + "description": "Optional. Specifies information about the marketplace image used to create the virtual machine. This element is only used for marketplace images. Before you can use a marketplace image from an API, you must enable the image for programmatic use." + } + }, + "osDisk": { + "$ref": "#/definitions/osDiskType", + "metadata": { + "description": "Required. Specifies the OS disk. For security reasons, it is recommended to specify DiskEncryptionSet into the osDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs." + } + }, + "dataDisks": { + "type": "array", + "items": { + "$ref": "#/definitions/dataDiskType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies the data disks. For security reasons, it is recommended to specify DiskEncryptionSet into the dataDisk object. Restrictions: DiskEncryptionSet cannot be enabled if Azure Disk Encryption (guest-VM encryption using bitlocker/DM-Crypt) is enabled on your VMs." + } + }, + "ultraSSDEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM or VMSS. Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine or virtual machine scale set only if this property is enabled." + } + }, + "hibernationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag that enables or disables hibernation capability on the VM." + } + }, + "adminUsername": { + "type": "securestring", + "metadata": { + "description": "Required. Administrator username." + } + }, + "adminPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. When specifying a Windows Virtual Machine, this value should be passed." + } + }, + "userData": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. UserData for the VM, which must be base-64 encoded. Customer should not pass any secrets in here." + } + }, + "customData": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Custom data associated to the VM, this value will be automatically converted into base64 to account for the expected VM format." + } + }, + "certificatesToBeInstalled": { + "type": "array", + "items": { + "$ref": "#/definitions/vaultSecretGroupType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies set of certificates that should be installed onto the virtual machine." + } + }, + "priority": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Regular", + "Low", + "Spot" + ], + "metadata": { + "description": "Optional. Specifies the priority for the virtual machine." + } + }, + "evictionPolicy": { + "type": "string", + "defaultValue": "Deallocate", + "allowedValues": [ + "Deallocate", + "Delete" + ], + "metadata": { + "description": "Optional. Specifies the eviction policy for the low priority virtual machine." + } + }, + "maxPriceForLowPriorityVm": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specifies the maximum price you are willing to pay for a low priority VM/VMSS. This price is in US Dollars." + } + }, + "dedicatedHostId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specifies resource ID about the dedicated host that the virtual machine resides in." + } + }, + "licenseType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "RHEL_BYOS", + "SLES_BYOS", + "Windows_Client", + "Windows_Server", + "" + ], + "metadata": { + "description": "Optional. Specifies that the image or disk that is being used was licensed on-premises." + } + }, + "publicKeys": { + "type": "array", + "items": { + "$ref": "#/definitions/publicKeyType" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The list of SSH public keys used to authenticate with linux based VMs." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource. The system-assigned managed identity will automatically be enabled if extensionAadJoinConfig.enabled = \"True\"." + } + }, + "bootDiagnostics": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether boot diagnostics should be enabled on the Virtual Machine. Boot diagnostics will be enabled with a managed storage account if no bootDiagnosticsStorageAccountName value is provided. If bootDiagnostics and bootDiagnosticsStorageAccountName values are not provided, boot diagnostics will be disabled." + } + }, + "bootDiagnosticStorageAccountName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Custom storage account used to store boot diagnostic information. Boot diagnostics will be enabled with a custom storage account if a value is provided." + } + }, + "bootDiagnosticStorageAccountUri": { + "type": "string", + "defaultValue": "[format('.blob.{0}/', environment().suffixes.storage)]", + "metadata": { + "description": "Optional. Storage account boot diagnostic base URI." + } + }, + "proximityPlacementGroupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of a proximity placement group." + } + }, + "virtualMachineScaleSetResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of a virtual machine scale set, where the VM should be added." + } + }, + "availabilitySetResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of an availability set. Cannot be used in combination with availability zone nor scale set." + } + }, + "galleryApplications": { + "type": "array", + "items": { + "$ref": "#/definitions/vmGalleryApplicationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies the gallery applications that should be made available to the VM/VMSS." + } + }, + "zone": { + "type": "int", + "allowedValues": [ + 0, + 1, + 2, + 3 + ], + "metadata": { + "description": "Required. If set to 1, 2 or 3, the availability zone for all VMs is hardcoded to that value. If zero, then availability zones is not used. Cannot be used in combination with availability set nor scale set." + } + }, + "nicConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/nicConfigurationType" + }, + "metadata": { + "description": "Required. Configures NICs and PIPs." + } + }, + "backupVaultName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Recovery service vault name to add VMs to backup." + } + }, + "backupVaultResourceGroup": { + "type": "string", + "defaultValue": "[resourceGroup().name]", + "metadata": { + "description": "Optional. Resource group of the backup recovery service vault. If not provided the current resource group name is considered by default." + } + }, + "backupPolicyName": { + "type": "string", + "defaultValue": "DefaultPolicy", + "metadata": { + "description": "Optional. Backup policy the VMs should be using for backup. If not provided, it will use the DefaultPolicy from the backup recovery service vault." + } + }, + "autoShutdownConfig": { + "$ref": "#/definitions/autoShutDownConfigType", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for auto-shutdown." + } + }, + "maintenanceConfigurationResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource Id of a maintenance configuration for this VM." + } + }, + "allowExtensionOperations": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether extension operations should be allowed on the virtual machine. This may only be set to False when no extensions are present on the virtual machine." + } + }, + "extensionDomainJoinPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Required if name is specified. Password of the user specified in user parameter." + } + }, + "extensionDomainJoinConfig": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. The configuration for the [Domain Join] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionAadJoinConfig": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [AAD Join] extension. Must at least contain the [\"enabled\": true] property to be executed. To enroll in Intune, add the setting mdmId: \"0000000a-0000-0000-c000-000000000000\"." + } + }, + "extensionAntiMalwareConfig": { + "type": "object", + "defaultValue": "[if(equals(parameters('osType'), 'Windows'), createObject('enabled', true()), createObject('enabled', false()))]", + "metadata": { + "description": "Optional. The configuration for the [Anti Malware] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionMonitoringAgentConfig": { + "type": "object", + "defaultValue": { + "enabled": false, + "dataCollectionRuleAssociations": [] + }, + "metadata": { + "description": "Optional. The configuration for the [Monitoring Agent] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionDependencyAgentConfig": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [Dependency Agent] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionNetworkWatcherAgentConfig": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [Network Watcher Agent] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionAzureDiskEncryptionConfig": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [Azure Disk Encryption] extension. Must at least contain the [\"enabled\": true] property to be executed. Restrictions: Cannot be enabled on disks that have encryption at host enabled. Managed disks encrypted using Azure Disk Encryption cannot be encrypted using customer-managed keys." + } + }, + "extensionDSCConfig": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [Desired State Configuration] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionCustomScriptConfig": { + "type": "object", + "defaultValue": { + "enabled": false, + "fileData": [] + }, + "metadata": { + "description": "Optional. The configuration for the [Custom Script] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionNvidiaGpuDriverWindows": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [Nvidia Gpu Driver Windows] extension. Must at least contain the [\"enabled\": true] property to be executed." + } + }, + "extensionHostPoolRegistration": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [Host Pool Registration] extension. Must at least contain the [\"enabled\": true] property to be executed. Needs a managed identy." + } + }, + "extensionGuestConfigurationExtension": { + "type": "object", + "defaultValue": { + "enabled": false + }, + "metadata": { + "description": "Optional. The configuration for the [Guest Configuration] extension. Must at least contain the [\"enabled\": true] property to be executed. Needs a managed identy." + } + }, + "guestConfiguration": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The guest configuration for the virtual machine. Needs the Guest Configuration extension to be enabled." + } + }, + "extensionCustomScriptProtectedSetting": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. An object that contains the extension specific protected settings." + } + }, + "extensionGuestConfigurationExtensionProtectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. An object that contains the extension specific protected settings." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('u')]", + "metadata": { + "description": "Generated. Do not provide a value! This date value is used to generate a registration token." + } + }, + "sasTokenValidityLength": { + "type": "string", + "defaultValue": "PT8H", + "metadata": { + "description": "Optional. SAS token validity length to use to download files from storage accounts. Usage: 'PT8H' - valid for 8 hours; 'P5D' - valid for 5 days; 'P1Y' - valid for 1 year. When not provided, the SAS token will be valid for 8 hours." + } + }, + "osType": { + "type": "string", + "allowedValues": [ + "Windows", + "Linux" + ], + "metadata": { + "description": "Required. The chosen OS type." + } + }, + "disablePasswordAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether password authentication should be disabled." + } + }, + "provisionVMAgent": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether virtual machine agent should be provisioned on the virtual machine. When this property is not specified in the request body, default behavior is to set it to true. This will ensure that VM Agent is installed on the VM so that extensions can be added to the VM later." + } + }, + "enableAutomaticUpdates": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether Automatic Updates is enabled for the Windows virtual machine. Default value is true. When patchMode is set to Manual, this parameter must be set to false. For virtual machine scale sets, this property can be updated and updates will take effect on OS reprovisioning." + } + }, + "patchMode": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "AutomaticByPlatform", + "AutomaticByOS", + "Manual", + "ImageDefault", + "" + ], + "metadata": { + "description": "Optional. VM guest patching orchestration mode. 'AutomaticByOS' & 'Manual' are for Windows only, 'ImageDefault' for Linux only. Refer to 'https://learn.microsoft.com/en-us/azure/virtual-machines/automatic-vm-guest-patching'." + } + }, + "bypassPlatformSafetyChecksOnUserSchedule": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enables customer to schedule patching without accidental upgrades." + } + }, + "rebootSetting": { + "type": "string", + "defaultValue": "IfRequired", + "allowedValues": [ + "Always", + "IfRequired", + "Never", + "Unknown" + ], + "metadata": { + "description": "Optional. Specifies the reboot setting for all AutomaticByPlatform patch installation operations." + } + }, + "patchAssessmentMode": { + "type": "string", + "defaultValue": "ImageDefault", + "allowedValues": [ + "AutomaticByPlatform", + "ImageDefault" + ], + "metadata": { + "description": "Optional. VM guest patching assessment mode. Set it to 'AutomaticByPlatform' to enable automatically check for updates every 24 hours." + } + }, + "enableHotpatching": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables customers to patch their Azure VMs without requiring a reboot. For enableHotpatching, the 'provisionVMAgent' must be set to true and 'patchMode' must be set to 'AutomaticByPlatform'." + } + }, + "timeZone": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Specifies the time zone of the virtual machine. e.g. 'Pacific Standard Time'. Possible values can be `TimeZoneInfo.id` value from time zones returned by `TimeZoneInfo.GetSystemTimeZones`." + } + }, + "additionalUnattendContent": { + "type": "array", + "items": { + "$ref": "#/definitions/additionalUnattendContentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies additional XML formatted information that can be included in the Unattend.xml file, which is used by Windows Setup. Contents are defined by setting name, component name, and the pass in which the content is applied." + } + }, + "winRMListeners": { + "type": "array", + "items": { + "$ref": "#/definitions/winRMListenerType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies the Windows Remote Management listeners. This enables remote Windows PowerShell." + } + }, + "configurationProfile": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The configuration profile of automanage. Either '/providers/Microsoft.Automanage/bestPractices/AzureBestPracticesProduction', 'providers/Microsoft.Automanage/bestPractices/AzureBestPracticesDevTest' or the resource Id of custom profile." + } + } + }, + "variables": { + "copy": [ + { + "name": "publicKeysFormatted", + "count": "[length(parameters('publicKeys'))]", + "input": { + "path": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].path]", + "keyData": "[parameters('publicKeys')[copyIndex('publicKeysFormatted')].keyData]" + } + }, + { + "name": "additionalUnattendContentFormatted", + "count": "[length(coalesce(parameters('additionalUnattendContent'), createArray()))]", + "input": { + "settingName": "[coalesce(parameters('additionalUnattendContent'), createArray())[copyIndex('additionalUnattendContentFormatted')].settingName]", + "content": "[coalesce(parameters('additionalUnattendContent'), createArray())[copyIndex('additionalUnattendContentFormatted')].content]", + "componentName": "Microsoft-Windows-Shell-Setup", + "passName": "OobeSystem" + } + }, + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "linuxConfiguration": { + "disablePasswordAuthentication": "[parameters('disablePasswordAuthentication')]", + "ssh": { + "publicKeys": "[variables('publicKeysFormatted')]" + }, + "provisionVMAgent": "[parameters('provisionVMAgent')]", + "patchSettings": "[if(and(parameters('provisionVMAgent'), or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('ImageDefault')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'automaticByPlatformSettings', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting')), null())), null())]" + }, + "windowsConfiguration": { + "provisionVMAgent": "[parameters('provisionVMAgent')]", + "enableAutomaticUpdates": "[parameters('enableAutomaticUpdates')]", + "patchSettings": "[if(and(parameters('provisionVMAgent'), or(or(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), equals(toLower(parameters('patchMode')), toLower('AutomaticByOS'))), equals(toLower(parameters('patchMode')), toLower('Manual')))), createObject('patchMode', parameters('patchMode'), 'assessmentMode', parameters('patchAssessmentMode'), 'enableHotpatching', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), parameters('enableHotpatching'), false()), 'automaticByPlatformSettings', if(equals(toLower(parameters('patchMode')), toLower('AutomaticByPlatform')), createObject('bypassPlatformSafetyChecksOnUserSchedule', parameters('bypassPlatformSafetyChecksOnUserSchedule'), 'rebootSetting', parameters('rebootSetting')), null())), null())]", + "timeZone": "[if(empty(parameters('timeZone')), null(), parameters('timeZone'))]", + "additionalUnattendContent": "[if(empty(parameters('additionalUnattendContent')), null(), variables('additionalUnattendContentFormatted'))]", + "winRM": "[if(not(empty(parameters('winRMListeners'))), createObject('listeners', parameters('winRMListeners')), null())]" + }, + "accountSasProperties": { + "signedServices": "b", + "signedPermission": "r", + "signedExpiry": "[dateTimeAdd(parameters('baseTime'), parameters('sasTokenValidityLength'))]", + "signedResourceTypes": "o", + "signedProtocol": "https" + }, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(if(parameters('extensionAadJoinConfig').enabled, true(), coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false())), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Data Operator for Managed Disks": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '959f8984-c045-4866-89c7-12bf9737be2e')]", + "Desktop Virtualization Power On Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '489581de-a3bd-480d-9518-53dea7416b33')]", + "Desktop Virtualization Power On Off Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "Desktop Virtualization Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "DevTest Labs User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76283e04-6283-4c54-8f91-bcf1374a3c64')]", + "Disk Backup Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3e5e47e6-65f7-47ef-90b5-e5dd4d455f24')]", + "Disk Pool Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '60fc6e62-5479-42d4-8bf4-67625fcc2840')]", + "Disk Restore Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b50d9833-a0cb-478e-945f-707fcc997c13')]", + "Disk Snapshot Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7efff54f-a5b4-42b5-a1c5-5411624893ce')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Virtual Machine Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", + "Virtual Machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", + "Virtual Machine User Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fb879df8-f326-4884-b1cf-06f3ad86be52')]", + "VM Scanner Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd24ecba3-c1f4-40fa-a7bb-4588a071e8fd')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.compute-virtualmachine.{0}.{1}', replace('0.15.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "managedDataDisks": { + "copy": { + "name": "managedDataDisks", + "count": "[length(coalesce(parameters('dataDisks'), createArray()))]" + }, + "condition": "[empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()].managedDisk, 'id'))]", + "type": "Microsoft.Compute/disks", + "apiVersion": "2024-03-02", + "name": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex(), 1), 2, '0')))]", + "location": "[parameters('location')]", + "sku": { + "name": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()].managedDisk, 'storageAccountType')]" + }, + "properties": { + "diskSizeGB": "[coalesce(parameters('dataDisks'), createArray())[copyIndex()].diskSizeGB]", + "creationData": { + "createOption": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'createoption'), 'Empty')]" + }, + "diskIOPSReadWrite": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'diskIOPSReadWrite')]", + "diskMBpsReadWrite": "[tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'diskMBpsReadWrite')]" + }, + "zones": "[if(and(not(equals(parameters('zone'), 0)), not(contains(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()].managedDisk, 'storageAccountType'), 'ZRS'))), array(string(parameters('zone'))), null())]", + "tags": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "vm": { + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2024-07-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "zones": "[if(not(equals(parameters('zone'), 0)), array(string(parameters('zone'))), null())]", + "plan": "[parameters('plan')]", + "properties": { + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "securityProfile": { + "encryptionAtHost": "[if(parameters('encryptionAtHost'), parameters('encryptionAtHost'), null())]", + "securityType": "[parameters('securityType')]", + "uefiSettings": "[if(equals(parameters('securityType'), 'TrustedLaunch'), createObject('secureBootEnabled', parameters('secureBootEnabled'), 'vTpmEnabled', parameters('vTpmEnabled')), null())]" + }, + "storageProfile": { + "copy": [ + { + "name": "dataDisks", + "count": "[length(coalesce(parameters('dataDisks'), createArray()))]", + "input": { + "lun": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'lun'), copyIndex('dataDisks'))]", + "name": "[if(not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'))), last(split(coalesce(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.id, ''), '/')), coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0'))))]", + "createOption": "[if(or(not(equals(resourceId('Microsoft.Compute/disks', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))), null())), not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id')))), 'Attach', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'createoption'), 'Empty'))]", + "deleteOption": "[if(not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'))), 'Detach', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'deleteOption'), 'Delete'))]", + "caching": "[if(not(empty(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'))), 'None', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'caching'), 'ReadOnly'))]", + "managedDisk": { + "id": "[coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'id'), resourceId('Microsoft.Compute/disks', coalesce(tryGet(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')], 'name'), format('{0}-disk-data-{1}', parameters('name'), padLeft(add(copyIndex('dataDisks'), 1), 2, '0')))))]", + "diskEncryptionSet": "[if(contains(coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk, 'diskEncryptionSet'), createObject('id', coalesce(parameters('dataDisks'), createArray())[copyIndex('dataDisks')].managedDisk.diskEncryptionSet.id), null())]" + } + } + } + ], + "imageReference": "[parameters('imageReference')]", + "osDisk": { + "name": "[coalesce(tryGet(parameters('osDisk'), 'name'), format('{0}-disk-os-01', parameters('name')))]", + "createOption": "[coalesce(tryGet(parameters('osDisk'), 'createOption'), 'FromImage')]", + "deleteOption": "[coalesce(tryGet(parameters('osDisk'), 'deleteOption'), 'Delete')]", + "diffDiskSettings": "[if(empty(coalesce(tryGet(parameters('osDisk'), 'diffDiskSettings'), createObject())), null(), createObject('option', 'Local', 'placement', parameters('osDisk').diffDiskSettings.placement))]", + "diskSizeGB": "[tryGet(parameters('osDisk'), 'diskSizeGB')]", + "caching": "[coalesce(tryGet(parameters('osDisk'), 'caching'), 'ReadOnly')]", + "managedDisk": { + "storageAccountType": "[tryGet(parameters('osDisk').managedDisk, 'storageAccountType')]", + "diskEncryptionSet": { + "id": "[tryGet(parameters('osDisk').managedDisk, 'diskEncryptionSetResourceId')]" + } + } + } + }, + "additionalCapabilities": { + "ultraSSDEnabled": "[parameters('ultraSSDEnabled')]", + "hibernationEnabled": "[parameters('hibernationEnabled')]" + }, + "osProfile": { + "computerName": "[parameters('computerName')]", + "adminUsername": "[parameters('adminUsername')]", + "adminPassword": "[parameters('adminPassword')]", + "customData": "[if(not(empty(parameters('customData'))), base64(parameters('customData')), null())]", + "windowsConfiguration": "[if(equals(parameters('osType'), 'Windows'), variables('windowsConfiguration'), null())]", + "linuxConfiguration": "[if(equals(parameters('osType'), 'Linux'), variables('linuxConfiguration'), null())]", + "secrets": "[parameters('certificatesToBeInstalled')]", + "allowExtensionOperations": "[parameters('allowExtensionOperations')]" + }, + "networkProfile": { + "copy": [ + { + "name": "networkInterfaces", + "count": "[length(parameters('nicConfigurations'))]", + "input": { + "properties": { + "deleteOption": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'deleteOption'), 'Delete')]", + "primary": "[if(equals(copyIndex('networkInterfaces'), 0), true(), false())]" + }, + "id": "[resourceId('Microsoft.Network/networkInterfaces', coalesce(tryGet(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'name'), format('{0}{1}', parameters('name'), tryGet(parameters('nicConfigurations')[copyIndex('networkInterfaces')], 'nicSuffix'))))]" + } + } + ] + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": "[if(not(empty(parameters('bootDiagnosticStorageAccountName'))), true(), parameters('bootDiagnostics'))]", + "storageUri": "[if(not(empty(parameters('bootDiagnosticStorageAccountName'))), format('https://{0}{1}', parameters('bootDiagnosticStorageAccountName'), parameters('bootDiagnosticStorageAccountUri')), null())]" + } + }, + "applicationProfile": "[if(not(empty(parameters('galleryApplications'))), createObject('galleryApplications', parameters('galleryApplications')), null())]", + "availabilitySet": "[if(not(empty(parameters('availabilitySetResourceId'))), createObject('id', parameters('availabilitySetResourceId')), null())]", + "proximityPlacementGroup": "[if(not(empty(parameters('proximityPlacementGroupResourceId'))), createObject('id', parameters('proximityPlacementGroupResourceId')), null())]", + "virtualMachineScaleSet": "[if(not(empty(parameters('virtualMachineScaleSetResourceId'))), createObject('id', parameters('virtualMachineScaleSetResourceId')), null())]", + "priority": "[parameters('priority')]", + "evictionPolicy": "[if(and(not(empty(parameters('priority'))), not(equals(parameters('priority'), 'Regular'))), parameters('evictionPolicy'), null())]", + "billingProfile": "[if(and(not(empty(parameters('priority'))), not(empty(parameters('maxPriceForLowPriorityVm')))), createObject('maxPrice', json(parameters('maxPriceForLowPriorityVm'))), null())]", + "host": "[if(not(empty(parameters('dedicatedHostId'))), createObject('id', parameters('dedicatedHostId')), null())]", + "licenseType": "[if(not(empty(parameters('licenseType'))), parameters('licenseType'), null())]", + "userData": "[if(not(empty(parameters('userData'))), base64(parameters('userData')), null())]" + }, + "dependsOn": [ + "managedDataDisks", + "vm_nic" + ] + }, + "vm_configurationAssignment": { + "condition": "[not(empty(parameters('maintenanceConfigurationResourceId')))]", + "type": "Microsoft.Maintenance/configurationAssignments", + "apiVersion": "2023-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[format('{0}assignment', parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "maintenanceConfigurationId": "[parameters('maintenanceConfigurationResourceId')]", + "resourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_configurationProfileAssignment": { + "condition": "[not(empty(parameters('configurationProfile')))]", + "type": "Microsoft.Automanage/configurationProfileAssignments", + "apiVersion": "2022-05-04", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "default", + "properties": { + "configurationProfile": "[parameters('configurationProfile')]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_autoShutdownConfiguration": { + "condition": "[not(empty(parameters('autoShutdownConfig')))]", + "type": "Microsoft.DevTestLab/schedules", + "apiVersion": "2018-09-15", + "name": "[format('shutdown-computevm-{0}', parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "status": "[coalesce(tryGet(parameters('autoShutdownConfig'), 'status'), 'Disabled')]", + "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]", + "taskType": "ComputeVmShutdownTask", + "dailyRecurrence": { + "time": "[coalesce(tryGet(parameters('autoShutdownConfig'), 'dailyRecurrenceTime'), '19:00')]" + }, + "timeZoneId": "[coalesce(tryGet(parameters('autoShutdownConfig'), 'timeZone'), 'UTC')]", + "notificationSettings": "[if(contains(parameters('autoShutdownConfig'), 'notificationSettings'), createObject('status', coalesce(tryGet(parameters('autoShutdownConfig'), 'status'), 'Disabled'), 'emailRecipient', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'emailRecipient'), ''), 'notificationLocale', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'notificationLocale'), 'en'), 'webhookUrl', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'webhookUrl'), ''), 'timeInMinutes', coalesce(tryGet(tryGet(parameters('autoShutdownConfig'), 'notificationSettings'), 'timeInMinutes'), 30)), null())]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_dataCollectionRuleAssociations": { + "copy": { + "name": "vm_dataCollectionRuleAssociations", + "count": "[length(parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations)]" + }, + "condition": "[parameters('extensionMonitoringAgentConfig').enabled]", + "type": "Microsoft.Insights/dataCollectionRuleAssociations", + "apiVersion": "2023-03-11", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations[copyIndex()].name]", + "properties": { + "dataCollectionRuleId": "[parameters('extensionMonitoringAgentConfig').dataCollectionRuleAssociations[copyIndex()].dataCollectionRuleResourceId]" + }, + "dependsOn": [ + "vm", + "vm_azureMonitorAgentExtension" + ] + }, + "AzureWindowsBaseline": { + "condition": "[not(empty(parameters('guestConfiguration')))]", + "type": "Microsoft.GuestConfiguration/guestConfigurationAssignments", + "apiVersion": "2020-06-25", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('guestConfiguration'), 'name'), 'AzureWindowsBaseline')]", + "location": "[parameters('location')]", + "properties": { + "guestConfiguration": "[parameters('guestConfiguration')]" + }, + "dependsOn": [ + "vm", + "vm_azureGuestConfigurationExtension" + ] + }, + "vm_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_roleAssignments": { + "copy": { + "name": "vm_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Compute/virtualMachines', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "vm" + ] + }, + "vm_nic": { + "copy": { + "name": "vm_nic", + "count": "[length(parameters('nicConfigurations'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-Nic-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "networkInterfaceName": { + "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'name'), format('{0}{1}', parameters('name'), tryGet(parameters('nicConfigurations')[copyIndex()], 'nicSuffix')))]" + }, + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableIPForwarding": { + "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'enableIPForwarding'), false())]" + }, + "enableAcceleratedNetworking": { + "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'enableAcceleratedNetworking'), true())]" + }, + "dnsServers": "[if(contains(parameters('nicConfigurations')[copyIndex()], 'dnsServers'), if(not(empty(tryGet(parameters('nicConfigurations')[copyIndex()], 'dnsServers'))), createObject('value', tryGet(parameters('nicConfigurations')[copyIndex()], 'dnsServers')), createObject('value', createArray())), createObject('value', createArray()))]", + "networkSecurityGroupResourceId": { + "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'networkSecurityGroupResourceId'), '')]" + }, + "ipConfigurations": { + "value": "[parameters('nicConfigurations')[copyIndex()].ipConfigurations]" + }, + "lock": { + "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'lock'), parameters('lock'))]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('nicConfigurations')[copyIndex()], 'tags'), parameters('tags'))]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('nicConfigurations')[copyIndex()], 'diagnosticSettings')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('nicConfigurations')[copyIndex()], 'roleAssignments')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "3333482934245501039" + } + }, + "definitions": { + "publicIPConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Public IP Address." + } + }, + "publicIPAddressResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the public IP address." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Diagnostic settings for the public IP address." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The idle timeout in minutes." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the public IP address." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "publicIPAddressVersion": { + "type": "string", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "nullable": true, + "metadata": { + "description": "Optional. The public IP address version." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "allowedValues": [ + "Dynamic", + "Static" + ], + "nullable": true, + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix." + } + }, + "publicIpNameSuffix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name suffix of the public IP address resource." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "skuName": { + "type": "string", + "allowedValues": [ + "Basic", + "Standard" + ], + "nullable": true, + "metadata": { + "description": "Optional. The SKU name of the public IP address." + } + }, + "skuTier": { + "type": "string", + "allowedValues": [ + "Global", + "Regional" + ], + "nullable": true, + "metadata": { + "description": "Optional. The SKU tier of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the public IP address." + } + }, + "zones": { + "type": "array", + "allowedValues": [ + 1, + 2, + 3 + ], + "nullable": true, + "metadata": { + "description": "Optional. The zones of the public IP address." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for the module." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the public IP address configuration." + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the IP configuration." + } + }, + "privateIPAllocationMethod": { + "type": "string", + "allowedValues": [ + "Dynamic", + "Static" + ], + "nullable": true, + "metadata": { + "description": "Optional. The private IP address allocation method." + } + }, + "privateIPAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The private IP address." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "loadBalancerBackendAddressPools": { + "type": "array", + "items": { + "$ref": "#/definitions/backendAddressPoolType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The load balancer backend address pools." + } + }, + "applicationSecurityGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/applicationSecurityGroupType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The application security groups." + } + }, + "applicationGatewayBackendAddressPools": { + "type": "array", + "items": { + "$ref": "#/definitions/applicationGatewayBackendAddressPoolsType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The application gateway backend address pools." + } + }, + "gatewayLoadBalancer": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. The gateway load balancer settings." + } + }, + "loadBalancerInboundNatRules": { + "type": "array", + "items": { + "$ref": "#/definitions/inboundNatRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The load balancer inbound NAT rules." + } + }, + "privateIPAddressVersion": { + "type": "string", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "nullable": true, + "metadata": { + "description": "Optional. The private IP address version." + } + }, + "virtualNetworkTaps": { + "type": "array", + "items": { + "$ref": "#/definitions/virtualNetworkTapType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The virtual network taps." + } + }, + "pipConfiguration": { + "$ref": "#/definitions/publicIPConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. The public IP address configuration." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the IP configuration." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the public IP address." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for the module." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the IP configuration." + } + }, + "applicationGatewayBackendAddressPoolsType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the backend address pool." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the backend address pool that is unique within an Application Gateway." + } + }, + "properties": { + "type": "object", + "properties": { + "backendAddresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. IP address of the backend address." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN of the backend address." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Backend addresses." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Properties of the application gateway backend address pool." + } + } + }, + "metadata": { + "description": "The type for the application gateway backend address pool.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "applicationSecurityGroupType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the application security group." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location of the application security group." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Properties of the application security group." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the application security group." + } + } + }, + "metadata": { + "description": "The type for the application security group.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "backendAddressPoolType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the backend address pool." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the backend address pool." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The properties of the backend address pool." + } + } + }, + "metadata": { + "description": "The type for a backend address pool.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "nullable": true, + "metadata": { + "description": "Optional. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0" + } + } + }, + "inboundNatRuleType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the inbound NAT rule." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the resource that is unique within the set of inbound NAT rules used by the load balancer. This name can be used to access the resource." + } + }, + "properties": { + "type": "object", + "properties": { + "backendAddressPool": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. A reference to backendAddressPool resource." + } + }, + "backendPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port used for the internal endpoint. Acceptable values range from 1 to 65535." + } + }, + "enableFloatingIP": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint." + } + }, + "enableTcpReset": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP." + } + }, + "frontendIPConfiguration": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. A reference to frontend IP addresses." + } + }, + "frontendPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. Acceptable values range from 1 to 65534." + } + }, + "frontendPortRangeStart": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534." + } + }, + "frontendPortRangeEnd": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "All", + "Tcp", + "Udp" + ], + "nullable": true, + "metadata": { + "description": "Optional. The reference to the transport protocol used by the load balancing rule." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Properties of the inbound NAT rule." + } + } + }, + "metadata": { + "description": "The type for the inbound NAT rule.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "ipTagType": { + "type": "object", + "properties": { + "ipTagType": { + "type": "string", + "metadata": { + "description": "Required. The IP tag type." + } + }, + "tag": { + "type": "string", + "metadata": { + "description": "Required. The IP tag." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/public-ip-address:0.8.0" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "networkInterfaceIPConfigurationOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the IP configuration." + } + }, + "privateIP": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The private IP address." + } + }, + "publicIP": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The public IP address." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "subResourceType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the sub resource." + } + } + }, + "metadata": { + "description": "The type for the sub resource.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + }, + "virtualNetworkTapType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the virtual network tap." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location of the virtual network tap." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Properties of the virtual network tap." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the virtual network tap." + } + } + }, + "metadata": { + "description": "The type for the virtual network tap.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/res/network/network-interface:0.5.1" + } + } + } + }, + "parameters": { + "networkInterfaceName": { + "type": "string" + }, + "virtualMachineName": { + "type": "string" + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableIPForwarding": { + "type": "bool", + "defaultValue": false + }, + "enableAcceleratedNetworking": { + "type": "bool", + "defaultValue": false + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [] + }, + "enableTelemetry": { + "type": "bool", + "metadata": { + "description": "Required. Enable telemetry via a Globally Unique Identifier (GUID)." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The network security group (NSG) to attach to the network interface." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "networkInterface_publicIPAddresses": { + "copy": { + "name": "networkInterface_publicIPAddresses", + "count": "[length(parameters('ipConfigurations'))]" + }, + "condition": "[and(not(empty(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'))), empty(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIPAddressResourceId')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-publicIP-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'name'), format('{0}{1}', parameters('virtualMachineName'), tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIpNameSuffix')))]" + }, + "diagnosticSettings": { + "value": "[coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'diagnosticSettings'), tryGet(parameters('ipConfigurations')[copyIndex()], 'diagnosticSettings'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "idleTimeoutInMinutes": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'idleTimeoutInMinutes')]" + }, + "ddosSettings": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'ddosSettings')]" + }, + "dnsSettings": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'dnsSettings')]" + }, + "publicIPAddressVersion": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIPAddressVersion')]" + }, + "publicIPAllocationMethod": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIPAllocationMethod')]" + }, + "publicIpPrefixResourceId": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'publicIpPrefixResourceId')]" + }, + "roleAssignments": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'roleAssignments')]" + }, + "skuName": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'skuName')]" + }, + "skuTier": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'skuTier')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'tags'), parameters('tags'))]" + }, + "zones": { + "value": "[tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'zones')]" + }, + "enableTelemetry": { + "value": "[coalesce(coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex()], 'pipConfiguration'), 'enableTelemetry'), tryGet(parameters('ipConfigurations')[copyIndex()], 'enableTelemetry')), parameters('enableTelemetry'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "5168739580767459761" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address." + }, + "definitions": { + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. The domain name label. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address. If a domain name label is specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "nullable": true, + "metadata": { + "description": "Optional. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Fully Qualified Domain Name of the A DNS record associated with the public IP. This is the concatenation of the domainNameLabel and the regionalized DNS zone." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The reverse FQDN. A user-visible, fully qualified domain name that resolves to this public IP address. If the reverseFqdn is specified, then a PTR DNS record is created pointing from the IP address in the in-addr.arpa domain to the reverse FQDN." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipTagType": { + "type": "object", + "properties": { + "ipTagType": { + "type": "string", + "metadata": { + "description": "Required. The IP tag type." + } + }, + "tag": { + "type": "string", + "metadata": { + "description": "Required. The IP tag." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.2.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the Public IP Prefix object. This is only needed if you want your Public IPs created in a PIP Prefix." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "ipTags": { + "type": "array", + "items": { + "$ref": "#/definitions/ipTagType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of tags associated with the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.8.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": "[parameters('ipTags')]" + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2024-05-01', 'full').location]" + } + } + } + } + }, + "networkInterface": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NetworkInterface', deployment().name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('networkInterfaceName')]" + }, + "ipConfigurations": { + "copy": [ + { + "name": "value", + "count": "[length(parameters('ipConfigurations'))]", + "input": "[createObject('name', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'name'), 'privateIPAllocationMethod', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAllocationMethod'), 'privateIPAddress', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddress'), 'publicIPAddressResourceId', if(not(empty(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'))), if(not(contains(coalesce(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), createObject()), 'publicIPAddressResourceId')), resourceId('Microsoft.Network/publicIPAddresses', coalesce(tryGet(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), 'name'), format('{0}{1}', parameters('virtualMachineName'), tryGet(tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration'), 'publicIpNameSuffix')))), tryGet(parameters('ipConfigurations')[copyIndex('value')], 'pipConfiguration', 'publicIPAddressResourceId')), null()), 'subnetResourceId', parameters('ipConfigurations')[copyIndex('value')].subnetResourceId, 'loadBalancerBackendAddressPools', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerBackendAddressPools'), 'applicationSecurityGroups', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'applicationSecurityGroups'), 'applicationGatewayBackendAddressPools', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'applicationGatewayBackendAddressPools'), 'gatewayLoadBalancer', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'gatewayLoadBalancer'), 'loadBalancerInboundNatRules', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'loadBalancerInboundNatRules'), 'privateIPAddressVersion', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'privateIPAddressVersion'), 'virtualNetworkTaps', tryGet(parameters('ipConfigurations')[copyIndex('value')], 'virtualNetworkTaps'))]" + } + ] + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "diagnosticSettings": { + "value": "[parameters('diagnosticSettings')]" + }, + "dnsServers": { + "value": "[parameters('dnsServers')]" + }, + "enableAcceleratedNetworking": { + "value": "[parameters('enableAcceleratedNetworking')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "enableIPForwarding": { + "value": "[parameters('enableIPForwarding')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "networkSecurityGroupResourceId": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('value', parameters('networkSecurityGroupResourceId')), createObject('value', ''))]", + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8196054567469390015" + }, + "name": "Network Interface", + "description": "This module deploys a Network Interface." + }, + "definitions": { + "networkInterfaceIPConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the IP configuration." + } + }, + "privateIPAllocationMethod": { + "type": "string", + "allowedValues": [ + "Dynamic", + "Static" + ], + "nullable": true, + "metadata": { + "description": "Optional. The private IP address allocation method." + } + }, + "privateIPAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The private IP address." + } + }, + "publicIPAddressResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the public IP address." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "loadBalancerBackendAddressPools": { + "type": "array", + "items": { + "$ref": "#/definitions/backendAddressPoolType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of load balancer backend address pools." + } + }, + "loadBalancerInboundNatRules": { + "type": "array", + "items": { + "$ref": "#/definitions/inboundNatRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of references of LoadBalancerInboundNatRules." + } + }, + "applicationSecurityGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/applicationSecurityGroupType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the IP configuration is included." + } + }, + "applicationGatewayBackendAddressPools": { + "type": "array", + "items": { + "$ref": "#/definitions/applicationGatewayBackendAddressPoolsType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The reference to Application Gateway Backend Address Pools." + } + }, + "gatewayLoadBalancer": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. The reference to gateway load balancer frontend IP." + } + }, + "privateIPAddressVersion": { + "type": "string", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "nullable": true, + "metadata": { + "description": "Optional. Whether the specific IP configuration is IPv4 or IPv6." + } + }, + "virtualNetworkTaps": { + "type": "array", + "items": { + "$ref": "#/definitions/virtualNetworkTapType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The reference to Virtual Network Taps." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The resource ID of the deployed resource." + } + }, + "backendAddressPoolType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the backend address pool." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the backend address pool." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The properties of the backend address pool." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a backend address pool." + } + }, + "applicationSecurityGroupType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the application security group." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location of the application security group." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Properties of the application security group." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the application security group." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the application security group." + } + }, + "applicationGatewayBackendAddressPoolsType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the backend address pool." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the backend address pool that is unique within an Application Gateway." + } + }, + "properties": { + "type": "object", + "properties": { + "backendAddresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. IP address of the backend address." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN of the backend address." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Backend addresses." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Properties of the application gateway backend address pool." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the application gateway backend address pool." + } + }, + "subResourceType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the sub resource." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the sub resource." + } + }, + "inboundNatRuleType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the inbound NAT rule." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the resource that is unique within the set of inbound NAT rules used by the load balancer. This name can be used to access the resource." + } + }, + "properties": { + "type": "object", + "properties": { + "backendAddressPool": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. A reference to backendAddressPool resource." + } + }, + "backendPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port used for the internal endpoint. Acceptable values range from 1 to 65535." + } + }, + "enableFloatingIP": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. This setting can't be changed after you create the endpoint." + } + }, + "enableTcpReset": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. This element is only used when the protocol is set to TCP." + } + }, + "frontendIPConfiguration": { + "$ref": "#/definitions/subResourceType", + "nullable": true, + "metadata": { + "description": "Optional. A reference to frontend IP addresses." + } + }, + "frontendPort": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port for the external endpoint. Port numbers for each rule must be unique within the Load Balancer. Acceptable values range from 1 to 65534." + } + }, + "frontendPortRangeStart": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port range start for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeEnd. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534." + } + }, + "frontendPortRangeEnd": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The port range end for the external endpoint. This property is used together with BackendAddressPool and FrontendPortRangeStart. Individual inbound NAT rule port mappings will be created for each backend address from BackendAddressPool. Acceptable values range from 1 to 65534." + } + }, + "protocol": { + "type": "string", + "allowedValues": [ + "All", + "Tcp", + "Udp" + ], + "nullable": true, + "metadata": { + "description": "Optional. The reference to the transport protocol used by the load balancing rule." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Properties of the inbound NAT rule." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the inbound NAT rule." + } + }, + "virtualNetworkTapType": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the virtual network tap." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location of the virtual network tap." + } + }, + "properties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Properties of the virtual network tap." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the virtual network tap." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the virtual network tap." + } + }, + "networkInterfaceIPConfigurationOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the IP configuration." + } + }, + "privateIP": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The private IP address." + } + }, + "publicIP": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The public IP address." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the network interface." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "enableIPForwarding": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether IP forwarding is enabled on this network interface." + } + }, + "enableAcceleratedNetworking": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If the network interface is accelerated networking enabled." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. List of DNS servers IP addresses. Use 'AzureProvidedDNS' to switch to azure provided DNS resolution. 'AzureProvidedDNS' value cannot be combined with other IPs, it must be the only value in dnsServers collection." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The network security group (NSG) to attach to the network interface." + } + }, + "auxiliaryMode": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "Floating", + "MaxConnections", + "None" + ], + "metadata": { + "description": "Optional. Auxiliary mode of Network Interface resource. Not all regions are enabled for Auxiliary Mode Nic." + } + }, + "auxiliarySku": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "A1", + "A2", + "A4", + "A8", + "None" + ], + "metadata": { + "description": "Optional. Auxiliary sku of Network Interface resource. Not all regions are enabled for Auxiliary Mode Nic." + } + }, + "disableTcpStateTracking": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether to disable tcp state tracking. Subscription must be registered for the Microsoft.Network/AllowDisableTcpStateTracking feature before this property can be set to true." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/networkInterfaceIPConfigurationType" + }, + "metadata": { + "description": "Required. A list of IPConfigurations of the network interface." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "publicIp": { + "copy": { + "name": "publicIp", + "count": "[length(parameters('ipConfigurations'))]" + }, + "condition": "[and(contains(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), not(equals(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), null())))]", + "existing": true, + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2024-05-01", + "resourceGroup": "[split(coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), ''), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), ''), '/'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-networkinterface.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "networkInterface": { + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "ipConfigurations", + "count": "[length(parameters('ipConfigurations'))]", + "input": { + "name": "[coalesce(tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'name'), format('ipconfig0{0}', add(copyIndex('ipConfigurations'), 1)))]", + "properties": { + "primary": "[if(equals(copyIndex('ipConfigurations'), 0), true(), false())]", + "privateIPAllocationMethod": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAllocationMethod')]", + "privateIPAddress": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddress')]", + "publicIPAddress": "[if(contains(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId'), if(not(equals(tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId'), null())), createObject('id', tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'publicIPAddressResourceId')), null()), null())]", + "subnet": { + "id": "[parameters('ipConfigurations')[copyIndex('ipConfigurations')].subnetResourceId]" + }, + "loadBalancerBackendAddressPools": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerBackendAddressPools')]", + "applicationSecurityGroups": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationSecurityGroups')]", + "applicationGatewayBackendAddressPools": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'applicationGatewayBackendAddressPools')]", + "gatewayLoadBalancer": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'gatewayLoadBalancer')]", + "loadBalancerInboundNatRules": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'loadBalancerInboundNatRules')]", + "privateIPAddressVersion": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'privateIPAddressVersion')]", + "virtualNetworkTaps": "[tryGet(parameters('ipConfigurations')[copyIndex('ipConfigurations')], 'virtualNetworkTaps')]" + } + } + } + ], + "auxiliaryMode": "[parameters('auxiliaryMode')]", + "auxiliarySku": "[parameters('auxiliarySku')]", + "disableTcpStateTracking": "[parameters('disableTcpStateTracking')]", + "dnsSettings": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', parameters('dnsServers')), null())]", + "enableAcceleratedNetworking": "[parameters('enableAcceleratedNetworking')]", + "enableIPForwarding": "[parameters('enableIPForwarding')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]" + } + }, + "networkInterface_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "networkInterface" + ] + }, + "networkInterface_diagnosticSettings": { + "copy": { + "name": "networkInterface_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkInterface" + ] + }, + "networkInterface_roleAssignments": { + "copy": { + "name": "networkInterface_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkInterfaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkInterfaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkInterface" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed resource." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed resource." + }, + "value": "[resourceId('Microsoft.Network/networkInterfaces', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed resource." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkInterface', '2024-05-01', 'full').location]" + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/networkInterfaceIPConfigurationOutputType" + }, + "metadata": { + "description": "The list of IP configurations of the network interface." + }, + "copy": { + "count": "[length(parameters('ipConfigurations'))]", + "input": { + "name": "[reference('networkInterface').ipConfigurations[copyIndex()].name]", + "privateIP": "[coalesce(tryGet(reference('networkInterface').ipConfigurations[copyIndex()].properties, 'privateIPAddress'), '')]", + "publicIP": "[if(and(contains(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), not(equals(tryGet(parameters('ipConfigurations')[copyIndex()], 'publicIPAddressResourceId'), null()))), coalesce(reference(format('publicIp[{0}]', copyIndex())).ipAddress, ''), '')]" + } + } + } + } + } + }, + "dependsOn": [ + "networkInterface_publicIPAddresses" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the network interface." + }, + "value": "[reference('networkInterface').outputs.name.value]" + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/networkInterfaceIPConfigurationOutputType" + }, + "metadata": { + "description": "The list of IP configurations of the network interface." + }, + "value": "[reference('networkInterface').outputs.ipConfigurations.value]" + } + } + } + } + }, + "vm_domainJoinExtension": { + "condition": "[and(contains(parameters('extensionDomainJoinConfig'), 'enabled'), parameters('extensionDomainJoinConfig').enabled)]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-DomainJoin', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'name'), 'DomainJoin')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Compute" + }, + "type": { + "value": "JsonADDomainExtension" + }, + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'typeHandlerVersion'), '1.3')]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "settings": { + "value": "[parameters('extensionDomainJoinConfig').settings]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionDomainJoinConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": { + "value": { + "Password": "[parameters('extensionDomainJoinPassword')]" + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm" + ] + }, + "vm_aadJoinExtension": { + "condition": "[parameters('extensionAadJoinConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-AADLogin', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'name'), 'AADLogin')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.ActiveDirectory" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AADLoginForWindows'), createObject('value', 'AADSSHLoginforLinux'))]", + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '2.0', '1.0'))]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "settings": { + "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'settings'), createObject())]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionAadJoinConfig'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_domainJoinExtension" + ] + }, + "vm_microsoftAntiMalwareExtension": { + "condition": "[parameters('extensionAntiMalwareConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-MicrosoftAntiMalware', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'name'), 'MicrosoftAntiMalware')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": { + "value": "IaaSAntimalware" + }, + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'typeHandlerVersion'), '1.3')]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "settings": { + "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'settings'), createObject('AntimalwareEnabled', 'true', 'Exclusions', createObject(), 'RealtimeProtectionEnabled', 'true', 'ScheduledScanSettings', createObject('day', '7', 'isEnabled', 'true', 'scanType', 'Quick', 'time', '120')))]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionAntiMalwareConfig'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_aadJoinExtension" + ] + }, + "vm_azureMonitorAgentExtension": { + "condition": "[parameters('extensionMonitoringAgentConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-AzureMonitorAgent', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'name'), 'AzureMonitorAgent')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Monitor" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AzureMonitorWindowsAgent'), createObject('value', 'AzureMonitorLinuxAgent'))]", + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '1.22', '1.29'))]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionMonitoringAgentConfig'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_microsoftAntiMalwareExtension" + ] + }, + "vm_dependencyAgentExtension": { + "condition": "[parameters('extensionDependencyAgentConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-DependencyAgent', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'name'), 'DependencyAgent')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Monitoring.DependencyAgent" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'DependencyAgentWindows'), createObject('value', 'DependencyAgentLinux'))]", + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'typeHandlerVersion'), '9.10')]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'enableAutomaticUpgrade'), true())]" + }, + "settings": { + "value": { + "enableAMA": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'enableAMA'), true())]" + } + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionDependencyAgentConfig'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_azureMonitorAgentExtension" + ] + }, + "vm_networkWatcherAgentExtension": { + "condition": "[parameters('extensionNetworkWatcherAgentConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-NetworkWatcherAgent', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'name'), 'NetworkWatcherAgent')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.NetworkWatcher" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'NetworkWatcherAgentWindows'), createObject('value', 'NetworkWatcherAgentLinux'))]", + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'typeHandlerVersion'), '1.4')]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionNetworkWatcherAgentConfig'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_dependencyAgentExtension" + ] + }, + "vm_desiredStateConfigurationExtension": { + "condition": "[parameters('extensionDSCConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-DesiredStateConfiguration', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'name'), 'DesiredStateConfiguration')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Powershell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'typeHandlerVersion'), '2.77')]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "settings": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'settings'), createObject())]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": { + "value": "[coalesce(tryGet(parameters('extensionDSCConfig'), 'protectedSettings'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_networkWatcherAgentExtension" + ] + }, + "vm_customScriptExtension": { + "condition": "[parameters('extensionCustomScriptConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-CustomScriptExtension', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'name'), 'CustomScriptExtension')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'Microsoft.Compute'), createObject('value', 'Microsoft.Azure.Extensions'))]", + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'CustomScriptExtension'), createObject('value', 'CustomScript'))]", + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '1.10', '2.1'))]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "settings": { + "value": { + "copy": [ + { + "name": "fileUris", + "count": "[length(parameters('extensionCustomScriptConfig').fileData)]", + "input": "[if(contains(parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')], 'storageAccountId'), format('{0}?{1}', parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')].uri, listAccountSas(parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')].storageAccountId, '2019-04-01', variables('accountSasProperties')).accountSasToken), parameters('extensionCustomScriptConfig').fileData[copyIndex('fileUris')].uri)]" + } + ] + } + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionCustomScriptConfig'), 'tags'), parameters('tags'))]" + }, + "protectedSettings": { + "value": "[parameters('extensionCustomScriptProtectedSetting')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_desiredStateConfigurationExtension" + ] + }, + "vm_azureDiskEncryptionExtension": { + "condition": "[parameters('extensionAzureDiskEncryptionConfig').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-AzureDiskEncryption', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'name'), 'AzureDiskEncryption')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.Azure.Security" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'AzureDiskEncryption'), createObject('value', 'AzureDiskEncryptionForLinux'))]", + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '2.2', '1.1'))]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'enableAutomaticUpgrade'), false())]" + }, + "forceUpdateTag": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'forceUpdateTag'), '1.0')]" + }, + "settings": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'settings'), createObject())]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionAzureDiskEncryptionConfig'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_customScriptExtension" + ] + }, + "vm_nvidiaGpuDriverWindowsExtension": { + "condition": "[parameters('extensionNvidiaGpuDriverWindows').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-NvidiaGpuDriverWindows', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'name'), 'NvidiaGpuDriverWindows')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.HpcCompute" + }, + "type": { + "value": "NvidiaGpuDriverWindows" + }, + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'typeHandlerVersion'), '1.4')]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'enableAutomaticUpgrade'), false())]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'supressFailures'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionNvidiaGpuDriverWindows'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_azureDiskEncryptionExtension" + ] + }, + "vm_hostPoolRegistrationExtension": { + "condition": "[parameters('extensionHostPoolRegistration').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-HostPoolRegistration', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'name'), 'HostPoolRegistration')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.PowerShell" + }, + "type": { + "value": "DSC" + }, + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'typeHandlerVersion'), '2.77')]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'enableAutomaticUpgrade'), false())]" + }, + "settings": { + "value": { + "modulesUrl": "[parameters('extensionHostPoolRegistration').modulesUrl]", + "configurationFunction": "[parameters('extensionHostPoolRegistration').configurationFunction]", + "properties": { + "hostPoolName": "[parameters('extensionHostPoolRegistration').hostPoolName]", + "registrationInfoToken": "[parameters('extensionHostPoolRegistration').registrationInfoToken]", + "aadJoin": true + }, + "supressFailures": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'supressFailures'), false())]" + } + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionHostPoolRegistration'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_nvidiaGpuDriverWindowsExtension" + ] + }, + "vm_azureGuestConfigurationExtension": { + "condition": "[parameters('extensionGuestConfigurationExtension').enabled]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-GuestConfiguration', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualMachineName": { + "value": "[parameters('name')]" + }, + "name": "[if(coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'name'), equals(parameters('osType'), 'Windows')), createObject('value', 'AzurePolicyforWindows'), createObject('value', 'AzurePolicyforLinux'))]", + "location": { + "value": "[parameters('location')]" + }, + "publisher": { + "value": "Microsoft.GuestConfiguration" + }, + "type": "[if(equals(parameters('osType'), 'Windows'), createObject('value', 'ConfigurationforWindows'), createObject('value', 'ConfigurationForLinux'))]", + "typeHandlerVersion": { + "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'typeHandlerVersion'), if(equals(parameters('osType'), 'Windows'), '1.0', '1.0'))]" + }, + "autoUpgradeMinorVersion": { + "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'autoUpgradeMinorVersion'), true())]" + }, + "enableAutomaticUpgrade": { + "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'enableAutomaticUpgrade'), true())]" + }, + "forceUpdateTag": { + "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'forceUpdateTag'), '1.0')]" + }, + "settings": { + "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'settings'), createObject())]" + }, + "supressFailures": { + "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'supressFailures'), false())]" + }, + "protectedSettings": { + "value": "[parameters('extensionGuestConfigurationExtensionProtectedSettings')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('extensionGuestConfigurationExtension'), 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "8482591295619883067" + }, + "name": "Virtual Machine Extensions", + "description": "This module deploys a Virtual Machine Extension." + }, + "parameters": { + "virtualMachineName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual machine that extension is provisioned for. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the virtual machine extension." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The location the extension is deployed to." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the extension handler publisher." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. Specifies the type of the extension; an example is \"CustomScriptExtension\"." + } + }, + "typeHandlerVersion": { + "type": "string", + "metadata": { + "description": "Required. Specifies the version of the script handler." + } + }, + "autoUpgradeMinorVersion": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should use a newer minor version if one is available at deployment time. Once deployed, however, the extension will not upgrade minor versions unless redeployed, even with this property set to true." + } + }, + "forceUpdateTag": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. How the extension handler should be forced to update even if the extension configuration has not changed." + } + }, + "settings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific settings." + } + }, + "protectedSettings": { + "type": "secureObject", + "defaultValue": {}, + "metadata": { + "description": "Optional. Any object that contains the extension specific protected settings." + } + }, + "supressFailures": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether failures stemming from the extension will be suppressed (Operational failures such as not connecting to the VM will not be suppressed regardless of this value). The default is false." + } + }, + "enableAutomaticUpgrade": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether the extension should be automatically upgraded by the platform if there is a newer version of the extension available." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "virtualMachine": { + "existing": true, + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2022-11-01", + "name": "[parameters('virtualMachineName')]" + }, + "extension": { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2022-11-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "publisher": "[parameters('publisher')]", + "type": "[parameters('type')]", + "typeHandlerVersion": "[parameters('typeHandlerVersion')]", + "autoUpgradeMinorVersion": "[parameters('autoUpgradeMinorVersion')]", + "enableAutomaticUpgrade": "[parameters('enableAutomaticUpgrade')]", + "forceUpdateTag": "[if(not(empty(parameters('forceUpdateTag'))), parameters('forceUpdateTag'), null())]", + "settings": "[if(not(empty(parameters('settings'))), parameters('settings'), null())]", + "protectedSettings": "[if(not(empty(parameters('protectedSettings'))), parameters('protectedSettings'), null())]", + "suppressFailures": "[parameters('supressFailures')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('virtualMachineName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the extension was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('extension', '2022-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_hostPoolRegistrationExtension" + ] + }, + "vm_backup": { + "condition": "[not(empty(parameters('backupVaultName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-VM-Backup', uniqueString(deployment().name, parameters('location')))]", + "resourceGroup": "[parameters('backupVaultResourceGroup')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('vm;iaasvmcontainerv2;{0};{1}', resourceGroup().name, parameters('name'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "policyId": { + "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies', parameters('backupVaultName'), parameters('backupPolicyName'))]" + }, + "protectedItemType": { + "value": "Microsoft.Compute/virtualMachines" + }, + "protectionContainerName": { + "value": "[format('iaasvmcontainer;iaasvmcontainerv2;{0};{1}', resourceGroup().name, parameters('name'))]" + }, + "recoveryVaultName": { + "value": "[parameters('backupVaultName')]" + }, + "sourceResourceId": { + "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "7743264001610407207" + }, + "name": "Recovery Service Vaults Protection Container Protected Item", + "description": "This module deploys a Recovery Services Vault Protection Container Protected Item." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the resource." + } + }, + "protectionContainerName": { + "type": "string", + "metadata": { + "description": "Conditional. Name of the Azure Recovery Service Vault Protection Container. Required if the template is used in a standalone deployment." + } + }, + "recoveryVaultName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Azure Recovery Service Vault. Required if the template is used in a standalone deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "protectedItemType": { + "type": "string", + "allowedValues": [ + "AzureFileShareProtectedItem", + "AzureVmWorkloadSAPAseDatabase", + "AzureVmWorkloadSAPHanaDatabase", + "AzureVmWorkloadSQLDatabase", + "DPMProtectedItem", + "GenericProtectedItem", + "MabFileFolderProtectedItem", + "Microsoft.ClassicCompute/virtualMachines", + "Microsoft.Compute/virtualMachines", + "Microsoft.Sql/servers/databases" + ], + "metadata": { + "description": "Required. The backup item type." + } + }, + "policyId": { + "type": "string", + "metadata": { + "description": "Required. ID of the backup policy with which this item is backed up." + } + }, + "sourceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the resource to back up." + } + } + }, + "resources": [ + { + "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems", + "apiVersion": "2023-01-01", + "name": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "protectedItemType": "[parameters('protectedItemType')]", + "policyId": "[parameters('policyId')]", + "sourceResourceId": "[parameters('sourceResourceId')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Resource Group the protected item was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the protected item." + }, + "value": "[resourceId('Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems', split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[0], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[1], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[2], split(format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name')), '/')[3])]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The Name of the protected item." + }, + "value": "[format('{0}/Azure/{1}/{2}', parameters('recoveryVaultName'), parameters('protectionContainerName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "vm", + "vm_azureGuestConfigurationExtension" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the VM." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the VM." + }, + "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the VM was created in." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('vm', '2024-07-01', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('vm', '2024-07-01', 'full').location]" + }, + "nicConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/nicConfigurationOutputType" + }, + "metadata": { + "description": "The list of NIC configurations of the virtual machine." + }, + "copy": { + "count": "[length(parameters('nicConfigurations'))]", + "input": { + "name": "[reference(format('vm_nic[{0}]', copyIndex())).outputs.name.value]", + "ipConfigurations": "[reference(format('vm_nic[{0}]', copyIndex())).outputs.ipConfigurations.value]" + } + } + } + } + } + }, + "dependsOn": [ + "nsg", + "subnetResource" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "value": "[reference('vm').outputs.resourceId.value]" + }, + "name": { + "type": "string", + "value": "[reference('vm').outputs.name.value]" + }, + "location": { + "type": "string", + "value": "[reference('vm').outputs.location.value]" + }, + "subnetId": { + "type": "string", + "value": "[reference('subnetResource').outputs.resourceId.value]" + }, + "subnetName": { + "type": "string", + "value": "[reference('subnetResource').outputs.name.value]" + }, + "nsgId": { + "type": "string", + "value": "[reference('nsg').outputs.resourceId.value]" + }, + "nsgName": { + "type": "string", + "value": "[reference('nsg').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + } + }, + "outputs": { + "vnetName": { + "type": "string", + "value": "[reference('virtualNetwork').outputs.name.value]" + }, + "vnetResourceId": { + "type": "string", + "value": "[reference('virtualNetwork').outputs.resourceId.value]" + }, + "subnets": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetOutputType" + }, + "value": "[reference('virtualNetwork').outputs.subnets.value]" + }, + "bastionSubnetId": { + "type": "string", + "value": "[reference('bastionHost').outputs.subnetId.value]" + }, + "bastionSubnetName": { + "type": "string", + "value": "[reference('bastionHost').outputs.subnetName.value]" + }, + "bastionHostId": { + "type": "string", + "value": "[reference('bastionHost').outputs.resourceId.value]" + }, + "bastionHostName": { + "type": "string", + "value": "[reference('bastionHost').outputs.name.value]" + }, + "jumpboxSubnetName": { + "type": "string", + "value": "[reference('jumpbox').outputs.subnetName.value]" + }, + "jumpboxSubnetId": { + "type": "string", + "value": "[reference('jumpbox').outputs.subnetId.value]" + }, + "jumpboxName": { + "type": "string", + "value": "[reference('jumpbox').outputs.name.value]" + }, + "jumpboxResourceId": { + "type": "string", + "value": "[reference('jumpbox').outputs.resourceId.value]" + } + } + } + } + } + ], + "outputs": { + "vnetName": { + "type": "string", + "metadata": { + "description": "Name of the Virtual Network resource." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('network-{0}-create', parameters('resourcesName')), 64)), '2022-09-01').outputs.vnetName.value]" + }, + "vnetResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Virtual Network." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('network-{0}-create', parameters('resourcesName')), 64)), '2022-09-01').outputs.vnetResourceId.value]" + }, + "subnetWebResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the \"web\" subnet." + }, + "value": "[coalesce(tryGet(first(filter(reference(resourceId('Microsoft.Resources/deployments', take(format('network-{0}-create', parameters('resourcesName')), 64)), '2022-09-01').outputs.subnets.value, lambda('s', equals(lambdaVariables('s').name, 'web')))), 'resourceId'), '')]" + }, + "subnetPrivateEndpointsResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the \"peps\" subnet for Private Endpoints." + }, + "value": "[coalesce(tryGet(first(filter(reference(resourceId('Microsoft.Resources/deployments', take(format('network-{0}-create', parameters('resourcesName')), 64)), '2022-09-01').outputs.subnets.value, lambda('s', equals(lambdaVariables('s').name, 'peps')))), 'resourceId'), '')]" + }, + "bastionResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Bastion Host." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('network-{0}-create', parameters('resourcesName')), 64)), '2022-09-01').outputs.bastionHostId.value]" + }, + "jumpboxResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the Jumpbox VM." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', take(format('network-{0}-create', parameters('resourcesName')), 64)), '2022-09-01').outputs.jumpboxResourceId.value]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "userAssignedIdentity": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.managed-identity.user-assigned-identity.{0}', variables('userAssignedIdentityResourceName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('userAssignedIdentityResourceName')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "16707109626832623586" + }, + "name": "User Assigned Identities", + "description": "This module deploys a User Assigned Identity." + }, + "definitions": { + "federatedIdentityCredentialType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the federated identity credential." + } + }, + "audiences": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external identity." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the federated identity credential." + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the User Assigned Identity." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "federatedIdentityCredentials": { + "type": "array", + "items": { + "$ref": "#/definitions/federatedIdentityCredentialType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The federated identity credentials list to indicate which token from the external IdP should be trusted by your application. Federated identity credentials are supported on applications only. A maximum of 20 federated identity credentials can be added per application object." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.managedidentity-userassignedidentity.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "userAssignedIdentity": { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2024-11-30", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "userAssignedIdentity_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_roleAssignments": { + "copy": { + "name": "userAssignedIdentity_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_federatedIdentityCredentials": { + "copy": { + "name": "userAssignedIdentity_federatedIdentityCredentials", + "count": "[length(coalesce(parameters('federatedIdentityCredentials'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-UserMSI-FederatedIdentityCred-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].name]" + }, + "userAssignedIdentityName": { + "value": "[parameters('name')]" + }, + "audiences": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].audiences]" + }, + "issuer": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].issuer]" + }, + "subject": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].subject]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "13656021764446440473" + }, + "name": "User Assigned Identity Federated Identity Credential", + "description": "This module deploys a User Assigned Identity Federated Identity Credential." + }, + "parameters": { + "userAssignedIdentityName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent user assigned identity. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "audiences": { + "type": "array", + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token. Should be set to api://AzureADTokenExchange for Azure AD. It says what Microsoft identity platform should accept in the aud claim in the incoming token. This value represents Azure AD in your external identity provider and has no fixed value across identity providers - you might need to create a new application registration in your IdP to serve as the audience of this token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted. Must match the issuer claim of the external token being exchanged." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external software workload within the external identity provider. Like the audience value, it has no fixed format, as each IdP uses their own - sometimes a GUID, sometimes a colon delimited identifier, sometimes arbitrary strings. The value here must match the sub claim within the token presented to Azure AD." + } + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials", + "apiVersion": "2024-11-30", + "name": "[format('{0}/{1}', parameters('userAssignedIdentityName'), parameters('name'))]", + "properties": { + "audiences": "[parameters('audiences')]", + "issuer": "[parameters('issuer')]", + "subject": "[parameters('subject')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the federated identity credential." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the federated identity credential." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials', parameters('userAssignedIdentityName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the federated identity credential was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "userAssignedIdentity" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the user assigned identity." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user assigned identity." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "The principal ID (object ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').principalId]" + }, + "clientId": { + "type": "string", + "metadata": { + "description": "The client ID (application ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').clientId]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the user assigned identity was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('userAssignedIdentity', '2024-11-30', 'full').location]" + } + } + } + } + }, + "avmContainerRegistry": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('avmContainerRegistry-{0}', uniqueString('avmContainerRegistry', deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "acrName": { + "value": "[format('cr{0}', replace(variables('solutionSuffix'), '-', ''))]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "acrSku": { + "value": "Standard" + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "zoneRedundancy": { + "value": "Disabled" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('managedCluster').outputs.systemAssignedMIPrincipalId.value]", + "roleDefinitionIdOrName": "AcrPull", + "principalType": "ServicePrincipal" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "1461768805384515582" + }, + "name": "Container Registry Module" + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "acrName": { + "type": "string", + "metadata": { + "description": "The name of the Azure Container Registry" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location of the Azure Container Registry" + } + }, + "acrSku": { + "type": "string", + "defaultValue": "Basic", + "metadata": { + "description": "SKU for the Azure Container Registry" + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "metadata": { + "description": "Public network access setting for the Azure Container Registry" + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "metadata": { + "description": "Zone redundancy setting for the Azure Container Registry" + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags to be applied to the Container Registry" + } + } + }, + "resources": { + "avmContainerRegistry": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('acrName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('acrName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "acrSku": { + "value": "[parameters('acrSku')]" + }, + "publicNetworkAccess": { + "value": "[parameters('publicNetworkAccess')]" + }, + "zoneRedundancy": { + "value": "[parameters('zoneRedundancy')]" + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "12422547988165106386" + }, + "name": "Azure Container Registries (ACR)", + "description": "This module deploys an Azure Container Registry (ACR)." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "scopeMapsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a scope map." + } + }, + "cacheRuleType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cache rule." + } + }, + "credentialSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityOnlySysAssignedType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "authCredentials": { + "type": "array", + "items": { + "$ref": "#/definitions/authCredentialsType" + }, + "metadata": { + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." + } + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "Required. The credentials are stored for this upstream or login server." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a credential set." + } + }, + "replicationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a replication." + } + }, + "webhookType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Optional. The name of the registry webhook." + } + }, + "serviceUri": { + "type": "string", + "metadata": { + "description": "Required. The service URI for the webhook to post notifications." + } + }, + "status": { + "type": "string", + "allowedValues": [ + "disabled", + "enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. The status of the webhook at the time the operation was called." + } + }, + "action": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of actions that trigger the webhook to post notifications." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customHeaders": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a webhook." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "authCredentialsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential." + } + }, + "usernameSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the username." + } + }, + "passwordSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the password." + } + } + }, + "metadata": { + "description": "The type for auth credentials.", + "__bicep_imported_from!": { + "sourceTemplate": "credential-set/main.bicep" + } + } + }, + "customerManagedKeyWithAutoRotateType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityOnlySysAssignedType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Required. Name of your Azure Container Registry." + } + }, + "acrAdminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable admin user that have push / pull permission to the registry." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "acrSku": { + "type": "string", + "defaultValue": "Premium", + "allowedValues": [ + "Basic", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Tier of your Azure container registry." + } + }, + "exportPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the export policy is enabled or not." + } + }, + "quarantinePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "trustPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "retentionPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the retention policy is enabled or not." + } + }, + "retentionPolicyDays": { + "type": "int", + "defaultValue": 15, + "metadata": { + "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." + } + }, + "azureADAuthenticationAsArmPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registry is enabled or not. Default is enabled." + } + }, + "softDeletePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. Soft Delete policy status. Default is disabled." + } + }, + "softDeletePolicyDays": { + "type": "int", + "defaultValue": 7, + "metadata": { + "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "allowedValues": [ + "AzureServices", + "None" + ], + "metadata": { + "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." + } + }, + "networkRuleSetDefaultAction": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. The default action of allow or deny when no other rules match." + } + }, + "networkRuleSetIpRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + }, + "replications": { + "type": "array", + "items": { + "$ref": "#/definitions/replicationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. All replications to create." + } + }, + "webhooks": { + "type": "array", + "items": { + "$ref": "#/definitions/webhookType" + }, + "nullable": true, + "metadata": { + "description": "Optional. All webhooks to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "cacheRules": { + "type": "array", + "items": { + "$ref": "#/definitions/cacheRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Cache Rules." + } + }, + "credentialSets": { + "type": "array", + "items": { + "$ref": "#/definitions/credentialSetType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of Credential Sets." + } + }, + "scopeMaps": { + "type": "array", + "items": { + "$ref": "#/definitions/scopeMapsType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Scope maps setting." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", + "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", + "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", + "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", + "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.containerregistry-registry.{0}.{1}', replace('0.9.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "registry": { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('acrSku')]" + }, + "properties": { + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, tryGet(parameters('customerManagedKey'), 'keyVersion')), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)))), null())]", + "policies": { + "azureADAuthenticationAsArmPolicy": { + "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" + }, + "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", + "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", + "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", + "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", + "softDeletePolicy": { + "retentionDays": "[parameters('softDeletePolicyDays')]", + "status": "[parameters('softDeletePolicyStatus')]" + } + }, + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "networkRuleSet": "[if(not(empty(parameters('networkRuleSetIpRules'))), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', parameters('networkRuleSetIpRules')), null())]", + "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" + }, + "dependsOn": [ + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "registry_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_diagnosticSettings": { + "copy": { + "name": "registry_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_roleAssignments": { + "copy": { + "name": "registry_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_scopeMaps": { + "copy": { + "name": "registry_scopeMaps", + "count": "[length(coalesce(parameters('scopeMaps'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Scope-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'name')]" + }, + "actions": { + "value": "[coalesce(parameters('scopeMaps'), createArray())[copyIndex()].actions]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'description')]" + }, + "registryName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "11112300500664950599" + }, + "name": "Container Registries scopeMaps", + "description": "This module deploys an Azure Container Registry (ACR) scopeMap." + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "scopeMap": { + "type": "Microsoft.ContainerRegistry/registries/scopeMaps", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "actions": "[parameters('actions')]", + "description": "[parameters('description')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the scope map." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the scope map was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the scope map." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_replications": { + "copy": { + "name": "registry_replications", + "count": "[length(coalesce(parameters('replications'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" + }, + "regionEndpointEnabled": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" + }, + "zoneRedundancy": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "6036875058945996178" + }, + "name": "Azure Container Registry (ACR) Replications", + "description": "This module deploys an Azure Container Registry (ACR) Replication." + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "replication": { + "type": "Microsoft.ContainerRegistry/registries/replications", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the replication." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the replication." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the replication was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('replication', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_credentialSets": { + "copy": { + "name": "registry_credentialSets", + "count": "[length(coalesce(parameters('credentialSets'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-CredentialSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "managedIdentities": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].managedIdentities]" + }, + "authCredentials": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].authCredentials]" + }, + "loginServer": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].loginServer]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "15848218260506856293" + }, + "name": "Container Registries Credential Sets", + "description": "This module deploys an ACR Credential Set." + }, + "definitions": { + "authCredentialsType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential." + } + }, + "usernameSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the username." + } + }, + "passwordSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the password." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for auth credentials." + } + }, + "managedIdentityOnlySysAssignedType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if only system-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityOnlySysAssignedType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "authCredentials": { + "type": "array", + "items": { + "$ref": "#/definitions/authCredentialsType" + }, + "metadata": { + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." + } + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "Required. The credentials are stored for this upstream or login server." + } + } + }, + "variables": { + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]" + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "credentialSet": { + "type": "Microsoft.ContainerRegistry/registries/credentialSets", + "apiVersion": "2023-11-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "identity": "[variables('identity')]", + "properties": { + "authCredentials": "[parameters('authCredentials')]", + "loginServer": "[parameters('loginServer')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Credential Set." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Credential Set." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Credential Set." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('credentialSet', '2023-11-01-preview', 'full'), 'identity'), 'principalId')]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_cacheRules": { + "copy": { + "name": "registry_cacheRules", + "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "registryName": { + "value": "[parameters('name')]" + }, + "sourceRepository": { + "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name')]" + }, + "targetRepository": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" + }, + "credentialSetResourceId": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "3783697279882479947" + }, + "name": "Container Registries Cache", + "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache))." + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[replace(replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-'), '*', '')]", + "metadata": { + "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "defaultValue": "[parameters('sourceRepository')]", + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the credential store which is associated with the cache rule." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "cacheRule": { + "type": "Microsoft.ContainerRegistry/registries/cacheRules", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "sourceRepository": "[parameters('sourceRepository')]", + "targetRepository": "[parameters('targetRepository')]", + "credentialSetResourceId": "[parameters('credentialSetResourceId')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Cache Rule." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Cache Rule." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Cache Rule." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry", + "registry_credentialSets" + ] + }, + "registry_webhooks": { + "copy": { + "name": "registry_webhooks", + "count": "[length(coalesce(parameters('webhooks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" + }, + "action": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action')]" + }, + "customHeaders": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" + }, + "scope": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" + }, + "status": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" + }, + "serviceUri": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "10084997815751263562" + }, + "name": "Azure Container Registry (ACR) Webhooks", + "description": "This module deploys an Azure Container Registry (ACR) Webhook." + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}webhook', parameters('registryName'))]", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Optional. The name of the registry webhook." + } + }, + "serviceUri": { + "type": "string", + "metadata": { + "description": "Required. The service URI for the webhook to post notifications." + } + }, + "status": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The status of the webhook at the time the operation was called." + } + }, + "action": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [ + "chart_delete", + "chart_push", + "delete", + "push", + "quarantine" + ], + "metadata": { + "description": "Optional. The list of actions that trigger the webhook to post notifications." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customHeaders": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "webhook": { + "type": "Microsoft.ContainerRegistry/registries/webhooks", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "actions": "[parameters('action')]", + "customHeaders": "[parameters('customHeaders')]", + "scope": "[parameters('scope')]", + "serviceUri": "[parameters('serviceUri')]", + "status": "[parameters('status')]" + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the webhook." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the webhook." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "actions": { + "type": "array", + "metadata": { + "description": "The actions of the webhook." + }, + "value": "[reference('webhook').actions]" + }, + "status": { + "type": "string", + "metadata": { + "description": "The status of the webhook." + }, + "value": "[reference('webhook').status]" + }, + "provistioningState": { + "type": "string", + "metadata": { + "description": "The provisioning state of the webhook." + }, + "value": "[reference('webhook').provisioningState]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('webhook', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_privateEndpoints": { + "copy": { + "name": "registry_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The resource IDs of the network interfaces associated with the private endpoint." + }, + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + } + } + } + }, + "dependsOn": [ + "registry", + "registry_replications" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Azure container registry." + }, + "value": "[parameters('name')]" + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "The reference to the Azure container registry." + }, + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2019-05-01').loginServer]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure container registry." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('registry', '2023-06-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('registry', '2023-06-01-preview', 'full').location]" + }, + "credentialSetsSystemAssignedMIPrincipalIds": { + "type": "array", + "metadata": { + "description": "The Principal IDs of the ACR Credential Sets system-assigned identities." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", + "input": "[tryGet(tryGet(reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs, 'systemAssignedMIPrincipalId'), 'value')]" + } + }, + "credentialSetsResourceIds": { + "type": "array", + "metadata": { + "description": "The Resource IDs of the ACR Credential Sets." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('credentialSets'), createArray()))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(coalesce(parameters('credentialSets'), createArray())))[copyIndex()])).outputs.resourceId.value]" + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the Azure container registry." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('registry_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "value": "[reference('avmContainerRegistry').outputs.name.value]" + }, + "resourceId": { + "type": "string", + "value": "[reference('avmContainerRegistry').outputs.resourceId.value]" + }, + "loginServer": { + "type": "string", + "value": "[reference('avmContainerRegistry').outputs.loginServer.value]" + } + } + } + }, + "dependsOn": [ + "managedCluster" + ] + }, + "avmCosmosDB": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.cosmos-{0}', variables('solutionSuffix')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('cosmos-{0}', variables('solutionSuffix'))]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "mongodbDatabases": { + "value": [ + { + "name": "default", + "tag": "default database" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "databaseAccountOfferType": { + "value": "Standard" + }, + "serverVersion": { + "value": "7.0" + }, + "enableAnalyticalStorage": { + "value": true + }, + "defaultConsistencyLevel": { + "value": "Session" + }, + "maxIntervalInSeconds": { + "value": 5 + }, + "maxStalenessPrefix": { + "value": 100 + }, + "networkRestrictions": { + "value": { + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), 'Disabled', 'Enabled')]", + "ipRules": [], + "virtualNetworkRules": [] + } + }, + "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('cosmosdb-private-endpoint-{0}', variables('solutionSuffix')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cosmosDB)).outputs.resourceId.value))), 'service', 'MongoDB', 'subnetResourceId', reference('network').outputs.subnetPrivateEndpointsResourceId.value))), createObject('value', createArray()))]", + "zoneRedundant": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]", + "capabilitiesToAdd": { + "value": [ + "EnableMongo" + ] + }, + "automaticFailover": "[if(parameters('enableRedundancy'), createObject('value', true()), createObject('value', false()))]", + "failoverLocations": "[if(parameters('enableRedundancy'), createObject('value', createArray(createObject('failoverPriority', 0, 'isZoneRedundant', true(), 'locationName', variables('solutionLocation')), createObject('failoverPriority', 1, 'isZoneRedundant', true(), 'locationName', variables('cosmosDbHaLocation')))), createObject('value', createArray(createObject('locationName', variables('solutionLocation'), 'failoverPriority', 0, 'isZoneRedundant', parameters('enableRedundancy')))))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "8020152823352819436" + }, + "name": "Azure Cosmos DB account", + "description": "This module deploys an Azure Cosmos DB account. The API used for the account is determined by the child resources that are deployed." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group ID for the private endpoint group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "fully-qualified domain name (FQDN) that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses for the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "failoverLocationType": { + "type": "object", + "properties": { + "failoverPriority": { + "type": "int", + "metadata": { + "description": "Required. The failover priority of the region. A failover priority of 0 indicates a write region. The maximum value for a failover priority = (total number of regions - 1). Failover priority values must be unique for each of the regions in which the database account exists." + } + }, + "isZoneRedundant": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Flag to indicate whether or not this region is an AvailabilityZone region. Defaults to true." + } + }, + "locationName": { + "type": "string", + "metadata": { + "description": "Required. The name of the region." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the failover location." + } + }, + "dataPlaneRoleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The unique name of the role assignment." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier of the Azure Cosmos DB for NoSQL native role-based access control definition." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier for the associated Microsoft Entra ID principal to which access is being granted through this role-based access control assignment. The tenant ID for the principal is inferred using the tenant associated with the subscription." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an Azure Cosmos DB for NoSQL native role-based access control assignment." + } + }, + "dataPlaneRoleDefinitionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The unique identifier of the role-based access control definition." + } + }, + "roleName": { + "type": "string", + "metadata": { + "description": "Required. A user-friendly name for the role-based access control definition. This must be unique within the database account." + } + }, + "dataActions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "assignableScopes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. A set of fully-qualified scopes at or below which role-based access control assignments may be created using this definition. This setting allows application of this definition on the entire account or any underlying resource. This setting must have at least one element. Scopes higher than the account level are not enforceable as assignable scopes. Resources referenced in assignable scopes do not need to exist at creation. Defaults to the current account scope." + } + }, + "assignments": { + "type": "array", + "items": { + "$ref": "#/definitions/sqlRoleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of role-based access control assignments to be created for the definition." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an Azure Cosmos DB for NoSQL or Table native role-based access control definition." + } + }, + "sqlDatabaseType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the database ." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Request units per second. Will be ignored if `autoscaleSettingsMaxThroughput` is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level. Defaults to 400." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the autoscale settings and represents maximum throughput the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If the value is not set, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "containers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the container." + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "maxLength": 3, + "metadata": { + "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." + } + }, + "analyticalStorageTtl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "maxValue": 1000000, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level." + } + }, + "conflictResolutionPolicy": { + "type": "object", + "properties": { + "conflictResolutionPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The conflict resolution path in the case of LastWriterWins mode. Required if `mode` is set to 'LastWriterWins'." + } + }, + "conflictResolutionProcedure": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The procedure to resolve conflicts in the case of custom mode. Required if `mode` is set to 'Custom'." + } + }, + "mode": { + "type": "string", + "allowedValues": [ + "Custom", + "LastWriterWins" + ], + "metadata": { + "description": "Required. Indicates the conflict resolution mode." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." + } + }, + "defaultTtl": { + "type": "int", + "nullable": true, + "minValue": -1, + "maxValue": 2147483647, + "metadata": { + "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." + } + }, + "indexingPolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Indexing policy of the container." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "Hash", + "MultiHash" + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." + } + }, + "version": { + "type": "int", + "allowedValues": [ + 1, + 2 + ], + "nullable": true, + "metadata": { + "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used." + } + }, + "uniqueKeyPolicyKeys": { + "type": "array", + "items": { + "type": "object", + "properties": { + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of paths must be unique for each document in the Azure Cosmos DB service." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Set of containers to deploy in the database." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an Azure Cosmos DB for NoSQL database." + } + }, + "networkRestrictionType": { + "type": "object", + "properties": { + "ipRules": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. A single IPv4 address or a single IPv4 address range in Classless Inter-Domain Routing (CIDR) format. Provided IPs must be well-formatted and cannot be contained in one of the following ranges: `10.0.0.0/8`, `100.64.0.0/10`, `172.16.0.0/12`, `192.168.0.0/16`, since these are not enforceable by the IP address filter. Example of valid inputs: `23.40.210.245` or `23.40.210.0/8`." + } + }, + "networkAclBypass": { + "type": "string", + "allowedValues": [ + "AzureServices", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the network ACL bypass for Azure services. Default to \"None\"." + } + }, + "publicNetworkAccess": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. Whether requests from the public network are allowed. Default to \"Disabled\"." + } + }, + "virtualNetworkRules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of a subnet." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of virtual network access control list (ACL) rules configured for the account." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the network restriction." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateEndpointMultiServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\" for a Storage Account's Private Endpoints." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can NOT be assumed (i.e., for services that have more than one subresource, like Storage Account with Blob (blob, table, queue, file, ...).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "sqlRoleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name unique identifier of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription." + } + } + }, + "metadata": { + "description": "The type for the SQL Role Assignments.", + "__bicep_imported_from!": { + "sourceTemplate": "sql-role-definition/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the account." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Defaults to the current resource group scope location. Location for all resources." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.DocumentDB/databaseAccounts@2024-11-15#properties/tags" + }, + "description": "Optional. Tags for the resource." + }, + "nullable": true + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "databaseAccountOfferType": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard" + ], + "metadata": { + "description": "Optional. The offer type for the account. Defaults to \"Standard\"." + } + }, + "failoverLocations": { + "type": "array", + "items": { + "$ref": "#/definitions/failoverLocationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The set of locations enabled for the account. Defaults to the location where the account is deployed." + } + }, + "zoneRedundant": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether the single-region account is zone redundant. Defaults to true. This property is ignored for multi-region accounts." + } + }, + "defaultConsistencyLevel": { + "type": "string", + "defaultValue": "Session", + "allowedValues": [ + "Eventual", + "ConsistentPrefix", + "Session", + "BoundedStaleness", + "Strong" + ], + "metadata": { + "description": "Optional. The default consistency level of the account. Defaults to \"Session\"." + } + }, + "disableLocalAuthentication": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Opt-out of local authentication and ensure that only Microsoft Entra can be used exclusively for authentication. Defaults to true." + } + }, + "enableAnalyticalStorage": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Flag to indicate whether to enable storage analytics. Defaults to false." + } + }, + "automaticFailover": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable automatic failover for regions. Defaults to true." + } + }, + "enableFreeTier": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Flag to indicate whether \"Free Tier\" is enabled. Defaults to false." + } + }, + "enableMultipleWriteLocations": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables the account to write in multiple locations. Periodic backup must be used if enabled. Defaults to false." + } + }, + "disableKeyBasedMetadataWriteAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Disable write operations on metadata resources (databases, containers, throughput) via account keys. Defaults to true." + } + }, + "maxStalenessPrefix": { + "type": "int", + "defaultValue": 100000, + "minValue": 1, + "maxValue": 2147483647, + "metadata": { + "description": "Optional. The maximum stale requests. Required for \"BoundedStaleness\" consistency level. Valid ranges, Single Region: 10 to 1000000. Multi Region: 100000 to 1000000. Defaults to 100000." + } + }, + "maxIntervalInSeconds": { + "type": "int", + "defaultValue": 300, + "minValue": 5, + "maxValue": 86400, + "metadata": { + "description": "Optional. The maximum lag time in minutes. Required for \"BoundedStaleness\" consistency level. Valid ranges, Single Region: 5 to 84600. Multi Region: 300 to 86400. Defaults to 300." + } + }, + "serverVersion": { + "type": "string", + "defaultValue": "4.2", + "allowedValues": [ + "3.2", + "3.6", + "4.0", + "4.2", + "5.0", + "6.0", + "7.0" + ], + "metadata": { + "description": "Optional. Specifies the MongoDB server version to use if using Azure Cosmos DB for MongoDB RU. Defaults to \"4.2\"." + } + }, + "sqlDatabases": { + "type": "array", + "items": { + "$ref": "#/definitions/sqlDatabaseType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration for databases when using Azure Cosmos DB for NoSQL." + } + }, + "mongodbDatabases": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Configuration for databases when using Azure Cosmos DB for MongoDB RU." + } + }, + "gremlinDatabases": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Configuration for databases when using Azure Cosmos DB for Apache Gremlin." + } + }, + "tables": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Configuration for databases when using Azure Cosmos DB for Table." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "totalThroughputLimit": { + "type": "int", + "defaultValue": -1, + "metadata": { + "description": "Optional. The total throughput limit imposed on this account in request units per second (RU/s). Default to unlimited throughput." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of control plane Azure role-based access control assignments." + } + }, + "dataPlaneRoleDefinitions": { + "type": "array", + "items": { + "$ref": "#/definitions/dataPlaneRoleDefinitionType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configurations for Azure Cosmos DB for NoSQL native role-based access control definitions. Allows the creations of custom role definitions." + } + }, + "dataPlaneRoleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/dataPlaneRoleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configurations for Azure Cosmos DB for NoSQL native role-based access control assignments." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings for the service." + } + }, + "capabilitiesToAdd": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "allowedValues": [ + "EnableCassandra", + "EnableTable", + "EnableGremlin", + "EnableMongo", + "DisableRateLimitingResponses", + "EnableServerless", + "EnableNoSQLVectorSearch", + "EnableNoSQLFullTextSearch", + "EnableMaterializedViews", + "DeleteAllItemsByPartitionKey" + ], + "metadata": { + "description": "Optional. A list of Azure Cosmos DB specific capabilities for the account." + } + }, + "backupPolicyType": { + "type": "string", + "defaultValue": "Continuous", + "allowedValues": [ + "Periodic", + "Continuous" + ], + "metadata": { + "description": "Optional. Configures the backup mode. Periodic backup must be used if multiple write locations are used. Defaults to \"Continuous\"." + } + }, + "backupPolicyContinuousTier": { + "type": "string", + "defaultValue": "Continuous30Days", + "allowedValues": [ + "Continuous30Days", + "Continuous7Days" + ], + "metadata": { + "description": "Optional. Configuration values to specify the retention period for continuous mode backup. Default to \"Continuous30Days\"." + } + }, + "backupIntervalInMinutes": { + "type": "int", + "defaultValue": 240, + "minValue": 60, + "maxValue": 1440, + "metadata": { + "description": "Optional. An integer representing the interval in minutes between two backups. This setting only applies to the periodic backup type. Defaults to 240." + } + }, + "backupRetentionIntervalInHours": { + "type": "int", + "defaultValue": 8, + "minValue": 2, + "maxValue": 720, + "metadata": { + "description": "Optional. An integer representing the time (in hours) that each backup is retained. This setting only applies to the periodic backup type. Defaults to 8." + } + }, + "backupStorageRedundancy": { + "type": "string", + "defaultValue": "Local", + "allowedValues": [ + "Geo", + "Local", + "Zone" + ], + "metadata": { + "description": "Optional. Setting that indicates the type of backup residency. This setting only applies to the periodic backup type. Defaults to \"Local\"." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointMultiServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is advised to use private endpoints whenever possible." + } + }, + "networkRestrictions": { + "$ref": "#/definitions/networkRestrictionType", + "defaultValue": { + "ipRules": [], + "virtualNetworkRules": [], + "publicNetworkAccess": "Disabled" + }, + "metadata": { + "description": "Optional. The network configuration of this module. Defaults to `{ ipRules: [], virtualNetworkRules: [], publicNetworkAccess: 'Disabled' }`." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "Tls12", + "allowedValues": [ + "Tls12" + ], + "metadata": { + "description": "Optional. Setting that indicates the minimum allowed TLS version. Azure Cosmos DB for MongoDB RU and Apache Cassandra only work with TLS 1.2 or later. Defaults to \"Tls12\" (TLS 1.2)." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInControlPlaneRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInControlPlaneRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", + "Cosmos DB Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "CosmosBackupOperator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db7b14f2-5adf-42da-9f96-f2ee17bab5cb')]", + "CosmosRestoreOperator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5432c526-bc82-444a-b7ba-57c5b0b5b34f')]", + "DocumentDB Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5bd9cd88-fe45-4216-938b-f97437e15450')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-07-01", + "name": "[format('46d3xbcp.res.documentdb-databaseaccount.{0}.{1}', replace('0.15.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "databaseAccount": { + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "kind": "[if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'GlobalDocumentDB')]", + "properties": "[shallowMerge(createArray(createObject('databaseAccountOfferType', parameters('databaseAccountOfferType'), 'backupPolicy', shallowMerge(createArray(createObject('type', parameters('backupPolicyType')), if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject()), if(equals(parameters('backupPolicyType'), 'Periodic'), createObject('periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))), createObject()))), 'capabilities', map(coalesce(parameters('capabilitiesToAdd'), createArray()), lambda('capability', createObject('name', lambdaVariables('capability')))), 'minimalTlsVersion', parameters('minimumTlsVersion'), 'capacity', createObject('totalThroughputLimit', parameters('totalThroughputLimit')), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Disabled')), if(or(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('tables')))), createObject('consistencyPolicy', shallowMerge(createArray(createObject('defaultConsistencyLevel', parameters('defaultConsistencyLevel')), if(equals(parameters('defaultConsistencyLevel'), 'BoundedStaleness'), createObject('maxStalenessPrefix', parameters('maxStalenessPrefix'), 'maxIntervalInSeconds', parameters('maxIntervalInSeconds')), createObject()))), 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(not(empty(parameters('failoverLocations'))), map(parameters('failoverLocations'), lambda('failoverLocation', createObject('failoverPriority', lambdaVariables('failoverLocation').failoverPriority, 'locationName', lambdaVariables('failoverLocation').locationName, 'isZoneRedundant', coalesce(tryGet(lambdaVariables('failoverLocation'), 'isZoneRedundant'), true())))), createArray(createObject('failoverPriority', 0, 'locationName', parameters('location'), 'isZoneRedundant', parameters('zoneRedundant')))), 'ipRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray()), lambda('ipRule', createObject('ipAddressOrRange', lambdaVariables('ipRule')))), 'virtualNetworkRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray()), lambda('rule', createObject('id', lambdaVariables('rule').subnetResourceId, 'ignoreMissingVNetServiceEndpoint', false()))), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'None'), 'isVirtualNetworkFilterEnabled', or(not(empty(tryGet(parameters('networkRestrictions'), 'ipRules'))), not(empty(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules')))), 'enableFreeTier', parameters('enableFreeTier'), 'enableAutomaticFailover', parameters('automaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(or(not(empty(parameters('mongodbDatabases'))), not(empty(parameters('gremlinDatabases')))), createObject('disableLocalAuth', false(), 'disableKeyBasedMetadataWriteAccess', false()), createObject('disableLocalAuth', parameters('disableLocalAuthentication'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess'))), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject())))]" + }, + "databaseAccount_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_diagnosticSettings": { + "copy": { + "name": "databaseAccount_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_roleAssignments": { + "copy": { + "name": "databaseAccount_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_sqlDatabases": { + "copy": { + "name": "databaseAccount_sqlDatabases", + "count": "[length(coalesce(parameters('sqlDatabases'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('sqlDatabases'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('sqlDatabases'), createArray())[copyIndex()].name]" + }, + "containers": { + "value": "[tryGet(coalesce(parameters('sqlDatabases'), createArray())[copyIndex()], 'containers')]" + }, + "throughput": { + "value": "[tryGet(coalesce(parameters('sqlDatabases'), createArray())[copyIndex()], 'throughput')]" + }, + "databaseAccountName": { + "value": "[parameters('name')]" + }, + "autoscaleSettingsMaxThroughput": { + "value": "[tryGet(coalesce(parameters('sqlDatabases'), createArray())[copyIndex()], 'autoscaleSettingsMaxThroughput')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "6801379641184405078" + }, + "name": "DocumentDB Database Account SQL Databases", + "description": "This module deploys a SQL Database in a CosmosDB Account." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the SQL database ." + } + }, + "containers": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of containers to deploy in the SQL database." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Request units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. Setting throughput at the database level is only recommended for development/test or when workload across all containers in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the SQL database resource." + } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "sqlDatabase": { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('name')]" + }, + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', if(equals(parameters('autoscaleSettingsMaxThroughput'), null()), parameters('throughput'), null()), 'autoscaleSettings', if(not(equals(parameters('autoscaleSettingsMaxThroughput'), null())), createObject('maxThroughput', parameters('autoscaleSettingsMaxThroughput')), null())))]" + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "container": { + "copy": { + "name": "container", + "count": "[length(coalesce(parameters('containers'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sqldb-{1}', uniqueString(deployment().name, parameters('name')), coalesce(parameters('containers'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "sqlDatabaseName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" + }, + "analyticalStorageTtl": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'analyticalStorageTtl')]" + }, + "autoscaleSettingsMaxThroughput": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'autoscaleSettingsMaxThroughput')]" + }, + "conflictResolutionPolicy": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'conflictResolutionPolicy')]" + }, + "defaultTtl": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultTtl')]" + }, + "indexingPolicy": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'indexingPolicy')]" + }, + "kind": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'kind')]" + }, + "version": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'version')]" + }, + "paths": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'paths')]" + }, + "throughput": "[if(and(or(not(equals(parameters('throughput'), null())), not(equals(parameters('autoscaleSettingsMaxThroughput'), null()))), equals(tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'throughput'), null())), createObject('value', -1), createObject('value', tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'throughput')))]", + "uniqueKeyPolicyKeys": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'uniqueKeyPolicyKeys')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "5467755913632158534" + }, + "name": "DocumentDB Database Account SQL Database Containers", + "description": "This module deploys a SQL Database Container in a CosmosDB Account." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "sqlDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent SQL Database. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the container." + } + }, + "analyticalStorageTtl": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. Default to 0. Indicates how long data should be retained in the analytical store, for a container. Analytical store is enabled when ATTL is set with a value other than 0. If the value is set to -1, the analytical store retains all historical data, irrespective of the retention of the data in the transactional store." + } + }, + "conflictResolutionPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The conflict resolution policy for the container. Conflicts and conflict resolution policies are applicable if the Azure Cosmos DB account is configured with multiple write regions." + } + }, + "defaultTtl": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "maxValue": 2147483647, + "metadata": { + "description": "Optional. Default to -1. Default time to live (in seconds). With Time to Live or TTL, Azure Cosmos DB provides the ability to delete items automatically from a container after a certain time period. If the value is set to \"-1\", it is equal to infinity, and items don't expire by default." + } + }, + "throughput": { + "type": "int", + "defaultValue": 400, + "metadata": { + "description": "Optional. Default to 400. Request Units per second. Will be ignored if autoscaleSettingsMaxThroughput is used. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "autoscaleSettingsMaxThroughput": { + "type": "int", + "nullable": true, + "maxValue": 1000000, + "metadata": { + "description": "Optional. Specifies the Autoscale settings and represents maximum throughput, the resource can scale up to. The autoscale throughput should have valid throughput values between 1000 and 1000000 inclusive in increments of 1000. If value is set to null, then autoscale will be disabled. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the container level and not at the database level." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the SQL Database resource." + } + }, + "paths": { + "type": "array", + "items": { + "type": "string" + }, + "minLength": 1, + "maxLength": 3, + "metadata": { + "description": "Required. List of paths using which data within the container can be partitioned. For kind=MultiHash it can be up to 3. For anything else it needs to be exactly 1." + } + }, + "indexingPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Indexing policy of the container." + } + }, + "uniqueKeyPolicyKeys": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The unique key policy configuration containing a list of unique keys that enforces uniqueness constraint on documents in the collection in the Azure Cosmos DB service." + } + }, + "kind": { + "type": "string", + "defaultValue": "Hash", + "allowedValues": [ + "Hash", + "MultiHash" + ], + "metadata": { + "description": "Optional. Default to Hash. Indicates the kind of algorithm used for partitioning." + } + }, + "version": { + "type": "int", + "defaultValue": 1, + "allowedValues": [ + 1, + 2 + ], + "metadata": { + "description": "Optional. Default to 1 for Hash and 2 for MultiHash - 1 is not allowed for MultiHash. Version of the partition key definition." + } + } + }, + "variables": { + "copy": [ + { + "name": "partitionKeyPaths", + "count": "[length(parameters('paths'))]", + "input": "[if(startsWith(parameters('paths')[copyIndex('partitionKeyPaths')], '/'), parameters('paths')[copyIndex('partitionKeyPaths')], format('/{0}', parameters('paths')[copyIndex('partitionKeyPaths')]))]" + } + ], + "containerResourceParams": "[union(createObject('conflictResolutionPolicy', parameters('conflictResolutionPolicy'), 'defaultTtl', parameters('defaultTtl'), 'id', parameters('name'), 'indexingPolicy', if(not(empty(parameters('indexingPolicy'))), parameters('indexingPolicy'), null()), 'partitionKey', createObject('paths', variables('partitionKeyPaths'), 'kind', parameters('kind'), 'version', if(equals(parameters('kind'), 'MultiHash'), 2, parameters('version'))), 'uniqueKeyPolicy', if(not(empty(parameters('uniqueKeyPolicyKeys'))), createObject('uniqueKeys', parameters('uniqueKeyPolicyKeys')), null())), if(not(equals(parameters('analyticalStorageTtl'), 0)), createObject('analyticalStorageTtl', parameters('analyticalStorageTtl')), createObject()))]" + }, + "resources": { + "databaseAccount::sqlDatabase": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('sqlDatabaseName'))]" + }, + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "container": { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": "[variables('containerResourceParams')]", + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', if(and(equals(parameters('autoscaleSettingsMaxThroughput'), null()), not(equals(parameters('throughput'), -1))), parameters('throughput'), null()), 'autoscaleSettings', if(not(equals(parameters('autoscaleSettingsMaxThroughput'), null())), createObject('maxThroughput', parameters('autoscaleSettingsMaxThroughput')), null())))]" + }, + "dependsOn": [ + "databaseAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the container." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the container." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('databaseAccountName'), parameters('sqlDatabaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the container was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "sqlDatabase" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL database." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases', parameters('databaseAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL database was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_sqlRoleDefinitions": { + "copy": { + "name": "databaseAccount_sqlRoleDefinitions", + "count": "[length(coalesce(parameters('dataPlaneRoleDefinitions'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sqlrd-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'name')]" + }, + "dataActions": { + "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'dataActions')]" + }, + "roleName": { + "value": "[coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()].roleName]" + }, + "assignableScopes": { + "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'assignableScopes')]" + }, + "sqlRoleAssignments": { + "value": "[tryGet(coalesce(parameters('dataPlaneRoleDefinitions'), createArray())[copyIndex()], 'assignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "12119240119487993734" + }, + "name": "DocumentDB Database Account SQL Role Definitions.", + "description": "This module deploys a SQL Role Definision in a CosmosDB Account." + }, + "definitions": { + "sqlRoleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name unique identifier of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the SQL Role Assignments." + } + } + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The unique identifier of the Role Definition." + } + }, + "roleName": { + "type": "string", + "metadata": { + "description": "Required. A user-friendly name for the Role Definition. Must be unique for the database account." + } + }, + "dataActions": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. An array of data actions that are allowed." + } + }, + "assignableScopes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. A set of fully qualified Scopes at or below which Role Assignments may be created using this Role Definition. This will allow application of this Role Definition on the entire database account or any underlying Database / Collection. Must have at least one element. Scopes higher than Database account are not enforceable as assignable Scopes. Note that resources referenced in assignable Scopes need not exist. Defaults to the current account." + } + }, + "sqlRoleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/sqlRoleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of SQL Role Assignments to be created for the SQL Role Definition." + } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "sqlRoleDefinition": { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')))]", + "properties": { + "assignableScopes": "[coalesce(parameters('assignableScopes'), createArray(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))))]", + "permissions": [ + { + "dataActions": "[parameters('dataActions')]" + } + ], + "roleName": "[parameters('roleName')]", + "type": "CustomRole" + } + }, + "databaseAccount_sqlRoleAssignments": { + "copy": { + "name": "databaseAccount_sqlRoleAssignments", + "count": "[length(coalesce(parameters('sqlRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sqlra-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "roleDefinitionId": { + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')))]" + }, + "principalId": { + "value": "[coalesce(parameters('sqlRoleAssignments'), createArray())[copyIndex()].principalId]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('sqlRoleAssignments'), createArray())[copyIndex()], 'name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "11941443499827753966" + }, + "name": "DocumentDB Database Account SQL Role Assignments.", + "description": "This module deploys a SQL Role Assignment in a CosmosDB Account." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name unique identifier of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier of the associated SQL Role Definition." + } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "sqlRoleAssignment": { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL Role Assignment." + }, + "value": "[coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL Role Assignment." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Definition was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "sqlRoleDefinition" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL Role Definition." + }, + "value": "[coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL Role Definition." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('databaseAccountName'), coalesce(parameters('name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), parameters('databaseAccountName'), 'sql-role')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Definition was created in." + }, + "value": "[resourceGroup().name]" + }, + "roleName": { + "type": "string", + "metadata": { + "description": "The role name of the SQL Role Definition." + }, + "value": "[reference('sqlRoleDefinition').roleName]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_sqlRoleAssignments": { + "copy": { + "name": "databaseAccount_sqlRoleAssignments", + "count": "[length(coalesce(parameters('dataPlaneRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sqlra-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('name')]" + }, + "roleDefinitionId": { + "value": "[coalesce(parameters('dataPlaneRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" + }, + "principalId": { + "value": "[coalesce(parameters('dataPlaneRoleAssignments'), createArray())[copyIndex()].principalId]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('dataPlaneRoleAssignments'), createArray())[copyIndex()], 'name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "11941443499827753966" + }, + "name": "DocumentDB Database Account SQL Role Assignments.", + "description": "This module deploys a SQL Role Assignment in a CosmosDB Account." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name unique identifier of the SQL Role Assignment." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier for the associated AAD principal in the AAD graph to which access is being granted through this Role Assignment. Tenant ID for the principal is inferred using the tenant associated with the subscription." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The unique identifier of the associated SQL Role Definition." + } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "sqlRoleAssignment": { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]", + "properties": { + "principalId": "[parameters('principalId')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the SQL Role Assignment." + }, + "value": "[coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName'))))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the SQL Role Assignment." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments', parameters('databaseAccountName'), coalesce(parameters('name'), guid(parameters('roleDefinitionId'), parameters('principalId'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')))))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the SQL Role Definition was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_mongodbDatabases": { + "copy": { + "name": "databaseAccount_mongodbDatabases", + "count": "[length(coalesce(parameters('mongodbDatabases'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-mongodb-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()].name]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "collections": { + "value": "[tryGet(coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()], 'collections')]" + }, + "throughput": { + "value": "[tryGet(coalesce(parameters('mongodbDatabases'), createArray())[copyIndex()], 'throughput')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "16911349070369924403" + }, + "name": "DocumentDB Database Account MongoDB Databases", + "description": "This module deploys a MongoDB Database within a CosmosDB Account." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the mongodb database." + } + }, + "throughput": { + "type": "int", + "defaultValue": 400, + "metadata": { + "description": "Optional. Request Units per second. Setting throughput at the database level is only recommended for development/test or when workload across all collections in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the collection level and not at the database level." + } + }, + "collections": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Collections in the mongodb database." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "mongodbDatabase": { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('name')]" + }, + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', parameters('throughput')))]" + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "mongodbDatabase_collections": { + "copy": { + "name": "mongodbDatabase_collections", + "count": "[length(coalesce(parameters('collections'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-collection-{1}', uniqueString(deployment().name, parameters('name')), coalesce(parameters('collections'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "mongodbDatabaseName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('collections'), createArray())[copyIndex()].name]" + }, + "indexes": { + "value": "[coalesce(parameters('collections'), createArray())[copyIndex()].indexes]" + }, + "shardKey": { + "value": "[coalesce(parameters('collections'), createArray())[copyIndex()].shardKey]" + }, + "throughput": { + "value": "[tryGet(coalesce(parameters('collections'), createArray())[copyIndex()], 'throughput')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "7802955893269337475" + }, + "name": "DocumentDB Database Account MongoDB Database Collections", + "description": "This module deploys a MongoDB Database Collection." + }, + "parameters": { + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Cosmos DB database account. Required if the template is used in a standalone deployment." + } + }, + "mongodbDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent mongodb database. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the collection." + } + }, + "throughput": { + "type": "int", + "defaultValue": 400, + "metadata": { + "description": "Optional. Request Units per second. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the collection level and not at the database level." + } + }, + "indexes": { + "type": "array", + "metadata": { + "description": "Required. Indexes for the collection." + } + }, + "shardKey": { + "type": "object", + "metadata": { + "description": "Required. ShardKey for the collection." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('mongodbDatabaseName'), parameters('name'))]", + "properties": { + "options": "[if(contains(reference(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('databaseAccountName')), '2024-11-15').capabilities, createObject('name', 'EnableServerless')), null(), createObject('throughput', parameters('throughput')))]", + "resource": { + "id": "[parameters('name')]", + "indexes": "[parameters('indexes')]", + "shardKey": "[parameters('shardKey')]" + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the mongodb database collection." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the mongodb database collection." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases/collections', parameters('databaseAccountName'), parameters('mongodbDatabaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the mongodb database collection was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "mongodbDatabase" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the mongodb database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the mongodb database." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/mongodbDatabases', parameters('databaseAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the mongodb database was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_gremlinDatabases": { + "copy": { + "name": "databaseAccount_gremlinDatabases", + "count": "[length(coalesce(parameters('gremlinDatabases'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-gremlin-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()].name]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "graphs": { + "value": "[tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'graphs')]" + }, + "maxThroughput": { + "value": "[tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'maxThroughput')]" + }, + "throughput": { + "value": "[tryGet(coalesce(parameters('gremlinDatabases'), createArray())[copyIndex()], 'throughput')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "4743052544503629108" + }, + "name": "DocumentDB Database Account Gremlin Databases", + "description": "This module deploys a Gremlin Database within a CosmosDB Account." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Gremlin database." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the Gremlin database resource." + } + }, + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Gremlin database. Required if the template is used in a standalone deployment." + } + }, + "graphs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of graphs to deploy in the Gremlin database." + } + }, + "maxThroughput": { + "type": "int", + "defaultValue": 4000, + "metadata": { + "description": "Optional. Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored. Setting throughput at the database level is only recommended for development/test or when workload across all graphs in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the graph level and not at the database level." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`. Setting throughput at the database level is only recommended for development/test or when workload across all graphs in the shared throughput database is uniform. For best performance for large production workloads, it is recommended to set dedicated throughput (autoscale or manual) at the graph level and not at the database level." + } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "gremlinDatabase": { + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), null()), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', parameters('throughput')))]", + "resource": { + "id": "[parameters('name')]" + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "gremlinDatabase_gremlinGraphs": { + "copy": { + "name": "gremlinDatabase_gremlinGraphs", + "count": "[length(parameters('graphs'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-gremlindb-{1}', uniqueString(deployment().name, parameters('name')), parameters('graphs')[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('graphs')[copyIndex()].name]" + }, + "gremlinDatabaseName": { + "value": "[parameters('name')]" + }, + "databaseAccountName": { + "value": "[parameters('databaseAccountName')]" + }, + "indexingPolicy": { + "value": "[tryGet(parameters('graphs')[copyIndex()], 'indexingPolicy')]" + }, + "partitionKeyPaths": "[if(not(empty(parameters('graphs')[copyIndex()].partitionKeyPaths)), createObject('value', parameters('graphs')[copyIndex()].partitionKeyPaths), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "9587717186996793648" + }, + "name": "DocumentDB Database Accounts Gremlin Databases Graphs", + "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the graph." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the Gremlin graph resource." + } + }, + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Database Account. Required if the template is used in a standalone deployment." + } + }, + "gremlinDatabaseName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Gremlin Database. Required if the template is used in a standalone deployment." + } + }, + "indexingPolicy": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Indexing policy of the graph." + } + }, + "partitionKeyPaths": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of paths using which data within the container can be partitioned." + } + } + }, + "resources": { + "databaseAccount::gremlinDatabase": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('gremlinDatabaseName'))]" + }, + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "gremlinGraph": { + "type": "Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}/{2}', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "resource": { + "id": "[parameters('name')]", + "indexingPolicy": "[if(not(empty(parameters('indexingPolicy'))), parameters('indexingPolicy'), null())]", + "partitionKey": { + "paths": "[if(not(empty(parameters('partitionKeyPaths'))), parameters('partitionKeyPaths'), null())]" + } + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the graph." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the graph." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases/graphs', parameters('databaseAccountName'), parameters('gremlinDatabaseName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the graph was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "gremlinDatabase" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Gremlin database." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Gremlin database." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/gremlinDatabases', parameters('databaseAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Gremlin database was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_tables": { + "copy": { + "name": "databaseAccount_tables", + "count": "[length(coalesce(parameters('tables'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-table-{1}', uniqueString(deployment().name, parameters('location')), coalesce(parameters('tables'), createArray())[copyIndex()].name)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "databaseAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('tables'), createArray())[copyIndex()].name]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "maxThroughput": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'maxThroughput')]" + }, + "throughput": { + "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'throughput')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "14106261468136691896" + }, + "name": "Azure Cosmos DB account tables", + "description": "This module deploys a table within an Azure Cosmos DB Account." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the table." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags for the table." + } + }, + "databaseAccountName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Azure Cosmos DB account. Required if the template is used in a standalone deployment." + } + }, + "maxThroughput": { + "type": "int", + "defaultValue": 4000, + "metadata": { + "description": "Optional. Represents maximum throughput, the resource can scale up to. Cannot be set together with `throughput`. If `throughput` is set to something else than -1, this autoscale setting is ignored." + } + }, + "throughput": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Request Units per second (for example 10000). Cannot be set together with `maxThroughput`." + } + } + }, + "resources": { + "databaseAccount": { + "existing": true, + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-11-15", + "name": "[parameters('databaseAccountName')]" + }, + "table": { + "type": "Microsoft.DocumentDB/databaseAccounts/tables", + "apiVersion": "2024-11-15", + "name": "[format('{0}/{1}', parameters('databaseAccountName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "options": "[if(contains(reference('databaseAccount').capabilities, createObject('name', 'EnableServerless')), createObject(), createObject('autoscaleSettings', if(equals(parameters('throughput'), null()), createObject('maxThroughput', parameters('maxThroughput')), null()), 'throughput', parameters('throughput')))]", + "resource": { + "id": "[parameters('name')]" + } + }, + "dependsOn": [ + "databaseAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the table." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the table." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts/tables', parameters('databaseAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the table was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + }, + "databaseAccount_privateEndpoints": { + "copy": { + "name": "databaseAccount_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-dbAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "15954548978129725136" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.13.18514", + "templateHash": "5440815542537978381" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The resource IDs of the network interfaces associated with the private endpoint." + }, + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + } + } + } + }, + "dependsOn": [ + "databaseAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the database account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the database account." + }, + "value": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the database account was created in." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('databaseAccount', '2024-11-15', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('databaseAccount', '2024-11-15', 'full').location]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The endpoint of the database account." + }, + "value": "[reference('databaseAccount').documentEndpoint]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the database account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('databaseAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + }, + "primaryReadWriteKey": { + "type": "securestring", + "metadata": { + "description": "The primary read-write key." + }, + "value": "[listKeys('databaseAccount', '2024-11-15').primaryMasterKey]" + }, + "primaryReadOnlyKey": { + "type": "securestring", + "metadata": { + "description": "The primary read-only key." + }, + "value": "[listKeys('databaseAccount', '2024-11-15').primaryReadonlyMasterKey]" + }, + "primaryReadWriteConnectionString": { + "type": "securestring", + "metadata": { + "description": "The primary read-write connection string." + }, + "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[0].connectionString]" + }, + "primaryReadOnlyConnectionString": { + "type": "securestring", + "metadata": { + "description": "The primary read-only connection string." + }, + "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[2].connectionString]" + }, + "secondaryReadWriteKey": { + "type": "securestring", + "metadata": { + "description": "The secondary read-write key." + }, + "value": "[listKeys('databaseAccount', '2024-11-15').secondaryMasterKey]" + }, + "secondaryReadOnlyKey": { + "type": "securestring", + "metadata": { + "description": "The secondary read-only key." + }, + "value": "[listKeys('databaseAccount', '2024-11-15').secondaryReadonlyMasterKey]" + }, + "secondaryReadWriteConnectionString": { + "type": "securestring", + "metadata": { + "description": "The secondary read-write connection string." + }, + "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[1].connectionString]" + }, + "secondaryReadOnlyConnectionString": { + "type": "securestring", + "metadata": { + "description": "The secondary read-only connection string." + }, + "value": "[listConnectionStrings('databaseAccount', '2024-11-15').connectionStrings[3].connectionString]" + } + } + } + }, + "dependsOn": [ + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cosmosDB)]", + "network" + ] + }, + "avmAppConfig": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.app-configuration.configuration-store.{0}', variables('appConfigName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('appConfigName')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "sku": { + "value": "Standard" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "disableLocalAuth": { + "value": false + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "App Configuration Data Reader", + "principalType": "ServicePrincipal" + } + ] + }, + "keyValues": { + "value": [ + { + "name": "Application:AIServices:GPT-4o-mini:Endpoint", + "value": "[reference('avmOpenAi').outputs.endpoint.value]" + }, + { + "name": "Application:AIServices:GPT-4o-mini:ModelName", + "value": "[variables('gptModelDeployment').modelName]" + }, + { + "name": "Application:Services:KernelMemory:Endpoint", + "value": "http://kernelmemory-service" + }, + { + "name": "Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Collection", + "value": "ChatHistory" + }, + { + "name": "Application:Services:PersistentStorage:CosmosMongo:Collections:ChatHistory:Database", + "value": "DPS" + }, + { + "name": "Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Collection", + "value": "Documents" + }, + { + "name": "Application:Services:PersistentStorage:CosmosMongo:Collections:DocumentManager:Database", + "value": "DPS" + }, + { + "name": "Application:Services:PersistentStorage:CosmosMongo:ConnectionString", + "value": "[listOutputsWithSecureValues('avmCosmosDB', '2022-09-01').primaryReadWriteConnectionString]" + }, + { + "name": "Application:Services:AzureAISearch:Endpoint", + "value": "[format('https://{0}.search.windows.net', reference('avmSearchSearchServices').outputs.name.value)]" + }, + { + "name": "KernelMemory:Services:AzureAIDocIntel:Auth", + "value": "AzureIdentity" + }, + { + "name": "KernelMemory:Services:AzureAIDocIntel:Endpoint", + "value": "[reference('documentIntelligence').outputs.endpoint.value]" + }, + { + "name": "KernelMemory:Services:AzureAISearch:Auth", + "value": "AzureIdentity" + }, + { + "name": "KernelMemory:Services:AzureAISearch:Endpoint", + "value": "[format('https://{0}.search.windows.net', reference('avmSearchSearchServices').outputs.name.value)]" + }, + { + "name": "KernelMemory:Services:AzureBlobs:Account", + "value": "[reference('avmStorageAccount').outputs.name.value]" + }, + { + "name": "KernelMemory:Services:AzureBlobs:Auth", + "value": "AzureIdentity" + }, + { + "name": "KernelMemory:Services:AzureBlobs:Container", + "value": "smemory" + }, + { + "name": "KernelMemory:Services:AzureOpenAIEmbedding:Auth", + "value": "AzureIdentity" + }, + { + "name": "KernelMemory:Services:AzureOpenAIEmbedding:Deployment", + "value": "[variables('embeddingModelDeployment').deploymentName]" + }, + { + "name": "KernelMemory:Services:AzureOpenAIEmbedding:Endpoint", + "value": "[reference('avmOpenAi').outputs.endpoint.value]" + }, + { + "name": "KernelMemory:Services:AzureOpenAIText:Auth", + "value": "AzureIdentity" + }, + { + "name": "KernelMemory:Services:AzureOpenAIText:Deployment", + "value": "[variables('gptModelDeployment').deploymentName]" + }, + { + "name": "KernelMemory:Services:AzureOpenAIText:Endpoint", + "value": "[reference('avmOpenAi').outputs.endpoint.value]" + }, + { + "name": "KernelMemory:Services:AzureQueues:Account", + "value": "[reference('avmStorageAccount').outputs.name.value]" + }, + { + "name": "KernelMemory:Services:AzureQueues:Auth", + "value": "AzureIdentity" + } + ] + }, + "publicNetworkAccess": { + "value": "Enabled" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "14233469524371710295" + }, + "name": "App Configuration Stores", + "description": "This module deploys an App Configuration Store." + }, + "definitions": { + "dataPlaneProxyType": { + "type": "object", + "properties": { + "authenticationMode": { + "type": "string", + "allowedValues": [ + "Local", + "Pass-through" + ], + "nullable": true, + "metadata": { + "description": "Optional. The data plane proxy authentication mode. This property manages the authentication mode of request to the data plane resources. 'Pass-through' is recommended." + } + }, + "privateLinkDelegation": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Required. The data plane proxy private link delegation. This property manages if a request from delegated Azure Resource Manager (ARM) private link is allowed when the data plane resource requires private link." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the data plane proxy." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "customerManagedKeyWithAutoRotateType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure App Configuration." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "sku": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Free", + "Standard" + ], + "metadata": { + "description": "Optional. Pricing tier of App Configuration." + } + }, + "createMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "Recover" + ], + "metadata": { + "description": "Optional. Indicates whether the configuration store need to be recovered." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Disables all authentication methods other than AAD authentication." + } + }, + "enablePurgeProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Property specifying whether protection against purge is enabled for this configuration store. Defaults to true unless sku is set to Free, since purge protection is not available in Free tier." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set." + } + }, + "softDeleteRetentionInDays": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "maxValue": 7, + "metadata": { + "description": "Optional. The amount of time in days that the configuration store will be retained when it is soft deleted." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "keyValues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All Key / Values to create. Requires local authentication to be enabled." + } + }, + "replicaLocations": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All Replicas to create." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "dataPlaneProxy": { + "$ref": "#/definitions/dataPlaneProxyType", + "nullable": true, + "metadata": { + "description": "Optional. Property specifying the configuration of data plane proxy for Azure Resource Manager (ARM)." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "App Compliance Automation Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f37683f-2463-46b6-9ce7-9b788b988ba2')]", + "App Compliance Automation Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ffc6bbe0-e443-4c3b-bf54-26581bb2f78e')]", + "App Configuration Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b')]", + "App Configuration Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '516239f1-63e1-4d78-a4de-a74fb236a071')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.appconfiguration-configurationstore.{0}.{1}', replace('0.6.3', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "configurationStore": { + "type": "Microsoft.AppConfiguration/configurationStores", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "identity": "[variables('identity')]", + "properties": { + "createMode": "[parameters('createMode')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "enablePurgeProtection": "[if(equals(parameters('sku'), 'Free'), false(), parameters('enablePurgeProtection'))]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keyVaultProperties', createObject('keyIdentifier', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)), 'identityClientId', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), reference('cMKUserAssignedIdentity').clientId, null()))), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(not(empty(parameters('privateEndpoints'))), 'Disabled', 'Enabled'))]", + "softDeleteRetentionInDays": "[if(equals(parameters('sku'), 'Free'), 0, parameters('softDeleteRetentionInDays'))]", + "dataPlaneProxy": "[if(not(empty(parameters('dataPlaneProxy'))), createObject('authenticationMode', coalesce(tryGet(parameters('dataPlaneProxy'), 'authenticationMode'), 'Pass-through'), 'privateLinkDelegation', parameters('dataPlaneProxy').privateLinkDelegation), null())]" + }, + "dependsOn": [ + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "configurationStore_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.AppConfiguration/configurationStores/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_diagnosticSettings": { + "copy": { + "name": "configurationStore_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.AppConfiguration/configurationStores/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_roleAssignments": { + "copy": { + "name": "configurationStore_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.AppConfiguration/configurationStores/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_keyValues": { + "copy": { + "name": "configurationStore_keyValues", + "count": "[length(coalesce(parameters('keyValues'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AppConfig-KeyValues-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appConfigurationName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('keyValues'), createArray())[copyIndex()].name]" + }, + "value": { + "value": "[coalesce(parameters('keyValues'), createArray())[copyIndex()].value]" + }, + "contentType": { + "value": "[tryGet(coalesce(parameters('keyValues'), createArray())[copyIndex()], 'contentType')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('keyValues'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "5084967332926134609" + }, + "name": "App Configuration Stores Key Values", + "description": "This module deploys an App Configuration Store Key Value." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the key." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. The value of the key-value." + } + }, + "appConfigurationName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent app configuration store. Required if the template is used in a standalone deployment." + } + }, + "contentType": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The content type of the key-values value. Providing a proper content-type can enable transformations of values when they are retrieved by applications." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "appConfiguration": { + "existing": true, + "type": "Microsoft.AppConfiguration/configurationStores", + "apiVersion": "2024-05-01", + "name": "[parameters('appConfigurationName')]" + }, + "keyValues": { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('appConfigurationName'), parameters('name'))]", + "properties": { + "contentType": "[parameters('contentType')]", + "tags": "[parameters('tags')]", + "value": "[parameters('value')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the key values." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key values." + }, + "value": "[resourceId('Microsoft.AppConfiguration/configurationStores/keyValues', parameters('appConfigurationName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the batch account was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_replicas": { + "copy": { + "name": "configurationStore_replicas", + "count": "[length(coalesce(parameters('replicaLocations'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AppConfig-Replicas-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appConfigurationName": { + "value": "[parameters('name')]" + }, + "replicaLocation": { + "value": "[coalesce(parameters('replicaLocations'), createArray())[copyIndex()]]" + }, + "name": { + "value": "[format('{0}replica', coalesce(parameters('replicaLocations'), createArray())[copyIndex()])]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "17508622087481054882" + }, + "name": "App Configuration Replicas", + "description": "This module deploys an App Configuration Replica." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the replica." + } + }, + "appConfigurationName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent app configuration store. Required if the template is used in a standalone deployment." + } + }, + "replicaLocation": { + "type": "string", + "metadata": { + "description": "Required. Location of the replica." + } + } + }, + "resources": [ + { + "type": "Microsoft.AppConfiguration/configurationStores/replicas", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('appConfigurationName'), parameters('name'))]", + "location": "[parameters('replicaLocation')]" + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the app configuration was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the replica that was deployed." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the replica that was deployed." + }, + "value": "[resourceId('Microsoft.AppConfiguration/configurationStores/replicas', parameters('appConfigurationName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_privateEndpoints": { + "copy": { + "name": "configurationStore_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-configurationStore-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceIds": { + "type": "array", + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + }, + "value": "[reference('privateEndpoint').networkInterfaces]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "configurationStore" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the app configuration." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the app configuration." + }, + "value": "[resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the app configuration store was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('configurationStore', '2024-05-01', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('configurationStore', '2024-05-01', 'full').location]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The endpoint of the app configuration." + }, + "value": "[reference('configurationStore').endpoint]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the app configuration." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + } + } + } + }, + "dependsOn": [ + "avmCosmosDB", + "avmOpenAi", + "avmSearchSearchServices", + "avmStorageAccount", + "documentIntelligence", + "userAssignedIdentity" + ] + }, + "avmAppConfigUpdated": { + "condition": "[parameters('enablePrivateNetworking')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.app-configuration.configuration-store-update.{0}', variables('appConfigName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('appConfigName')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "sku": { + "value": "Standard" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "disableLocalAuth": { + "value": true + }, + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-appconfig-{0}', variables('solutionSuffix')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('name', 'appconfig-dns-zone-group', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').appConfig)).outputs.resourceId.value))), 'subnetResourceId', reference('network').outputs.subnetPrivateEndpointsResourceId.value))), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "14233469524371710295" + }, + "name": "App Configuration Stores", + "description": "This module deploys an App Configuration Store." + }, + "definitions": { + "dataPlaneProxyType": { + "type": "object", + "properties": { + "authenticationMode": { + "type": "string", + "allowedValues": [ + "Local", + "Pass-through" + ], + "nullable": true, + "metadata": { + "description": "Optional. The data plane proxy authentication mode. This property manages the authentication mode of request to the data plane resources. 'Pass-through' is recommended." + } + }, + "privateLinkDelegation": { + "type": "string", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Required. The data plane proxy private link delegation. This property manages if a request from delegated Azure Resource Manager (ARM) private link is allowed when the data plane resource requires private link." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the data plane proxy." + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "customerManagedKeyWithAutoRotateType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.4.0" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure App Configuration." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "sku": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Free", + "Standard" + ], + "metadata": { + "description": "Optional. Pricing tier of App Configuration." + } + }, + "createMode": { + "type": "string", + "defaultValue": "Default", + "allowedValues": [ + "Default", + "Recover" + ], + "metadata": { + "description": "Optional. Indicates whether the configuration store need to be recovered." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Disables all authentication methods other than AAD authentication." + } + }, + "enablePurgeProtection": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Property specifying whether protection against purge is enabled for this configuration store. Defaults to true unless sku is set to Free, since purge protection is not available in Free tier." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set." + } + }, + "softDeleteRetentionInDays": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "maxValue": 7, + "metadata": { + "description": "Optional. The amount of time in days that the configuration store will be retained when it is soft deleted." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "keyValues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All Key / Values to create. Requires local authentication to be enabled." + } + }, + "replicaLocations": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All Replicas to create." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "dataPlaneProxy": { + "$ref": "#/definitions/dataPlaneProxyType", + "nullable": true, + "metadata": { + "description": "Optional. Property specifying the configuration of data plane proxy for Azure Resource Manager (ARM)." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "App Compliance Automation Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f37683f-2463-46b6-9ce7-9b788b988ba2')]", + "App Compliance Automation Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ffc6bbe0-e443-4c3b-bf54-26581bb2f78e')]", + "App Configuration Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5ae67dd6-50cb-40e7-96ff-dc2bfa4b606b')]", + "App Configuration Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '516239f1-63e1-4d78-a4de-a74fb236a071')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.appconfiguration-configurationstore.{0}.{1}', replace('0.6.3', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "configurationStore": { + "type": "Microsoft.AppConfiguration/configurationStores", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "identity": "[variables('identity')]", + "properties": { + "createMode": "[parameters('createMode')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "enablePurgeProtection": "[if(equals(parameters('sku'), 'Free'), false(), parameters('enablePurgeProtection'))]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keyVaultProperties', createObject('keyIdentifier', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), reference('cMKKeyVault::cMKKey').keyUri, reference('cMKKeyVault::cMKKey').keyUriWithVersion)), 'identityClientId', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), reference('cMKUserAssignedIdentity').clientId, null()))), null())]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(not(empty(parameters('privateEndpoints'))), 'Disabled', 'Enabled'))]", + "softDeleteRetentionInDays": "[if(equals(parameters('sku'), 'Free'), 0, parameters('softDeleteRetentionInDays'))]", + "dataPlaneProxy": "[if(not(empty(parameters('dataPlaneProxy'))), createObject('authenticationMode', coalesce(tryGet(parameters('dataPlaneProxy'), 'authenticationMode'), 'Pass-through'), 'privateLinkDelegation', parameters('dataPlaneProxy').privateLinkDelegation), null())]" + }, + "dependsOn": [ + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "configurationStore_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.AppConfiguration/configurationStores/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_diagnosticSettings": { + "copy": { + "name": "configurationStore_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.AppConfiguration/configurationStores/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_roleAssignments": { + "copy": { + "name": "configurationStore_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.AppConfiguration/configurationStores/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_keyValues": { + "copy": { + "name": "configurationStore_keyValues", + "count": "[length(coalesce(parameters('keyValues'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AppConfig-KeyValues-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appConfigurationName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('keyValues'), createArray())[copyIndex()].name]" + }, + "value": { + "value": "[coalesce(parameters('keyValues'), createArray())[copyIndex()].value]" + }, + "contentType": { + "value": "[tryGet(coalesce(parameters('keyValues'), createArray())[copyIndex()], 'contentType')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('keyValues'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "5084967332926134609" + }, + "name": "App Configuration Stores Key Values", + "description": "This module deploys an App Configuration Store Key Value." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the key." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. The value of the key-value." + } + }, + "appConfigurationName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent app configuration store. Required if the template is used in a standalone deployment." + } + }, + "contentType": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The content type of the key-values value. Providing a proper content-type can enable transformations of values when they are retrieved by applications." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "appConfiguration": { + "existing": true, + "type": "Microsoft.AppConfiguration/configurationStores", + "apiVersion": "2024-05-01", + "name": "[parameters('appConfigurationName')]" + }, + "keyValues": { + "type": "Microsoft.AppConfiguration/configurationStores/keyValues", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('appConfigurationName'), parameters('name'))]", + "properties": { + "contentType": "[parameters('contentType')]", + "tags": "[parameters('tags')]", + "value": "[parameters('value')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the key values." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the key values." + }, + "value": "[resourceId('Microsoft.AppConfiguration/configurationStores/keyValues', parameters('appConfigurationName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the batch account was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_replicas": { + "copy": { + "name": "configurationStore_replicas", + "count": "[length(coalesce(parameters('replicaLocations'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-AppConfig-Replicas-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appConfigurationName": { + "value": "[parameters('name')]" + }, + "replicaLocation": { + "value": "[coalesce(parameters('replicaLocations'), createArray())[copyIndex()]]" + }, + "name": { + "value": "[format('{0}replica', coalesce(parameters('replicaLocations'), createArray())[copyIndex()])]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "17508622087481054882" + }, + "name": "App Configuration Replicas", + "description": "This module deploys an App Configuration Replica." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the replica." + } + }, + "appConfigurationName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent app configuration store. Required if the template is used in a standalone deployment." + } + }, + "replicaLocation": { + "type": "string", + "metadata": { + "description": "Required. Location of the replica." + } + } + }, + "resources": [ + { + "type": "Microsoft.AppConfiguration/configurationStores/replicas", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('appConfigurationName'), parameters('name'))]", + "location": "[parameters('replicaLocation')]" + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the app configuration was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the replica that was deployed." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the replica that was deployed." + }, + "value": "[resourceId('Microsoft.AppConfiguration/configurationStores/replicas', parameters('appConfigurationName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "configurationStore" + ] + }, + "configurationStore_privateEndpoints": { + "copy": { + "name": "configurationStore_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-configurationStore-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'configurationStores')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceIds": { + "type": "array", + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + }, + "value": "[reference('privateEndpoint').networkInterfaces]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "configurationStore" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the app configuration." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the app configuration." + }, + "value": "[resourceId('Microsoft.AppConfiguration/configurationStores', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the app configuration store was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('configurationStore', '2024-05-01', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('configurationStore', '2024-05-01', 'full').location]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The endpoint of the app configuration." + }, + "value": "[reference('configurationStore').endpoint]" + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the app configuration." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('configurationStore_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + } + } + } + }, + "dependsOn": [ + "avmAppConfig", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').appConfig)]", + "network" + ] + }, + "avmStorageAccount": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.storage.storage-account.{0}', variables('storageAccountName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('storageAccountName')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "minimumTlsVersion": { + "value": "TLS1_2" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "accessTier": { + "value": "Hot" + }, + "supportsHttpsTrafficOnly": { + "value": true + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "Storage Blob Data Contributor", + "principalType": "ServicePrincipal" + } + ] + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "[if(parameters('enablePrivateNetworking'), 'Deny', 'Allow')]" + } + }, + "allowBlobPublicAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', true()), createObject('value', false()))]", + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-blob-{0}', variables('solutionSuffix')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('name', 'storage-dns-zone-group-blob', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageBlob)).outputs.resourceId.value))), 'subnetResourceId', reference('network').outputs.subnetPrivateEndpointsResourceId.value, 'service', 'blob'), createObject('name', format('pep-queue-{0}', variables('solutionSuffix')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('name', 'storage-dns-zone-group-queue', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageQueue)).outputs.resourceId.value))), 'subnetResourceId', reference('network').outputs.subnetPrivateEndpointsResourceId.value, 'service', 'queue'))), createObject('value', createArray()))]", + "blobServices": { + "value": { + "corsRules": [], + "deleteRetentionPolicyEnabled": false, + "containers": [ + { + "name": "data", + "publicAccess": "None" + } + ] + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "13086360467000063396" + }, + "name": "Storage Accounts", + "description": "This module deploys a Storage Account." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "networkAclsType": { + "type": "object", + "properties": { + "resourceAccessRules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The ID of the tenant in which the resource resides in." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target service. Can also contain a wildcard, if multiple services e.g. in a resource group should be included." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Sets the resource access rules. Array entries must consist of \"tenantId\" and \"resourceId\" fields only." + } + }, + "bypass": { + "type": "string", + "allowedValues": [ + "AzureServices", + "AzureServices, Logging", + "AzureServices, Logging, Metrics", + "AzureServices, Metrics", + "Logging", + "Logging, Metrics", + "Metrics", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Possible values are any combination of Logging,Metrics,AzureServices (For example, \"Logging, Metrics\"), or None to bypass none of those traffics." + } + }, + "virtualNetworkRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the virtual network rules." + } + }, + "ipRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the IP ACL rules." + } + }, + "defaultAction": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the default action of allow or deny when no other rules match." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey1 secret name to create." + } + }, + "connectionString1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString1 secret name to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey2 secret name to create." + } + }, + "connectionString2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString2 secret name to create." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "localUserType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the local user used for SFTP Authentication." + } + }, + "hasSharedKey": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." + } + }, + "hasSshKey": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." + } + }, + "hasSshPassword": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + } + }, + "homeDirectory": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The local user home directory." + } + }, + "permissionScopes": { + "type": "array", + "items": { + "$ref": "#/definitions/permissionScopeType" + }, + "metadata": { + "description": "Required. The permission scopes of the local user." + } + }, + "sshAuthorizedKeys": { + "type": "array", + "items": { + "$ref": "#/definitions/sshAuthorizedKeyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The local user SSH authorized keys for SFTP." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "customerManagedKeyWithAutoRotateType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using version as per 'autoRotationEnabled' setting." + } + }, + "autoRotationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable auto-rotating to the latest key version. Default is `true`. If set to `false`, the latest key version at the time of the deployment is used." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type supports auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "permissionScopeType": { + "type": "object", + "properties": { + "permissions": { + "type": "string", + "metadata": { + "description": "Required. The permissions for the local user. Possible values include: Read (r), Write (w), Delete (d), List (l), and Create (c)." + } + }, + "resourceName": { + "type": "string", + "metadata": { + "description": "Required. The name of resource, normally the container name or the file share name, used by the local user." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The service used by the local user, e.g. blob, file." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "local-user/main.bicep" + } + } + }, + "privateEndpointMultiServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\" for a Storage Account's Private Endpoints." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can NOT be assumed (i.e., for services that have more than one subresource, like Storage Account with Blob (blob, table, queue, file, ...).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "sshAuthorizedKeyType": { + "type": "object", + "properties": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description used to store the function/usage of the key." + } + }, + "key": { + "type": "securestring", + "metadata": { + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "local-user/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Name of the Storage Account. Must be lower-case." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2", + "allowedValues": [ + "Storage", + "StorageV2", + "BlobStorage", + "FileStorage", + "BlockBlobStorage" + ], + "metadata": { + "description": "Optional. Type of Storage Account to create." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard_GRS", + "allowedValues": [ + "Standard_LRS", + "Standard_GRS", + "Standard_RAGRS", + "Standard_ZRS", + "Premium_LRS", + "Premium_ZRS", + "Standard_GZRS", + "Standard_RAGZRS" + ], + "metadata": { + "description": "Optional. Storage Account Sku Name." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Premium", + "Hot", + "Cool", + "Cold" + ], + "metadata": { + "description": "Conditional. Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The \"Premium\" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type." + } + }, + "largeFileSharesState": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Allow large file shares if sets to 'Enabled'. It cannot be disabled once it is enabled. Only supported on locally redundant and zone redundant file shares. It cannot be set on FileStorage storage accounts (storage accounts for premium file shares)." + } + }, + "azureFilesIdentityBasedAuthentication": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Storage/storageAccounts@2024-01-01#properties/properties/properties/azureFilesIdentityBasedAuthentication" + }, + "description": "Optional. Provides the identity based authentication settings for Azure Files." + }, + "nullable": true + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not." + } + }, + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointMultiServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "managementPolicyRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The Storage Account ManagementPolicies Rules." + } + }, + "networkAcls": { + "$ref": "#/definitions/networkAclsType", + "nullable": true, + "metadata": { + "description": "Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny." + } + }, + "requireInfrastructureEncryption": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true." + } + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Allow or disallow cross AAD tenant object replication." + } + }, + "customDomainName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source." + } + }, + "customDomainUseSubDomainName": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether indirect CName validation is enabled. This should only be set on updates." + } + }, + "dnsEndpointType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "AzureDnsZone", + "Standard" + ], + "metadata": { + "description": "Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier." + } + }, + "blobServices": { + "type": "object", + "defaultValue": "[if(not(equals(parameters('kind'), 'FileStorage')), createObject('containerDeleteRetentionPolicyEnabled', true(), 'containerDeleteRetentionPolicyDays', 7, 'deleteRetentionPolicyEnabled', true(), 'deleteRetentionPolicyDays', 6), createObject())]", + "metadata": { + "description": "Optional. Blob service and containers to deploy." + } + }, + "fileServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. File service and shares to deploy." + } + }, + "queueServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Queue service and queues to create." + } + }, + "tableServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table service and tables to create." + } + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2", + "allowedValues": [ + "TLS1_2" + ], + "metadata": { + "description": "Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore." + } + }, + "enableHierarchicalNamespace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true." + } + }, + "enableSftp": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "localUsers": { + "type": "array", + "items": { + "$ref": "#/definitions/localUserType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Local users to deploy for SFTP authentication." + } + }, + "isLocalUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables local users feature, if set to true." + } + }, + "enableNfsV3": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Storage/storageAccounts@2024-01-01#properties/tags" + }, + "description": "Optional. Tags of the resource." + }, + "nullable": true + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "allowedCopyScope": { + "type": "string", + "nullable": true, + "allowedValues": [ + "AAD", + "PrivateLink" + ], + "metadata": { + "description": "Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "supportsHttpsTrafficOnly": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyWithAutoRotateType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "sasExpirationPeriod": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The SAS expiration period. DD.HH:MM:SS." + } + }, + "sasExpirationAction": { + "type": "string", + "defaultValue": "Log", + "allowedValues": [ + "Block", + "Log" + ], + "metadata": { + "description": "Optional. The SAS expiration action. Allowed values are Block and Log." + } + }, + "keyType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Account", + "Service" + ], + "metadata": { + "description": "Optional. The keyType to use with Queue & Table services." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "supportsBlobService": "[or(or(or(equals(parameters('kind'), 'BlockBlobStorage'), equals(parameters('kind'), 'BlobStorage')), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "supportsFileService": "[or(or(equals(parameters('kind'), 'FileStorage'), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "Storage File Data Privileged Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69566ab7-960f-475b-8e7c-b3118f30c6bd')]", + "Storage File Data Privileged Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b8eda974-7b85-4f76-af95-65846b26df6d')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.20.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2024-11-30", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "storageAccount": { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": "[shallowMerge(createArray(createObject('allowSharedKeyAccess', parameters('allowSharedKeyAccess'), 'defaultToOAuthAuthentication', parameters('defaultToOAuthAuthentication'), 'allowCrossTenantReplication', parameters('allowCrossTenantReplication'), 'allowedCopyScope', parameters('allowedCopyScope'), 'customDomain', createObject('name', parameters('customDomainName'), 'useSubDomainName', parameters('customDomainUseSubDomainName')), 'dnsEndpointType', parameters('dnsEndpointType'), 'isLocalUserEnabled', parameters('isLocalUserEnabled'), 'encryption', union(createObject('keySource', if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage'), 'services', createObject('blob', if(variables('supportsBlobService'), createObject('enabled', true()), null()), 'file', if(variables('supportsFileService'), createObject('enabled', true()), null()), 'table', createObject('enabled', true(), 'keyType', parameters('keyType')), 'queue', createObject('enabled', true(), 'keyType', parameters('keyType'))), 'keyvaultproperties', if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(tryGet(parameters('customerManagedKey'), 'keyVersion'))), parameters('customerManagedKey').keyVersion, if(coalesce(tryGet(parameters('customerManagedKey'), 'autoRotationEnabled'), true()), null(), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'identity', createObject('userAssignedIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2], split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))), null()))), if(parameters('requireInfrastructureEncryption'), createObject('requireInfrastructureEncryption', if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())), createObject())), 'accessTier', if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null()), 'sasPolicy', if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', parameters('sasExpirationAction'), 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null()), 'supportsHttpsTrafficOnly', parameters('supportsHttpsTrafficOnly'), 'isHnsEnabled', parameters('enableHierarchicalNamespace'), 'isSftpEnabled', parameters('enableSftp'), 'isNfsV3Enabled', if(parameters('enableNfsV3'), parameters('enableNfsV3'), ''), 'largeFileSharesState', if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null()), 'minimumTlsVersion', parameters('minimumTlsVersion'), 'networkAcls', if(not(empty(parameters('networkAcls'))), union(createObject('resourceAccessRules', tryGet(parameters('networkAcls'), 'resourceAccessRules'), 'defaultAction', coalesce(tryGet(parameters('networkAcls'), 'defaultAction'), 'Deny'), 'virtualNetworkRules', tryGet(parameters('networkAcls'), 'virtualNetworkRules'), 'ipRules', tryGet(parameters('networkAcls'), 'ipRules')), if(contains(parameters('networkAcls'), 'bypass'), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass')), createObject())), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny')), 'allowBlobPublicAccess', parameters('allowBlobPublicAccess'), 'publicNetworkAccess', if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))), if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), createObject('azureFilesIdentityBasedAuthentication', parameters('azureFilesIdentityBasedAuthentication')), createObject())))]", + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey" + ] + }, + "storageAccount_diagnosticSettings": { + "copy": { + "name": "storageAccount_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_roleAssignments": { + "copy": { + "name": "storageAccount_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_privateEndpoints": { + "copy": { + "name": "storageAccount_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-sa-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The resource IDs of the network interfaces associated with the private endpoint." + }, + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_managementPolicies": { + "condition": "[not(empty(coalesce(parameters('managementPolicyRules'), createArray())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-ManagementPolicies', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "rules": { + "value": "[parameters('managementPolicyRules')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "11585123047105458062" + }, + "name": "Storage Account Management Policies", + "description": "This module deploys a Storage Account Management Policy." + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "rules": { + "type": "array", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Storage/storageAccounts/managementPolicies@2024-01-01#properties/properties/properties/policy/properties/rules" + }, + "description": "Required. The Storage Account ManagementPolicies Rules." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/managementPolicies", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "properties": { + "policy": { + "rules": "[parameters('rules')]" + } + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed management policy." + }, + "value": "default" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed management policy." + }, + "value": "default" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed management policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount", + "storageAccount_blobServices" + ] + }, + "storageAccount_localUsers": { + "copy": { + "name": "storageAccount_localUsers", + "count": "[length(coalesce(parameters('localUsers'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-LocalUsers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].name]" + }, + "hasSshKey": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshKey]" + }, + "hasSshPassword": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].hasSshPassword]" + }, + "permissionScopes": { + "value": "[coalesce(parameters('localUsers'), createArray())[copyIndex()].permissionScopes]" + }, + "hasSharedKey": { + "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'hasSharedKey')]" + }, + "homeDirectory": { + "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'homeDirectory')]" + }, + "sshAuthorizedKeys": { + "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'sshAuthorizedKeys')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "18350684375691178826" + }, + "name": "Storage Account Local Users", + "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." + }, + "definitions": { + "sshAuthorizedKeyType": { + "type": "object", + "properties": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description used to store the function/usage of the key." + } + }, + "key": { + "type": "securestring", + "metadata": { + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "permissionScopeType": { + "type": "object", + "properties": { + "permissions": { + "type": "string", + "metadata": { + "description": "Required. The permissions for the local user. Possible values include: Read (r), Write (w), Delete (d), List (l), and Create (c)." + } + }, + "resourceName": { + "type": "string", + "metadata": { + "description": "Required. The name of resource, normally the container name or the file share name, used by the local user." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The service used by the local user, e.g. blob, file." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the local user used for SFTP Authentication." + } + }, + "hasSharedKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." + } + }, + "hasSshKey": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." + } + }, + "hasSshPassword": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + } + }, + "homeDirectory": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The local user home directory." + } + }, + "permissionScopes": { + "type": "array", + "items": { + "$ref": "#/definitions/permissionScopeType" + }, + "metadata": { + "description": "Required. The permission scopes of the local user." + } + }, + "sshAuthorizedKeys": { + "type": "array", + "items": { + "$ref": "#/definitions/sshAuthorizedKeyType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The local user SSH authorized keys for SFTP." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "localUsers": { + "type": "Microsoft.Storage/storageAccounts/localUsers", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "hasSharedKey": "[parameters('hasSharedKey')]", + "hasSshKey": "[parameters('hasSshKey')]", + "hasSshPassword": "[parameters('hasSshPassword')]", + "homeDirectory": "[parameters('homeDirectory')]", + "permissionScopes": "[parameters('permissionScopes')]", + "sshAuthorizedKeys": "[parameters('sshAuthorizedKeys')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed local user." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed local user." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed local user." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/localUsers', parameters('storageAccountName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_blobServices": { + "condition": "[not(empty(parameters('blobServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-BlobServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "containers": { + "value": "[tryGet(parameters('blobServices'), 'containers')]" + }, + "automaticSnapshotPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'automaticSnapshotPolicyEnabled')]" + }, + "changeFeedEnabled": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedEnabled')]" + }, + "changeFeedRetentionInDays": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedRetentionInDays')]" + }, + "containerDeleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyEnabled')]" + }, + "containerDeleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyDays')]" + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyAllowPermanentDelete')]" + }, + "corsRules": { + "value": "[tryGet(parameters('blobServices'), 'corsRules')]" + }, + "defaultServiceVersion": { + "value": "[tryGet(parameters('blobServices'), 'defaultServiceVersion')]" + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyAllowPermanentDelete')]" + }, + "deleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyEnabled')]" + }, + "deleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyDays')]" + }, + "isVersioningEnabled": { + "value": "[tryGet(parameters('blobServices'), 'isVersioningEnabled')]" + }, + "lastAccessTimeTrackingPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'lastAccessTimeTrackingPolicyEnabled')]" + }, + "restorePolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyEnabled')]" + }, + "restorePolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyDays')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('blobServices'), 'diagnosticSettings')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "6864791231608714221" + }, + "name": "Storage Account blob Services", + "description": "This module deploys a Storage Account Blob Service." + }, + "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "automaticSnapshotPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Automatic Snapshot is enabled if set to true." + } + }, + "changeFeedEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for change feed events. Indicates whether change feed event logging is enabled for the Blob service." + } + }, + "changeFeedRetentionInDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 146000, + "metadata": { + "description": "Optional. Indicates whether change feed event logging is enabled for the Blob service. Indicates the duration of changeFeed retention in days. If left blank, it indicates an infinite retention of the change feed." + } + }, + "containerDeleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for container soft delete. Indicates whether DeleteRetentionPolicy is enabled." + } + }, + "containerDeleteRetentionPolicyDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted item should be retained." + } + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, + "defaultServiceVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions." + } + }, + "deleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for blob soft delete." + } + }, + "deleteRetentionPolicyDays": { + "type": "int", + "defaultValue": 7, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted blob should be retained." + } + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "isVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use versioning to automatically maintain previous versions of your blobs." + } + }, + "lastAccessTimeTrackingPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service property to configure last access time based tracking policy. When set to true last access time based tracking is enabled." + } + }, + "restorePolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for blob restore policy. If point-in-time restore is enabled, then versioning, change feed, and blob soft delete must also be enabled." + } + }, + "restorePolicyDays": { + "type": "int", + "defaultValue": 7, + "minValue": 1, + "metadata": { + "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." + } + }, + "containers": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Blob containers to create." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "blobServices": { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "automaticSnapshotPolicyEnabled": "[parameters('automaticSnapshotPolicyEnabled')]", + "changeFeed": "[if(parameters('changeFeedEnabled'), createObject('enabled', true(), 'retentionInDays', parameters('changeFeedRetentionInDays')), null())]", + "containerDeleteRetentionPolicy": { + "enabled": "[parameters('containerDeleteRetentionPolicyEnabled')]", + "days": "[parameters('containerDeleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" + }, + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", + "defaultServiceVersion": "[parameters('defaultServiceVersion')]", + "deleteRetentionPolicy": { + "enabled": "[parameters('deleteRetentionPolicyEnabled')]", + "days": "[parameters('deleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(and(parameters('deleteRetentionPolicyEnabled'), parameters('deleteRetentionPolicyAllowPermanentDelete')), true(), null())]" + }, + "isVersioningEnabled": "[parameters('isVersioningEnabled')]", + "lastAccessTimeTrackingPolicy": "[if(not(equals(reference('storageAccount', '2024-01-01', 'full').kind, 'Storage')), createObject('enable', parameters('lastAccessTimeTrackingPolicyEnabled'), 'name', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 'AccessTimeTracking', null()), 'trackingGranularityInDays', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 1, null())), null())]", + "restorePolicy": "[if(parameters('restorePolicyEnabled'), createObject('enabled', true(), 'days', parameters('restorePolicyDays')), null())]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "blobServices_diagnosticSettings": { + "copy": { + "name": "blobServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "blobServices" + ] + }, + "blobServices_container": { + "copy": { + "name": "blobServices_container", + "count": "[length(coalesce(parameters('containers'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Container-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "blobServiceName": { + "value": "[variables('name')]" + }, + "name": { + "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" + }, + "defaultEncryptionScope": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultEncryptionScope')]" + }, + "denyEncryptionScopeOverride": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'denyEncryptionScopeOverride')]" + }, + "enableNfsV3AllSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3AllSquash')]" + }, + "enableNfsV3RootSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3RootSquash')]" + }, + "immutableStorageWithVersioningEnabled": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutableStorageWithVersioningEnabled')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'metadata')]" + }, + "publicAccess": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'publicAccess')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "immutabilityPolicyProperties": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutabilityPolicyProperties')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "16608863835956278253" + }, + "name": "Storage Account Blob Containers", + "description": "This module deploys a Storage Account Blob Container." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "blobServiceName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage container to deploy." + } + }, + "defaultEncryptionScope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Default the container to use specified encryption scope for all writes." + } + }, + "denyEncryptionScopeOverride": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Block override of encryption scope from the container default." + } + }, + "enableNfsV3AllSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 all squash on blob container." + } + }, + "enableNfsV3RootSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 root squash on blob container." + } + }, + "immutableStorageWithVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This is an immutable property, when set to true it enables object level immutability at the container level. The property is immutable and can only be set to true at the container creation time. Existing containers must undergo a migration process." + } + }, + "immutabilityPolicyName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. Name of the immutable policy." + } + }, + "immutabilityPolicyProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Configure immutability policy." + } + }, + "metadata": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Storage/storageAccounts/blobServices/containers@2024-01-01#properties/properties/properties/metadata" + }, + "description": "Optional. A name-value pair to associate with the container as metadata." + }, + "defaultValue": {} + }, + "publicAccess": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "Container", + "Blob", + "None" + ], + "metadata": { + "description": "Optional. Specifies whether data in the container may be accessed publicly and the level of access." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::blobServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('blobServiceName'))]" + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "container": { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", + "properties": { + "defaultEncryptionScope": "[parameters('defaultEncryptionScope')]", + "denyEncryptionScopeOverride": "[parameters('denyEncryptionScopeOverride')]", + "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", + "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", + "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", + "metadata": "[parameters('metadata')]", + "publicAccess": "[parameters('publicAccess')]" + } + }, + "container_roleAssignments": { + "copy": { + "name": "container_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "container" + ] + }, + "immutabilityPolicy": { + "condition": "[not(empty(coalesce(parameters('immutabilityPolicyProperties'), createObject())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('immutabilityPolicyName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "containerName": { + "value": "[parameters('name')]" + }, + "immutabilityPeriodSinceCreationInDays": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'immutabilityPeriodSinceCreationInDays')]" + }, + "allowProtectedAppendWrites": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWrites')]" + }, + "allowProtectedAppendWritesAll": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWritesAll')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "16507112099495773673" + }, + "name": "Storage Account Blob Container Immutability Policies", + "description": "This module deploys a Storage Account Blob Container Immutability Policy." + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "containerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment." + } + }, + "immutabilityPeriodSinceCreationInDays": { + "type": "int", + "defaultValue": 365, + "metadata": { + "description": "Optional. The immutability period for the blobs in the container since the policy creation, in days." + } + }, + "allowProtectedAppendWrites": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API." + } + }, + "allowProtectedAppendWritesAll": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}/{2}/{3}', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]", + "properties": { + "immutabilityPeriodSinceCreationInDays": "[parameters('immutabilityPeriodSinceCreationInDays')]", + "allowProtectedAppendWrites": "[parameters('allowProtectedAppendWrites')]", + "allowProtectedAppendWritesAll": "[parameters('allowProtectedAppendWritesAll')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed immutability policy." + }, + "value": "default" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed immutability policy." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed immutability policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "container" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed container." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed container." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed container." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "blobServices" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed blob service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_fileServices": { + "condition": "[not(empty(parameters('fileServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-FileServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('fileServices'), 'diagnosticSettings')]" + }, + "protocolSettings": { + "value": "[tryGet(parameters('fileServices'), 'protocolSettings')]" + }, + "shareDeleteRetentionPolicy": { + "value": "[tryGet(parameters('fileServices'), 'shareDeleteRetentionPolicy')]" + }, + "shares": { + "value": "[tryGet(parameters('fileServices'), 'shares')]" + }, + "corsRules": { + "value": "[tryGet(parameters('queueServices'), 'corsRules')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "16585885324390135986" + }, + "name": "Storage Account File Share Services", + "description": "This module deploys a Storage Account File Share Service." + }, + "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the file service." + } + }, + "protocolSettings": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Storage/storageAccounts/fileServices@2024-01-01#properties/properties/properties/protocolSettings" + }, + "description": "Optional. Protocol settings for file service." + }, + "defaultValue": {} + }, + "shareDeleteRetentionPolicy": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Storage/storageAccounts/fileServices@2024-01-01#properties/properties/properties/shareDeleteRetentionPolicy" + }, + "description": "Optional. The service properties for soft delete." + }, + "defaultValue": { + "enabled": true, + "days": 7 + } + }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "shares": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. File shares to create." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "fileServices": { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]", + "protocolSettings": "[parameters('protocolSettings')]", + "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" + } + }, + "fileServices_diagnosticSettings": { + "copy": { + "name": "fileServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/fileServices/{1}', parameters('storageAccountName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "fileServices" + ] + }, + "fileServices_shares": { + "copy": { + "name": "fileServices_shares", + "count": "[length(coalesce(parameters('shares'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-shares-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "fileServicesName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('shares'), createArray())[copyIndex()].name]" + }, + "accessTier": { + "value": "[coalesce(tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'accessTier'), if(equals(reference('storageAccount', '2024-01-01', 'full').kind, 'FileStorage'), 'Premium', 'TransactionOptimized'))]" + }, + "enabledProtocols": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'enabledProtocols')]" + }, + "rootSquash": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'rootSquash')]" + }, + "shareQuota": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'shareQuota')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "190690872747761309" + }, + "name": "Storage Account File Shares", + "description": "This module deploys a Storage Account File Share." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "fileServicesName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Conditional. The name of the parent file service. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the file share to create." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "TransactionOptimized", + "allowedValues": [ + "Premium", + "Hot", + "Cool", + "TransactionOptimized" + ], + "metadata": { + "description": "Conditional. Access tier for specific share. Required if the Storage Account kind is set to FileStorage (should be set to \"Premium\"). GpV2 account can choose between TransactionOptimized (default), Hot, and Cool." + } + }, + "shareQuota": { + "type": "int", + "defaultValue": 5120, + "metadata": { + "description": "Optional. The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5120 (5TB). For Large File Shares, the maximum size is 102400 (100TB)." + } + }, + "enabledProtocols": { + "type": "string", + "defaultValue": "SMB", + "allowedValues": [ + "NFS", + "SMB" + ], + "metadata": { + "description": "Optional. The authentication protocol that is used for the file share. Can only be specified when creating a share." + } + }, + "rootSquash": { + "type": "string", + "defaultValue": "NoRootSquash", + "allowedValues": [ + "AllSquash", + "NoRootSquash", + "RootSquash" + ], + "metadata": { + "description": "Optional. Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::fileService": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]" + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "fileShare": { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "shareQuota": "[parameters('shareQuota')]", + "rootSquash": "[if(equals(parameters('enabledProtocols'), 'NFS'), parameters('rootSquash'), null())]", + "enabledProtocols": "[parameters('enabledProtocols')]" + } + }, + "fileShare_roleAssignments": { + "copy": { + "name": "fileShare_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Share-Rbac-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "scope": { + "value": "[replace(resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name')), '/shares/', '/fileshares/')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]" + }, + "roleDefinitionId": { + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" + }, + "principalId": { + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]" + }, + "principalType": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]" + }, + "condition": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]" + }, + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), createObject('value', coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0')), createObject('value', null()))]", + "delegatedManagedIdentityResourceId": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "description": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "scope": { + "type": "string", + "metadata": { + "description": "Required. The scope to deploy the role assignment to." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the role assignment." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The role definition Id to assign." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User", + "" + ], + "defaultValue": "", + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"" + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "defaultValue": "2.0", + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[parameters('scope')]", + "name": "[parameters('name')]", + "properties": { + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "principalId": "[parameters('principalId')]", + "description": "[parameters('description')]", + "principalType": "[if(not(empty(parameters('principalType'))), parameters('principalType'), null())]", + "condition": "[if(not(empty(parameters('condition'))), parameters('condition'), null())]", + "conditionVersion": "[if(and(not(empty(parameters('conditionVersion'))), not(empty(parameters('condition')))), parameters('conditionVersion'), null())]", + "delegatedManagedIdentityResourceId": "[if(not(empty(parameters('delegatedManagedIdentityResourceId'))), parameters('delegatedManagedIdentityResourceId'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "fileShare" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "fileServices", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices', parameters('storageAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_queueServices": { + "condition": "[not(empty(parameters('queueServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-QueueServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('queueServices'), 'diagnosticSettings')]" + }, + "queues": { + "value": "[tryGet(parameters('queueServices'), 'queues')]" + }, + "corsRules": { + "value": "[tryGet(parameters('queueServices'), 'corsRules')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "15089132876669102729" + }, + "name": "Storage Account Queue Services", + "description": "This module deploys a Storage Account Queue Service." + }, + "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "queues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Queues to create." + } + }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "queueServices": { + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + } + }, + "queueServices_diagnosticSettings": { + "copy": { + "name": "queueServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "queueServices" + ] + }, + "queueServices_queues": { + "copy": { + "name": "queueServices_queues", + "count": "[length(coalesce(parameters('queues'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Queue-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('queues'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'metadata')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "9203389950224823099" + }, + "name": "Storage Account Queues", + "description": "This module deploys a Storage Account Queue." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage queue to deploy." + } + }, + "metadata": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Storage/storageAccounts/queueServices/queues@2024-01-01#properties/properties/properties/metadata" + }, + "description": "Optional. A name-value pair that represents queue metadata." + }, + "defaultValue": {} + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::queueServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "queue": { + "type": "Microsoft.Storage/storageAccounts/queueServices/queues", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]" + } + }, + "queue_roleAssignments": { + "copy": { + "name": "queue_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}/queues/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "queue" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed queue." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed queue." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed queue." + }, + "value": "[resourceGroup().name]" + } + } + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_tableServices": { + "condition": "[not(empty(parameters('tableServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-TableServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('tableServices'), 'diagnosticSettings')]" + }, + "tables": { + "value": "[tryGet(parameters('tableServices'), 'tables')]" + }, + "corsRules": { + "value": "[tryGet(parameters('tableServices'), 'corsRules')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "17345564162551993063" + }, + "name": "Storage Account Table Services", + "description": "This module deploys a Storage Account Table Service." + }, + "definitions": { + "corsRuleType": { + "type": "object", + "properties": { + "allowedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of headers allowed to be part of the cross-origin request." + } + }, + "allowedMethods": { + "type": "array", + "allowedValues": [ + "CONNECT", + "DELETE", + "GET", + "HEAD", + "MERGE", + "OPTIONS", + "PATCH", + "POST", + "PUT", + "TRACE" + ], + "metadata": { + "description": "Required. A list of HTTP methods that are allowed to be executed by the origin." + } + }, + "allowedOrigins": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of origin domains that will be allowed via CORS, or \"*\" to allow all domains." + } + }, + "exposedHeaders": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of response headers to expose to CORS clients." + } + }, + "maxAgeInSeconds": { + "type": "int", + "metadata": { + "description": "Required. The number of seconds that the client/browser should cache a preflight response." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cors rule." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. tables to create." + } + }, + "corsRules": { + "type": "array", + "items": { + "$ref": "#/definitions/corsRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The List of CORS rules. You can include up to five CorsRule elements in the request." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "tableServices": { + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "cors": "[if(not(equals(parameters('corsRules'), null())), createObject('corsRules', parameters('corsRules')), null())]" + } + }, + "tableServices_diagnosticSettings": { + "copy": { + "name": "tableServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "tableServices" + ] + }, + "tableServices_tables": { + "copy": { + "name": "tableServices_tables", + "count": "[length(parameters('tables'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Table-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('tables')[copyIndex()].name]" + }, + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "6286190839827082273" + }, + "name": "Storage Account Table", + "description": "This module deploys a Storage Account Table." + }, + "definitions": { + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the table." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Storage Account Backup Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e5e2a7ff-d759-4cd2-bb51-3152d37e2eb1')]", + "Storage Account Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::tableServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2024-01-01", + "name": "[parameters('storageAccountName')]" + }, + "table": { + "type": "Microsoft.Storage/storageAccounts/tableServices/tables", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}/tables/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "table" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed table service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed table service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed table service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('storageAccount', '2024-01-01').keys[0].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'connectionString1Name'), 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys('storageAccount', '2024-01-01').keys[0].value, environment().suffixes.storage))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('storageAccount', '2024-01-01').keys[1].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'connectionString2Name'), 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys('storageAccount', '2024-01-01').keys[1].value, environment().suffixes.storage))), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.35.1.17967", + "templateHash": "15126360152170162999" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage account." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed storage account." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed storage account." + }, + "value": "[resourceGroup().name]" + }, + "primaryBlobEndpoint": { + "type": "string", + "metadata": { + "description": "The primary blob endpoint reference if blob services are deployed." + }, + "value": "[if(and(not(empty(parameters('blobServices'))), contains(parameters('blobServices'), 'containers')), reference(format('Microsoft.Storage/storageAccounts/{0}', parameters('name')), '2019-04-01').primaryEndpoints.blob, '')]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('storageAccount', '2024-01-01', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('storageAccount', '2024-01-01', 'full').location]" + }, + "serviceEndpoints": { + "type": "object", + "metadata": { + "description": "All service endpoints of the deployed storage account, Note Standard_LRS and Standard_ZRS accounts only have a blob service endpoint." + }, + "value": "[reference('storageAccount').primaryEndpoints]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the Storage Account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('storageAccount_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "primaryAccessKey": { + "type": "securestring", + "metadata": { + "description": "The primary access key of the storage account." + }, + "value": "[listKeys('storageAccount', '2024-01-01').keys[0].value]" + }, + "secondayAccessKey": { + "type": "securestring", + "metadata": { + "description": "The secondary access key of the storage account." + }, + "value": "[listKeys('storageAccount', '2024-01-01').keys[1].value]" + }, + "primaryConnectionString": { + "type": "securestring", + "metadata": { + "description": "The primary connection string of the storage account." + }, + "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys('storageAccount', '2024-01-01').keys[0].value, environment().suffixes.storage)]" + }, + "secondaryConnectionString": { + "type": "securestring", + "metadata": { + "description": "The secondary connection string of the storage account." + }, + "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', parameters('name'), listKeys('storageAccount', '2024-01-01').keys[1].value, environment().suffixes.storage)]" + } + } + } + }, + "dependsOn": [ + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageBlob)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageQueue)]", + "network", + "userAssignedIdentity" + ] + }, + "avmSearchSearchServices": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.cognitive-search-services.{0}', variables('aiSearchName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('aiSearchName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', null()))]", + "sku": "[if(parameters('enableScalability'), createObject('value', 'standard'), createObject('value', 'basic'))]", + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[reference('userAssignedIdentity').outputs.resourceId.value]" + ] + } + }, + "replicaCount": { + "value": 1 + }, + "partitionCount": { + "value": 1 + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Search Index Data Contributor", + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "principalType": "ServicePrincipal" + }, + { + "roleDefinitionIdOrName": "Search Index Data Reader", + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "principalType": "ServicePrincipal" + } + ] + }, + "semanticSearch": { + "value": "free" + }, + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-{0}', variables('aiSearchName')), 'customNetworkInterfaceName', format('nic-{0}', variables('aiSearchName')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').search)).outputs.resourceId.value))), 'subnetResourceId', reference('network').outputs.subnetPrivateEndpointsResourceId.value))), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "6330685956569230662" + }, + "name": "Search Services", + "description": "This module deploys a Search Service." + }, + "definitions": { + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the API Admin keys generated by the modules." + } + }, + "primaryAdminKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryAdminKey secret name to create." + } + }, + "secondaryAdminKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryAdminKey secret name to create." + } + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, + "authOptionsType": { + "type": "object", + "properties": { + "aadOrApiKey": { + "type": "object", + "properties": { + "aadAuthFailureMode": { + "type": "string", + "allowedValues": [ + "http401WithBearerChallenge", + "http403" + ], + "nullable": true, + "metadata": { + "description": "Optional. Describes what response the data plane API of a search service would send for requests that failed authentication." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Indicates that either the API key or an access token from a Microsoft Entra ID tenant can be used for authentication." + } + }, + "apiKeyOnly": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Indicates that only the API key can be used for authentication." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "networkRuleSetType": { + "type": "object", + "properties": { + "bypass": { + "type": "string", + "allowedValues": [ + "AzurePortal", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Network specific rules that determine how the Azure AI Search service may be reached." + } + }, + "ipRules": { + "type": "array", + "items": { + "$ref": "#/definitions/ipRuleType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP restriction rules that defines the inbound network(s) with allowing access to the search service endpoint. At the meantime, all other public IP networks are blocked by the firewall. These restriction rules are applied only when the 'publicNetworkAccess' of the search service is 'enabled'; otherwise, traffic over public interface is not allowed even with any public IP rules, and private endpoint connections would be the exclusive access method." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipRuleType": { + "type": "object", + "properties": { + "value": { + "type": "string", + "metadata": { + "description": "Required. Value corresponding to a single IPv4 address (eg., 123.1.2.3) or an IP range in CIDR format (eg., 123.1.2.3/24) to be allowed." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "_1.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "_1.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "_1.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_1.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_1.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different Resource Group than the main resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + }, + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "modules/keyVaultExport.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure Cognitive Search service to create or update. Search service names must only contain lowercase letters, digits or dashes, cannot use dash as the first two or last one characters, cannot contain consecutive dashes, and must be between 2 and 60 characters in length. Search service names must be globally unique since they are part of the service URI (https://.search.windows.net). You cannot change the service name after the service is created." + } + }, + "authOptions": { + "$ref": "#/definitions/authOptionsType", + "nullable": true, + "metadata": { + "description": "Optional. Defines the options for how the data plane API of a Search service authenticates requests. Must remain an empty object {} if 'disableLocalAuth' is set to true." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. When set to true, calls to the search service will not be permitted to utilize API keys for authentication. This cannot be set to true if 'authOptions' are defined." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "cmkEnforcement": { + "type": "string", + "defaultValue": "Unspecified", + "allowedValues": [ + "Disabled", + "Enabled", + "Unspecified" + ], + "metadata": { + "description": "Optional. Describes a policy that determines how resources within the search service are to be encrypted with Customer Managed Keys." + } + }, + "hostingMode": { + "type": "string", + "defaultValue": "default", + "allowedValues": [ + "default", + "highDensity" + ], + "metadata": { + "description": "Optional. Applicable only for the standard3 SKU. You can set this property to enable up to 3 high density partitions that allow up to 1000 indexes, which is much higher than the maximum indexes allowed for any other SKU. For the standard3 SKU, the value is either 'default' or 'highDensity'. For all other SKUs, this value must be 'default'." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings for all Resources in the solution." + } + }, + "networkRuleSet": { + "$ref": "#/definitions/networkRuleSetType", + "nullable": true, + "metadata": { + "description": "Optional. Network specific rules that determine how the Azure Cognitive Search service may be reached." + } + }, + "partitionCount": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "maxValue": 12, + "metadata": { + "description": "Optional. The number of partitions in the search service; if specified, it can be 1, 2, 3, 4, 6, or 12. Values greater than 1 are only valid for standard SKUs. For 'standard3' services with hostingMode set to 'highDensity', the allowed values are between 1 and 3." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "sharedPrivateLinkResources": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The sharedPrivateLinkResources to create as part of the search Service." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. This value can be set to 'Enabled' to avoid breaking changes on existing customer resources and templates. If set to 'Disabled', traffic over public interface is not allowed, and private endpoint connections would be the exclusive access method." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "replicaCount": { + "type": "int", + "defaultValue": 3, + "minValue": 1, + "maxValue": 12, + "metadata": { + "description": "Optional. The number of replicas in the search service. If specified, it must be a value between 1 and 12 inclusive for standard SKUs or between 1 and 3 inclusive for basic SKU." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "semanticSearch": { + "type": "string", + "nullable": true, + "allowedValues": [ + "disabled", + "free", + "standard" + ], + "metadata": { + "description": "Optional. Sets options that control the availability of semantic search. This configuration is only possible for certain search SKUs in certain locations." + } + }, + "sku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "free", + "standard", + "standard2", + "standard3", + "storage_optimized_l1", + "storage_optimized_l2" + ], + "metadata": { + "description": "Optional. Defines the SKU of an Azure Cognitive Search Service, which determines price tier and capacity limits." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to help categorize the resource in the Azure portal." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', '')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Search Index Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", + "Search Index Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1407120a-92aa-4202-b7e9-c0e197c71c8f')]", + "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.search-searchservice.{0}.{1}', replace('0.9.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "searchService": { + "type": "Microsoft.Search/searchServices", + "apiVersion": "2024-03-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "sku": { + "name": "[parameters('sku')]" + }, + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "authOptions": "[parameters('authOptions')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryptionWithCmk": { + "enforcement": "[parameters('cmkEnforcement')]" + }, + "hostingMode": "[parameters('hostingMode')]", + "networkRuleSet": "[parameters('networkRuleSet')]", + "partitionCount": "[parameters('partitionCount')]", + "replicaCount": "[parameters('replicaCount')]", + "publicNetworkAccess": "[toLower(parameters('publicNetworkAccess'))]", + "semanticSearch": "[parameters('semanticSearch')]" + } + }, + "searchService_diagnosticSettings": { + "copy": { + "name": "searchService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "searchService" + ] + }, + "searchService_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "searchService" + ] + }, + "searchService_roleAssignments": { + "copy": { + "name": "searchService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Search/searchServices', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "searchService" + ] + }, + "searchService_privateEndpoints": { + "copy": { + "name": "searchService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-searchService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Search/searchServices', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Search/searchServices', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'searchService')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceIds": { + "type": "array", + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + }, + "value": "[reference('privateEndpoint').networkInterfaces]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "searchService" + ] + }, + "searchService_sharedPrivateLinkResources": { + "copy": { + "name": "searchService_sharedPrivateLinkResources", + "count": "[length(parameters('sharedPrivateLinkResources'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-searchService-SharedPrivateLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('sharedPrivateLinkResources')[copyIndex()], 'name'), format('spl-{0}-{1}-{2}', last(split(resourceId('Microsoft.Search/searchServices', parameters('name')), '/')), parameters('sharedPrivateLinkResources')[copyIndex()].groupId, copyIndex()))]" + }, + "searchServiceName": { + "value": "[parameters('name')]" + }, + "privateLinkResourceId": { + "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].privateLinkResourceId]" + }, + "groupId": { + "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].groupId]" + }, + "requestMessage": { + "value": "[parameters('sharedPrivateLinkResources')[copyIndex()].requestMessage]" + }, + "resourceRegion": { + "value": "[tryGet(parameters('sharedPrivateLinkResources')[copyIndex()], 'resourceRegion')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "16868169528574127897" + }, + "name": "Search Services Private Link Resources", + "description": "This module deploys a Search Service Private Link Resource." + }, + "parameters": { + "searchServiceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent searchServices. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the shared private link resource managed by the Azure Cognitive Search service within the specified resource group." + } + }, + "privateLinkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the resource the shared private link resource is for." + } + }, + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The group ID from the provider of resource the shared private link resource is for." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Required. The request message for requesting approval of the shared private link resource." + } + }, + "resourceRegion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Can be used to specify the Azure Resource Manager location of the resource to which a shared private link is to be created. This is only required for those resources whose DNS configuration are regional (such as Azure Kubernetes Service)." + } + } + }, + "resources": { + "searchService": { + "existing": true, + "type": "Microsoft.Search/searchServices", + "apiVersion": "2023-11-01", + "name": "[parameters('searchServiceName')]" + }, + "sharedPrivateLinkResource": { + "type": "Microsoft.Search/searchServices/sharedPrivateLinkResources", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('searchServiceName'), parameters('name'))]", + "properties": { + "privateLinkResourceId": "[parameters('privateLinkResourceId')]", + "groupId": "[parameters('groupId')]", + "requestMessage": "[parameters('requestMessage')]", + "resourceRegion": "[parameters('resourceRegion')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the shared private link resource." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the shared private link resource." + }, + "value": "[resourceId('Microsoft.Search/searchServices/sharedPrivateLinkResources', parameters('searchServiceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the shared private link resource was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "searchService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryAdminKeyName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryAdminKeyName, 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2024-03-01-preview').primaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryAdminKeyName, 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2024-03-01-preview').secondaryKey)), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "8130113748408624333" + } + }, + "definitions": { + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" + } + } + } + } + } + }, + "dependsOn": [ + "searchService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the search service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the search service." + }, + "value": "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the search service was created in." + }, + "value": "[resourceGroup().name]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('searchService', '2024-03-01-preview', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('searchService', '2024-03-01-preview', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + } + } + } + }, + "dependsOn": [ + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').search)]", + "logAnalyticsWorkspace", + "network", + "userAssignedIdentity" + ] + }, + "avmOpenAi": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.cognitiveservices.account.{0}', variables('openAiAccountName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('openAiAccountName')]" + }, + "location": { + "value": "[parameters('aiDeploymentsLocation')]" + }, + "kind": { + "value": "OpenAI" + }, + "sku": { + "value": "S0" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "customSubDomainName": { + "value": "[variables('openAiAccountName')]" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "networkAcls": { + "value": { + "defaultAction": "[if(parameters('enablePrivateNetworking'), 'Deny', 'Allow')]", + "bypass": "AzureServices" + } + }, + "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-openai-{0}', variables('solutionSuffix')), 'subnetResourceId', reference('network').outputs.subnetPrivateEndpointsResourceId.value, 'service', 'account', 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('name', 'openai-dns-zone-group', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)).outputs.resourceId.value)))))), createObject('value', createArray()))]", + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "Cognitive Services OpenAI Contributor", + "principalType": "ServicePrincipal" + }, + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "Cognitive Services OpenAI User", + "principalType": "ServicePrincipal" + } + ] + }, + "deployments": { + "value": "[variables('openAiDeployments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "9381727816193702843" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "commitmentPlanType": { + "type": "object", + "properties": { + "autoRenew": { + "type": "bool", + "metadata": { + "description": "Required. Whether the plan should auto-renew at the end of the current commitment period." + } + }, + "current": { + "type": "object", + "properties": { + "count": { + "type": "int", + "metadata": { + "description": "Required. The number of committed instances (e.g., number of containers or cores)." + } + }, + "tier": { + "type": "string", + "metadata": { + "description": "Required. The tier of the commitment plan (e.g., T1, T2)." + } + } + }, + "metadata": { + "description": "Required. The current commitment configuration." + } + }, + "hostingModel": { + "type": "string", + "metadata": { + "description": "Required. The hosting model for the commitment plan. (e.g., DisconnectedContainer, ConnectedContainer, ProvisionedWeb, Web)." + } + }, + "planType": { + "type": "string", + "metadata": { + "description": "Required. The plan type indicating which capability the plan applies to (e.g., NTTS, STT, CUSTOMSTT, ADDON)." + } + }, + "commitmentPlanGuid": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The unique identifier of an existing commitment plan to update. Set to null to create a new plan." + } + }, + "next": { + "type": "object", + "properties": { + "count": { + "type": "int", + "metadata": { + "description": "Required. The number of committed instances for the next period." + } + }, + "tier": { + "type": "string", + "metadata": { + "description": "Required. The tier for the next commitment period." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The configuration of the next commitment period, if scheduled." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a disconnected container commitment plan." + } + }, + "networkInjectionType": { + "type": "object", + "properties": { + "scenario": { + "type": "string", + "allowedValues": [ + "agent", + "none" + ], + "metadata": { + "description": "Required. The scenario for the network injection." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the subnet on the Virtual Network on which to inject." + } + }, + "useMicrosoftManagedNetwork": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to use Microsoft Managed Network. Defaults to false." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Type for network configuration in AI Foundry where virtual network injection occurs to secure scenarios like Agents entirely within a private network." + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "_2.lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "notes": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the notes of the lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "notes": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the notes of the lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_2.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/_2.lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Network/privateEndpoints@2024-07-01#properties/tags" + }, + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9", + "DC0" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "networkInjections": { + "$ref": "#/definitions/networkInjectionType", + "nullable": true, + "metadata": { + "description": "Optional. Specifies in AI Foundry where virtual network injection occurs to secure scenarios like Agents entirely within a private network." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.CognitiveServices/accounts@2025-04-01-preview#properties/properties/properties/userOwnedStorage" + }, + "description": "Optional. The storage accounts for this resource." + }, + "nullable": true + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + }, + "commitmentPlans": { + "type": "array", + "items": { + "$ref": "#/definitions/commitmentPlanType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Commitment plans to deploy for the cognitive services account." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.13.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-06-01", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "networkInjections": "[if(not(empty(parameters('networkInjections'))), createArray(createObject('scenario', tryGet(parameters('networkInjections'), 'scenario'), 'subnetArmId', tryGet(parameters('networkInjections'), 'subnetResourceId'), 'useMicrosoftManagedNetwork', coalesce(tryGet(parameters('networkInjections'), 'useMicrosoftManagedNetwork'), false()))), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[if(not(empty(parameters('userOwnedStorage'))), parameters('userOwnedStorage'), null())]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-06-01", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_commitmentPlans": { + "copy": { + "name": "cognitiveService_commitmentPlans", + "count": "[length(coalesce(parameters('commitmentPlans'), createArray()))]" + }, + "type": "Microsoft.CognitiveServices/accounts/commitmentPlans", + "apiVersion": "2025-06-01", + "name": "[format('{0}/{1}', parameters('name'), format('{0}-{1}', coalesce(parameters('commitmentPlans'), createArray())[copyIndex()].hostingModel, coalesce(parameters('commitmentPlans'), createArray())[copyIndex()].planType))]", + "properties": "[coalesce(parameters('commitmentPlans'), createArray())[copyIndex()]]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-cognitiveService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The resource IDs of the network interfaces associated with the private endpoint." + }, + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-06-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-06-01').key2)), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "10828079590669389085" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2024-11-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-06-01', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-06-01', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + }, + "dependsOn": [ + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", + "network", + "userAssignedIdentity" + ] + }, + "documentIntelligence": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.cognitiveservices.account.{0}', variables('docIntelAccountName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[variables('docIntelAccountName')]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "kind": { + "value": "FormRecognizer" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "sku": { + "value": "S0" + }, + "customSubDomainName": { + "value": "[variables('docIntelAccountName')]" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "publicNetworkAccess": "[if(parameters('enablePrivateNetworking'), createObject('value', 'Disabled'), createObject('value', 'Enabled'))]", + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "[if(parameters('enablePrivateNetworking'), 'Deny', 'Allow')]" + } + }, + "privateEndpoints": "[if(parameters('enablePrivateNetworking'), createObject('value', createArray(createObject('name', format('pep-docintel-{0}', variables('solutionSuffix')), 'subnetResourceId', reference('network').outputs.subnetPrivateEndpointsResourceId.value, 'service', 'account', 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('name', 'docintel-dns-zone-group', 'privateDnsZoneResourceId', reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)).outputs.resourceId.value)))))), createObject('value', createArray()))]", + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "Cognitive Services User", + "principalType": "ServicePrincipal" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "9381727816193702843" + }, + "name": "Cognitive Services", + "description": "This module deploys a Cognitive Service." + }, + "definitions": { + "privateEndpointOutputType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + } + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "A list of private IP addresses of the private endpoint." + } + } + } + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + } + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for the private endpoint output." + } + }, + "deploymentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of cognitive service account deployment." + } + }, + "model": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account deployment model." + } + }, + "format": { + "type": "string", + "metadata": { + "description": "Required. The format of Cognitive Services account deployment model." + } + }, + "version": { + "type": "string", + "metadata": { + "description": "Required. The version of Cognitive Services account deployment model." + } + } + }, + "metadata": { + "description": "Required. Properties of Cognitive Services account deployment model." + } + }, + "sku": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource model definition representing SKU." + } + }, + "capacity": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The capacity of the resource model definition representing SKU." + } + }, + "tier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The tier of the resource model definition representing SKU." + } + }, + "size": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The size of the resource model definition representing SKU." + } + }, + "family": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The family of the resource model definition representing SKU." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource model definition representing SKU." + } + }, + "raiPolicyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of RAI policy." + } + }, + "versionUpgradeOption": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version upgrade option." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account deployment." + } + }, + "endpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Type of the endpoint." + } + }, + "endpoint": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The endpoint URI." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a cognitive services account endpoint." + } + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of the secrets exported to the provided Key Vault." + } + }, + "commitmentPlanType": { + "type": "object", + "properties": { + "autoRenew": { + "type": "bool", + "metadata": { + "description": "Required. Whether the plan should auto-renew at the end of the current commitment period." + } + }, + "current": { + "type": "object", + "properties": { + "count": { + "type": "int", + "metadata": { + "description": "Required. The number of committed instances (e.g., number of containers or cores)." + } + }, + "tier": { + "type": "string", + "metadata": { + "description": "Required. The tier of the commitment plan (e.g., T1, T2)." + } + } + }, + "metadata": { + "description": "Required. The current commitment configuration." + } + }, + "hostingModel": { + "type": "string", + "metadata": { + "description": "Required. The hosting model for the commitment plan. (e.g., DisconnectedContainer, ConnectedContainer, ProvisionedWeb, Web)." + } + }, + "planType": { + "type": "string", + "metadata": { + "description": "Required. The plan type indicating which capability the plan applies to (e.g., NTTS, STT, CUSTOMSTT, ADDON)." + } + }, + "commitmentPlanGuid": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The unique identifier of an existing commitment plan to update. Set to null to create a new plan." + } + }, + "next": { + "type": "object", + "properties": { + "count": { + "type": "int", + "metadata": { + "description": "Required. The number of committed instances for the next period." + } + }, + "tier": { + "type": "string", + "metadata": { + "description": "Required. The tier for the next commitment period." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The configuration of the next commitment period, if scheduled." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a disconnected container commitment plan." + } + }, + "networkInjectionType": { + "type": "object", + "properties": { + "scenario": { + "type": "string", + "allowedValues": [ + "agent", + "none" + ], + "metadata": { + "description": "Required. The scenario for the network injection." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Resource ID of the subnet on the Virtual Network on which to inject." + } + }, + "useMicrosoftManagedNetwork": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to use Microsoft Managed Network. Defaults to false." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "Type for network configuration in AI Foundry where virtual network injection occurs to secure scenarios like Agents entirely within a private network." + } + }, + "_1.secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "_2.lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "notes": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the notes of the lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.privateEndpointCustomDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.privateEndpointIpConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.privateEndpointPrivateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS Zone Group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS Zone Groups to associate the Private Endpoint. A DNS Zone Group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "_2.roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, the deployment will use the latest version available at deployment time." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if the resource type does not support auto-rotation of the customer-managed key.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "notes": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the notes of the lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "privateEndpointSingleServiceType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private Endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the Private Endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the Private Endpoint for. For example \"vault\" for a Key Vault Private Endpoint." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "resourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the Resource Group the Private Endpoint will be created in. If not specified, the Resource Group of the provided Virtual Network Subnet is used." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/_2.privateEndpointPrivateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS Zone Group to configure for the Private Endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.privateEndpointCustomDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.privateEndpointIpConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the Private Endpoint. This will be used to map to the first-party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the Private Endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the Private Endpoint." + } + }, + "lock": { + "$ref": "#/definitions/_2.lockType", + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/_2.roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Network/privateEndpoints@2024-07-01#properties/tags" + }, + "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/_1.secretSetOutputType", + "metadata": { + "description": "An exported secret's references." + } + }, + "metadata": { + "description": "A map of the exported secrets", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of Cognitive Services account." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AIServices", + "AnomalyDetector", + "CognitiveServices", + "ComputerVision", + "ContentModerator", + "ContentSafety", + "ConversationalLanguageUnderstanding", + "CustomVision.Prediction", + "CustomVision.Training", + "Face", + "FormRecognizer", + "HealthInsights", + "ImmersiveReader", + "Internal.AllInOne", + "LUIS", + "LUIS.Authoring", + "LanguageAuthoring", + "MetricsAdvisor", + "OpenAI", + "Personalizer", + "QnAMaker.v2", + "SpeechServices", + "TextAnalytics", + "TextTranslation" + ], + "metadata": { + "description": "Required. Kind of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "sku": { + "type": "string", + "defaultValue": "S0", + "allowedValues": [ + "C2", + "C3", + "C4", + "F0", + "F1", + "S", + "S0", + "S1", + "S10", + "S2", + "S3", + "S4", + "S5", + "S6", + "S7", + "S8", + "S9", + "DC0" + ], + "metadata": { + "description": "Optional. SKU of the Cognitive Services account. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set." + } + }, + "customSubDomainName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Subdomain name used for token-based authentication. Required if 'networkAcls' or 'privateEndpoints' are set." + } + }, + "networkAcls": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A collection of rules governing the accessibility from specific network locations." + } + }, + "networkInjections": { + "$ref": "#/definitions/networkInjectionType", + "nullable": true, + "metadata": { + "description": "Optional. Specifies in AI Foundry where virtual network injection occurs to secure scenarios like Agents entirely within a private network." + } + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointSingleServiceType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "allowedFqdnList": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of allowed FQDN." + } + }, + "apiProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The API properties for special APIs." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow only Azure AD authentication. Should be enabled for security reasons." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "dynamicThrottlingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The flag to enable dynamic throttling." + } + }, + "migrationToken": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. Resource migration token." + } + }, + "restore": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists." + } + }, + "restrictOutboundNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Restrict outbound network access." + } + }, + "userOwnedStorage": { + "type": "array", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.CognitiveServices/accounts@2025-04-01-preview#properties/properties/properties/userOwnedStorage" + }, + "description": "Optional. The storage accounts for this resource." + }, + "nullable": true + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/deploymentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of deployments about cognitive service accounts to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "allowProjectManagement": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable project management feature for AI Foundry." + } + }, + "commitmentPlans": { + "type": "array", + "items": { + "$ref": "#/definitions/commitmentPlanType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Commitment plans to deploy for the cognitive services account." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Cognitive Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "Cognitive Services Custom Vision Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c1ff6cc2-c111-46fe-8896-e0ef812ad9f3')]", + "Cognitive Services Custom Vision Deployment": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5c4089e1-6d96-4d2f-b296-c1bc7137275f')]", + "Cognitive Services Custom Vision Labeler": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '88424f51-ebe7-446f-bc41-7fa16989e96c')]", + "Cognitive Services Custom Vision Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '93586559-c37d-4a6b-ba08-b9f0940c2d73')]", + "Cognitive Services Custom Vision Trainer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a5ae4ab-0d65-4eeb-be61-29fc9b54394b')]", + "Cognitive Services Data Reader (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b59867f0-fa02-499b-be73-45a86b5b3e1c')]", + "Cognitive Services Face Recognizer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9894cab4-e18a-44aa-828b-cb588cd6f2d7')]", + "Cognitive Services Immersive Reader User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b2de6794-95db-4659-8781-7e080d3f2b9d')]", + "Cognitive Services Language Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f07febfe-79bc-46b1-8b37-790e26e6e498')]", + "Cognitive Services Language Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7628b7b8-a8b2-4cdc-b46f-e9b35248918e')]", + "Cognitive Services Language Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2310ca1-dc64-4889-bb49-c8e0fa3d47a8')]", + "Cognitive Services LUIS Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f72c8140-2111-481c-87ff-72b910f6e3f8')]", + "Cognitive Services LUIS Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18e81cdc-4e98-4e29-a639-e7d10c5a6226')]", + "Cognitive Services LUIS Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6322a993-d5c9-4bed-b113-e49bbea25b27')]", + "Cognitive Services Metrics Advisor Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cb43c632-a144-4ec5-977c-e80c4affc34a')]", + "Cognitive Services Metrics Advisor User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3b20f47b-3825-43cb-8114-4bd2201156a8')]", + "Cognitive Services OpenAI Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", + "Cognitive Services OpenAI User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "Cognitive Services QnA Maker Editor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f4cc2bf9-21be-47a1-bdf1-5c5804381025')]", + "Cognitive Services QnA Maker Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '466ccd10-b268-4a11-b098-b4849f024126')]", + "Cognitive Services Speech Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e75ca1e-0464-4b4d-8b93-68208a576181')]", + "Cognitive Services Speech User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f2dc8367-1007-4938-bd23-fe263f013447')]", + "Cognitive Services User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "Azure AI Developer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '64702f94-c441-49e6-a78b-ef80e0188fee')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')), tryGet(parameters('customerManagedKey'), 'keyName'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.13.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2025-01-31-preview", + "subscriptionId": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/')[4]]", + "name": "[last(split(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '/'))]" + }, + "cognitiveService": { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2025-06-01", + "name": "[parameters('name')]", + "kind": "[parameters('kind')]", + "identity": "[variables('identity')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('sku')]" + }, + "properties": { + "allowProjectManagement": "[parameters('allowProjectManagement')]", + "customSubDomainName": "[parameters('customSubDomainName')]", + "networkAcls": "[if(not(empty(coalesce(parameters('networkAcls'), createObject()))), createObject('defaultAction', tryGet(parameters('networkAcls'), 'defaultAction'), 'virtualNetworkRules', coalesce(tryGet(parameters('networkAcls'), 'virtualNetworkRules'), createArray()), 'ipRules', coalesce(tryGet(parameters('networkAcls'), 'ipRules'), createArray())), null())]", + "networkInjections": "[if(not(empty(parameters('networkInjections'))), createArray(createObject('scenario', tryGet(parameters('networkInjections'), 'scenario'), 'subnetArmId', tryGet(parameters('networkInjections'), 'subnetResourceId'), 'useMicrosoftManagedNetwork', coalesce(tryGet(parameters('networkInjections'), 'useMicrosoftManagedNetwork'), false()))), null())]", + "publicNetworkAccess": "[if(not(equals(parameters('publicNetworkAccess'), null())), parameters('publicNetworkAccess'), if(not(empty(parameters('networkAcls'))), 'Enabled', 'Disabled'))]", + "allowedFqdnList": "[parameters('allowedFqdnList')]", + "apiProperties": "[parameters('apiProperties')]", + "disableLocalAuth": "[parameters('disableLocalAuth')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.KeyVault', 'keyVaultProperties', createObject('identityClientId', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), tryGet(parameters('customerManagedKey'), 'keyVersion'), last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null())]", + "migrationToken": "[parameters('migrationToken')]", + "restore": "[parameters('restore')]", + "restrictOutboundNetworkAccess": "[parameters('restrictOutboundNetworkAccess')]", + "userOwnedStorage": "[if(not(empty(parameters('userOwnedStorage'))), parameters('userOwnedStorage'), null())]", + "dynamicThrottlingEnabled": "[parameters('dynamicThrottlingEnabled')]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKKeyVault::cMKKey", + "cMKUserAssignedIdentity" + ] + }, + "cognitiveService_deployments": { + "copy": { + "name": "cognitiveService_deployments", + "count": "[length(coalesce(parameters('deployments'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2025-06-01", + "name": "[format('{0}/{1}', parameters('name'), coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'name'), format('{0}-deployments', parameters('name'))))]", + "properties": { + "model": "[coalesce(parameters('deployments'), createArray())[copyIndex()].model]", + "raiPolicyName": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'raiPolicyName')]", + "versionUpgradeOption": "[tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'versionUpgradeOption')]" + }, + "sku": "[coalesce(tryGet(coalesce(parameters('deployments'), createArray())[copyIndex()], 'sku'), createObject('name', parameters('sku'), 'capacity', tryGet(parameters('sku'), 'capacity'), 'tier', tryGet(parameters('sku'), 'tier'), 'size', tryGet(parameters('sku'), 'size'), 'family', tryGet(parameters('sku'), 'family')))]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_commitmentPlans": { + "copy": { + "name": "cognitiveService_commitmentPlans", + "count": "[length(coalesce(parameters('commitmentPlans'), createArray()))]" + }, + "type": "Microsoft.CognitiveServices/accounts/commitmentPlans", + "apiVersion": "2025-06-01", + "name": "[format('{0}/{1}', parameters('name'), format('{0}-{1}', coalesce(parameters('commitmentPlans'), createArray())[copyIndex()].hostingModel, coalesce(parameters('commitmentPlans'), createArray())[copyIndex()].planType))]", + "properties": "[coalesce(parameters('commitmentPlans'), createArray())[copyIndex()]]", + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_diagnosticSettings": { + "copy": { + "name": "cognitiveService_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_roleAssignments": { + "copy": { + "name": "cognitiveService_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "cognitiveService_privateEndpoints": { + "copy": { + "name": "cognitiveService_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-cognitiveService-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupResourceId'), resourceGroup().id), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'account')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "12389807800450456797" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint." + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "ipConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "privateLinkServiceConnectionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "customDnsConfigType": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. FQDN that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/ipConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." + } + }, + "privateLinkServiceConnections": { + "type": "array", + "items": { + "$ref": "#/definitions/privateLinkServiceConnectionType" + }, + "nullable": true, + "metadata": { + "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.34.44.8038", + "templateHash": "13997305779829540948" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group." + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2024-05-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2024-05-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2024-05-01', 'full').location]" + }, + "customDnsConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/customDnsConfigType" + }, + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The resource IDs of the network interfaces associated with the private endpoint." + }, + "value": "[map(reference('privateEndpoint').networkInterfaces, lambda('nic', lambdaVariables('nic').id))]" + }, + "groupId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[coalesce(tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'manualPrivateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0), tryGet(tryGet(tryGet(tryGet(reference('privateEndpoint'), 'privateLinkServiceConnections'), 0, 'properties'), 'groupIds'), 0))]" + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[2]]", + "resourceGroup": "[split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey1Name'), 'value', listKeys('cognitiveService', '2025-06-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', tryGet(parameters('secretsExportConfiguration'), 'accessKey2Name'), 'value', listKeys('cognitiveService', '2025-06-01').key2)), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "10828079590669389085" + } + }, + "definitions": { + "secretSetOutputType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + }, + "secretUriWithVersion": { + "type": "string", + "metadata": { + "description": "The secret URI with version of the exported secret." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the output of the secret set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for the secret to set via the secrets export feature.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2024-11-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + } + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetOutputType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]", + "secretUriWithVersion": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUriWithVersion]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the cognitive services account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the cognitive services account." + }, + "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the cognitive services account was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "endpoint": { + "type": "string", + "metadata": { + "description": "The service endpoint of the cognitive services account." + }, + "value": "[reference('cognitiveService').endpoint]" + }, + "endpoints": { + "$ref": "#/definitions/endpointType", + "metadata": { + "description": "All endpoints available for the cognitive services account, types depends on the cognitive service kind." + }, + "value": "[reference('cognitiveService').endpoints]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('cognitiveService', '2025-06-01', 'full'), 'identity'), 'principalId')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('cognitiveService', '2025-06-01', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + }, + "privateEndpoints": { + "type": "array", + "items": { + "$ref": "#/definitions/privateEndpointOutputType" + }, + "metadata": { + "description": "The private endpoints of the congitive services account." + }, + "copy": { + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]", + "input": { + "name": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[tryGet(tryGet(reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs, 'groupId'), 'value')]", + "customDnsConfigs": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfigs.value]", + "networkInterfaceResourceIds": "[reference(format('cognitiveService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceResourceIds.value]" + } + } + } + } + } + }, + "dependsOn": [ + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]", + "network", + "userAssignedIdentity" + ] + }, + "managedCluster": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('avm.res.container-service.managed-cluster.aks-{0}', variables('solutionSuffix')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('aks-{0}', variables('solutionSuffix'))]" + }, + "location": { + "value": "[variables('solutionLocation')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "kubernetesVersion": { + "value": "1.30.4" + }, + "dnsPrefix": { + "value": "[format('aks-{0}', variables('solutionSuffix'))]" + }, + "enableRBAC": { + "value": true + }, + "disableLocalAccounts": { + "value": false + }, + "publicNetworkAccess": { + "value": "Enabled" + }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, + "serviceCidr": { + "value": "10.20.0.0/16" + }, + "dnsServiceIP": { + "value": "10.20.0.10" + }, + "enablePrivateCluster": { + "value": false + }, + "primaryAgentPoolProfiles": { + "value": [ + { + "name": "agentpool", + "vmSize": "Standard_D4ds_v5", + "count": 2, + "osType": "Linux", + "mode": "System", + "type": "VirtualMachineScaleSets", + "minCount": 1, + "maxCount": 2, + "enableAutoScaling": true, + "scaleSetEvictionPolicy": "Delete", + "scaleSetPriority": "Regular", + "vnetSubnetResourceId": "[if(parameters('enablePrivateNetworking'), reference('network').outputs.subnetWebResourceId.value, null())]" + } + ] + }, + "autoNodeOsUpgradeProfileUpgradeChannel": { + "value": "Unmanaged" + }, + "autoUpgradeProfileUpgradeChannel": { + "value": "stable" + }, + "enableAzureDefender": { + "value": "[parameters('enablePrivateNetworking')]" + }, + "networkPlugin": { + "value": "azure" + }, + "networkPolicy": { + "value": "azure" + }, + "omsAgentEnabled": { + "value": true + }, + "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('logCategoriesAndGroups', createArray(createObject('category', 'kube-apiserver'), createObject('category', 'kube-controller-manager'), createObject('category', 'kube-scheduler'), createObject('category', 'cluster-autoscaler')), 'metricCategories', createArray(createObject('category', 'AllMetrics')), 'name', 'customSetting', 'workspaceResourceId', if(variables('useExistingLogAnalytics'), parameters('existingLogAnalyticsWorkspaceId'), reference('logAnalyticsWorkspace').outputs.resourceId.value)))), createObject('value', createArray()))]", + "monitoringWorkspaceResourceId": "[if(parameters('enableMonitoring'), if(variables('useExistingLogAnalytics'), createObject('value', parameters('existingLogAnalyticsWorkspaceId')), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value)), createObject('value', null()))]", + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "8388841527458028173" + }, + "name": "Azure Kubernetes Service (AKS) Managed Clusters", + "description": "This module deploys an Azure Kubernetes Service (AKS) Managed Cluster." + }, + "definitions": { + "agentPoolType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the agent pool." + } + }, + "availabilityZones": { + "type": "array", + "items": { + "type": "int" + }, + "nullable": true, + "metadata": { + "description": "Optional. The availability zones of the agent pool." + } + }, + "count": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of agents (VMs) to host docker containers. Allowed values must be in the range of 1 to 100 (inclusive)." + } + }, + "sourceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The source resource ID to create the agent pool from." + } + }, + "enableAutoScaling": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to enable auto-scaling for the agent pool." + } + }, + "enableEncryptionAtHost": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to enable encryption at host for the agent pool." + } + }, + "enableFIPS": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to enable FIPS for the agent pool." + } + }, + "enableNodePublicIP": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to enable node public IP for the agent pool." + } + }, + "enableUltraSSD": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether to enable Ultra SSD for the agent pool." + } + }, + "gpuInstanceProfile": { + "type": "string", + "allowedValues": [ + "MIG1g", + "MIG2g", + "MIG3g", + "MIG4g", + "MIG7g" + ], + "nullable": true, + "metadata": { + "description": "Optional. The GPU instance profile of the agent pool." + } + }, + "kubeletDiskType": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The kubelet disk type of the agent pool." + } + }, + "maxCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The maximum number of agents (VMs) to host docker containers. Allowed values must be in the range of 1 to 100 (inclusive)." + } + }, + "minCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The minimum number of agents (VMs) to host docker containers. Allowed values must be in the range of 1 to 100 (inclusive)." + } + }, + "maxPods": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The maximum number of pods that can run on a node." + } + }, + "minPods": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The minimum number of pods that can run on a node." + } + }, + "mode": { + "type": "string", + "allowedValues": [ + "System", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The mode of the agent pool." + } + }, + "nodeLabels": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The node labels of the agent pool." + } + }, + "nodePublicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The node public IP prefix ID of the agent pool." + } + }, + "nodeTaints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The node taints of the agent pool." + } + }, + "orchestratorVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Kubernetes version of the agent pool." + } + }, + "osDiskSizeGB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The OS disk size in GB of the agent pool." + } + }, + "osDiskType": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The OS disk type of the agent pool." + } + }, + "osSKU": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The OS SKU of the agent pool." + } + }, + "osType": { + "type": "string", + "allowedValues": [ + "Linux", + "Windows" + ], + "nullable": true, + "metadata": { + "description": "Optional. The OS type of the agent pool." + } + }, + "podSubnetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The pod subnet ID of the agent pool." + } + }, + "proximityPlacementGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The proximity placement group resource ID of the agent pool." + } + }, + "scaleDownMode": { + "type": "string", + "allowedValues": [ + "Deallocate", + "Delete" + ], + "nullable": true, + "metadata": { + "description": "Optional. The scale down mode of the agent pool." + } + }, + "scaleSetEvictionPolicy": { + "type": "string", + "allowedValues": [ + "Deallocate", + "Delete" + ], + "nullable": true, + "metadata": { + "description": "Optional. The scale set eviction policy of the agent pool." + } + }, + "scaleSetPriority": { + "type": "string", + "allowedValues": [ + "Low", + "Regular", + "Spot" + ], + "nullable": true, + "metadata": { + "description": "Optional. The scale set priority of the agent pool." + } + }, + "enableSecureBoot": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Secure Boot is a feature of Trusted Launch which ensures that only signed operating systems and drivers can boot. For more details, see aka.ms/aks/trustedlaunch." + } + }, + "enableVTPM": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. vTPM is a Trusted Launch feature for configuring a dedicated secure vault for keys and measurements held locally on the node. For more details, see aka.ms/aks/trustedlaunch." + } + }, + "spotMaxPrice": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The spot max price of the agent pool." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the agent pool." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "AvailabilitySet", + "VirtualMachineScaleSets" + ], + "nullable": true, + "metadata": { + "description": "Optional. The type of the agent pool." + } + }, + "maxSurge": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The maximum number of nodes that can be created during an upgrade." + } + }, + "vmSize": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The VM size of the agent pool." + } + }, + "vnetSubnetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The VNet subnet ID of the agent pool." + } + }, + "workloadRuntime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The workload runtime of the agent pool." + } + }, + "enableDefaultTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The enable default telemetry of the agent pool." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an agent pool." + } + }, + "fluxConfigurationProtectedSettingsType": { + "type": "object", + "properties": { + "sshPrivateKey": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Optional. The SSH private key to use for Git authentication." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for flux configuration protected settings." + } + }, + "extensionType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the extension." + } + }, + "releaseNamespace": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Namespace where the extension Release must be placed." + } + }, + "targetNamespace": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Namespace where the extension will be created for an Namespace scoped extension." + } + }, + "releaseTrain": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The release train of the extension." + } + }, + "configurationProtectedSettings": { + "$ref": "#/definitions/fluxConfigurationProtectedSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The configuration protected settings of the extension." + } + }, + "configurationSettings": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The configuration settings of the extension." + } + }, + "version": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the extension." + } + }, + "configurations": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The flux configurations of the extension." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an extension." + } + }, + "maintenanceConfigurationType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "allowedValues": [ + "aksManagedAutoUpgradeSchedule", + "aksManagedNodeOSUpgradeSchedule" + ], + "metadata": { + "description": "Required. Name of maintenance window." + } + }, + "maintenanceWindow": { + "type": "object", + "metadata": { + "description": "Required. Maintenance window for the maintenance configuration." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type of a mainenance configuration." + } + }, + "istioServiceMeshCertificateAuthorityType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a Certificate Authority from." + } + }, + "certChainObjectName": { + "type": "string", + "metadata": { + "description": "Required. The Certificate chain object name in Azure Key Vault." + } + }, + "certObjectName": { + "type": "string", + "metadata": { + "description": "Required. The Intermediate certificate object name in Azure Key Vault." + } + }, + "keyObjectName": { + "type": "string", + "metadata": { + "description": "Required. The Intermediate certificate private key object name in Azure Key Vault." + } + }, + "rootCertObjectName": { + "type": "string", + "metadata": { + "description": "Required. Root certificate object name in Azure Key Vault." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an The Istio Certificate Authority definition." + } + }, + "aadProfileType": { + "type": "object", + "properties": { + "aadProfileClientAppID": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The client AAD application ID." + } + }, + "aadProfileServerAppID": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The server AAD application ID." + } + }, + "aadProfileServerAppSecret": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The server AAD application secret." + } + }, + "aadProfileManaged": { + "type": "bool", + "metadata": { + "description": "Required. Specifies whether to enable managed AAD integration." + } + }, + "aadProfileEnableAzureRBAC": { + "type": "bool", + "metadata": { + "description": "Required. Specifies whether to enable Azure RBAC for Kubernetes authorization." + } + }, + "aadProfileAdminGroupObjectIDs": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specifies the AAD group object IDs that will have admin role of the cluster." + } + }, + "aadProfileTenantId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the tenant ID of the Azure Active Directory used by the AKS cluster for authentication." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for an AAD profile." + } + }, + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "notes": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the notes of the lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + } + } + }, + "managedIdentityAllType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Specifies the name of the AKS cluster." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Specifies the location of AKS cluster. It picks up Resource Group's location by default." + } + }, + "dnsPrefix": { + "type": "string", + "defaultValue": "[parameters('name')]", + "metadata": { + "description": "Optional. Specifies the DNS prefix specified when creating the managed cluster." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentityAllType", + "nullable": true, + "metadata": { + "description": "Optional. The managed identity definition for this resource. Only one type of identity is supported: system-assigned or user-assigned, but not both." + } + }, + "networkDataplane": { + "type": "string", + "nullable": true, + "allowedValues": [ + "azure", + "cilium" + ], + "metadata": { + "description": "Optional. Network dataplane used in the Kubernetes cluster. Not compatible with kubenet network plugin." + } + }, + "networkPlugin": { + "type": "string", + "nullable": true, + "allowedValues": [ + "azure", + "kubenet" + ], + "metadata": { + "description": "Optional. Specifies the network plugin used for building Kubernetes network." + } + }, + "networkPluginMode": { + "type": "string", + "nullable": true, + "allowedValues": [ + "overlay" + ], + "metadata": { + "description": "Optional. Network plugin mode used for building the Kubernetes network. Not compatible with kubenet network plugin." + } + }, + "networkPolicy": { + "type": "string", + "nullable": true, + "allowedValues": [ + "azure", + "calico", + "cilium" + ], + "metadata": { + "description": "Optional. Specifies the network policy used for building Kubernetes network. - calico or azure." + } + }, + "podCidr": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the CIDR notation IP range from which to assign pod IPs when kubenet is used." + } + }, + "serviceCidr": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A CIDR notation IP range from which to assign service cluster IPs. It must not overlap with any Subnet IP ranges." + } + }, + "dnsServiceIP": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the IP address assigned to the Kubernetes DNS service. It must be within the Kubernetes service address range specified in serviceCidr." + } + }, + "loadBalancerSku": { + "type": "string", + "defaultValue": "standard", + "allowedValues": [ + "basic", + "standard" + ], + "metadata": { + "description": "Optional. Specifies the sku of the load balancer used by the virtual machine scale sets used by nodepools." + } + }, + "managedOutboundIPCount": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. Outbound IP Count for the Load balancer." + } + }, + "allocatedOutboundPorts": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The desired number of allocated SNAT ports per VM. Default is 0, which results in Azure dynamically allocating ports." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 30, + "metadata": { + "description": "Optional. Desired outbound flow idle timeout in minutes." + } + }, + "outboundPublicIPResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of the resource IDs of the public IP addresses to use for the load balancer outbound rules." + } + }, + "outboundPublicIPPrefixResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of the resource IDs of the public IP prefixes to use for the load balancer outbound rules." + } + }, + "backendPoolType": { + "type": "string", + "defaultValue": "NodeIPConfiguration", + "allowedValues": [ + "NodeIP", + "NodeIPConfiguration" + ], + "metadata": { + "description": "Optional. The type of the managed inbound Load Balancer BackendPool." + } + }, + "outboundType": { + "type": "string", + "defaultValue": "loadBalancer", + "allowedValues": [ + "loadBalancer", + "userDefinedRouting", + "managedNATGateway", + "userAssignedNATGateway" + ], + "metadata": { + "description": "Optional. Specifies outbound (egress) routing method." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Base", + "allowedValues": [ + "Base", + "Automatic" + ], + "metadata": { + "description": "Optional. Name of a managed cluster SKU. AUTOMATIC CLUSTER SKU IS A PARAMETER USED FOR A PREVIEW FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-automatic-deploy?pivots=bicep#before-you-begin) FOR CLARIFICATION." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Free", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Tier of a managed cluster SKU." + } + }, + "kubernetesVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Version of Kubernetes specified when creating the managed cluster." + } + }, + "adminUsername": { + "type": "string", + "defaultValue": "azureuser", + "metadata": { + "description": "Optional. Specifies the administrator username of Linux virtual machines." + } + }, + "sshPublicKey": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the SSH RSA public key string for the Linux nodes." + } + }, + "aadProfile": { + "$ref": "#/definitions/aadProfileType", + "nullable": true, + "metadata": { + "description": "Optional. Enable Azure Active Directory integration." + } + }, + "aksServicePrincipalProfile": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Conditional. Information about a service principal identity for the cluster to use for manipulating Azure APIs. Required if no managed identities are assigned to the cluster." + } + }, + "enableRBAC": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether to enable Kubernetes Role-Based Access Control." + } + }, + "disableLocalAccounts": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If set to true, getting static credentials will be disabled for this cluster. This must only be used on Managed Clusters that are AAD enabled." + } + }, + "nodeProvisioningProfileMode": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Auto", + "Manual" + ], + "metadata": { + "description": "Optional. Node provisioning settings that apply to the whole cluster. AUTO MODE IS A PARAMETER USED FOR A PREVIEW FEATURE, MICROSOFT MAY NOT PROVIDE SUPPORT FOR THIS, PLEASE CHECK THE [PRODUCT DOCS](https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-automatic-deploy?pivots=bicep#before-you-begin) FOR CLARIFICATION." + } + }, + "nodeResourceGroup": { + "type": "string", + "defaultValue": "[format('{0}_aks_{1}_nodes', resourceGroup().name, parameters('name'))]", + "metadata": { + "description": "Optional. Name of the resource group containing agent pool nodes." + } + }, + "nodeResourceGroupProfile": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The node resource group configuration profile." + } + }, + "authorizedIPRanges": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. IP ranges are specified in CIDR format, e.g. 137.117.106.88/29. This feature is not compatible with clusters that use Public IP Per Node, or clusters that are using a Basic Load Balancer." + } + }, + "disableRunCommand": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to disable run command for the cluster or not." + } + }, + "publicNetworkAccess": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Enabled", + "Disabled", + "SecuredByPerimeter" + ], + "metadata": { + "description": "Optional. Allow or deny public network access for AKS." + } + }, + "enablePrivateCluster": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether to create the cluster as a private cluster or not." + } + }, + "enablePrivateClusterPublicFQDN": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to create additional public FQDN for private cluster or not." + } + }, + "privateDNSZone": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Private DNS Zone configuration. Set to 'system' and AKS will create a private DNS zone in the node resource group. Set to '' to disable private DNS Zone creation and use public DNS. Supply the resource ID here of an existing Private DNS zone to use an existing zone." + } + }, + "primaryAgentPoolProfiles": { + "type": "array", + "items": { + "$ref": "#/definitions/agentPoolType" + }, + "metadata": { + "description": "Required. Properties of the primary agent pool." + } + }, + "agentPools": { + "type": "array", + "items": { + "$ref": "#/definitions/agentPoolType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Define one or more secondary/additional agent pools." + } + }, + "maintenanceConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/maintenanceConfigurationType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Whether or not to use AKS Automatic mode." + } + }, + "costAnalysisEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the cost analysis add-on is enabled or not. If Enabled `enableStorageProfileDiskCSIDriver` is set to true as it is needed." + } + }, + "httpApplicationRoutingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the httpApplicationRouting add-on is enabled or not." + } + }, + "webApplicationRoutingEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the webApplicationRoutingEnabled add-on is enabled or not." + } + }, + "dnsZoneResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the resource ID of connected DNS zone. It will be ignored if `webApplicationRoutingEnabled` is set to `false`." + } + }, + "defaultIngressControllerType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "AnnotationControlled", + "External", + "Internal", + "None" + ], + "metadata": { + "description": "Optional. Ingress type for the default NginxIngressController custom resource. It will be ignored if `webApplicationRoutingEnabled` is set to `false`." + } + }, + "enableDnsZoneContributorRoleAssignment": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether assing the DNS zone contributor role to the cluster service principal. It will be ignored if `webApplicationRoutingEnabled` is set to `false` or `dnsZoneResourceId` not provided." + } + }, + "ingressApplicationGatewayEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the ingressApplicationGateway (AGIC) add-on is enabled or not." + } + }, + "appGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Specifies the resource ID of connected application gateway. Required if `ingressApplicationGatewayEnabled` is set to `true`." + } + }, + "aciConnectorLinuxEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the aciConnectorLinux add-on is enabled or not." + } + }, + "azurePolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the azurepolicy add-on is enabled or not. For security reasons, this setting should be enabled." + } + }, + "openServiceMeshEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the openServiceMesh add-on is enabled or not." + } + }, + "azurePolicyVersion": { + "type": "string", + "defaultValue": "v2", + "metadata": { + "description": "Optional. Specifies the azure policy version to use." + } + }, + "kubeDashboardEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the kubeDashboard add-on is enabled or not." + } + }, + "enableKeyvaultSecretsProvider": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the KeyvaultSecretsProvider add-on is enabled or not." + } + }, + "enableSecretRotation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the KeyvaultSecretsProvider add-on uses secret rotation." + } + }, + "autoScalerProfileScanInterval": { + "type": "string", + "defaultValue": "10s", + "metadata": { + "description": "Optional. Specifies the scan interval of the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileScaleDownDelayAfterAdd": { + "type": "string", + "defaultValue": "10m", + "metadata": { + "description": "Optional. Specifies the scale down delay after add of the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileScaleDownDelayAfterDelete": { + "type": "string", + "defaultValue": "20s", + "metadata": { + "description": "Optional. Specifies the scale down delay after delete of the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileScaleDownDelayAfterFailure": { + "type": "string", + "defaultValue": "3m", + "metadata": { + "description": "Optional. Specifies scale down delay after failure of the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileScaleDownUnneededTime": { + "type": "string", + "defaultValue": "10m", + "metadata": { + "description": "Optional. Specifies the scale down unneeded time of the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileScaleDownUnreadyTime": { + "type": "string", + "defaultValue": "20m", + "metadata": { + "description": "Optional. Specifies the scale down unready time of the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileUtilizationThreshold": { + "type": "string", + "defaultValue": "0.5", + "metadata": { + "description": "Optional. Specifies the utilization threshold of the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileMaxGracefulTerminationSec": { + "type": "int", + "defaultValue": 600, + "metadata": { + "description": "Optional. Specifies the max graceful termination time interval in seconds for the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileBalanceSimilarNodeGroups": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies the balance of similar node groups for the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileExpander": { + "type": "string", + "defaultValue": "random", + "allowedValues": [ + "least-waste", + "most-pods", + "priority", + "random" + ], + "metadata": { + "description": "Optional. Specifies the expand strategy for the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileMaxEmptyBulkDelete": { + "type": "int", + "defaultValue": 10, + "metadata": { + "description": "Optional. Specifies the maximum empty bulk delete for the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileMaxNodeProvisionTime": { + "type": "string", + "defaultValue": "15m", + "metadata": { + "description": "Optional. Specifies the maximum node provisioning time for the auto-scaler of the AKS cluster. Values must be an integer followed by an \"m\". No unit of time other than minutes (m) is supported." + } + }, + "autoScalerProfileMaxTotalUnreadyPercentage": { + "type": "int", + "defaultValue": 45, + "metadata": { + "description": "Optional. Specifies the mximum total unready percentage for the auto-scaler of the AKS cluster. The maximum is 100 and the minimum is 0." + } + }, + "autoScalerProfileNewPodScaleUpDelay": { + "type": "string", + "defaultValue": "0s", + "metadata": { + "description": "Optional. For scenarios like burst/batch scale where you do not want CA to act before the kubernetes scheduler could schedule all the pods, you can tell CA to ignore unscheduled pods before they are a certain age. Values must be an integer followed by a unit (\"s\" for seconds, \"m\" for minutes, \"h\" for hours, etc)." + } + }, + "autoScalerProfileOkTotalUnreadyCount": { + "type": "int", + "defaultValue": 3, + "metadata": { + "description": "Optional. Specifies the OK total unready count for the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileSkipNodesWithLocalStorage": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if nodes with local storage should be skipped for the auto-scaler of the AKS cluster." + } + }, + "autoScalerProfileSkipNodesWithSystemPods": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies if nodes with system pods should be skipped for the auto-scaler of the AKS cluster." + } + }, + "autoUpgradeProfileUpgradeChannel": { + "type": "string", + "defaultValue": "stable", + "allowedValues": [ + "node-image", + "none", + "patch", + "rapid", + "stable" + ], + "metadata": { + "description": "Optional. Auto-upgrade channel on the AKS cluster." + } + }, + "autoNodeOsUpgradeProfileUpgradeChannel": { + "type": "string", + "defaultValue": "Unmanaged", + "allowedValues": [ + "NodeImage", + "None", + "SecurityPatch", + "Unmanaged" + ], + "metadata": { + "description": "Optional. Auto-upgrade channel on the Node Os." + } + }, + "podIdentityProfileAllowNetworkPluginKubenet": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Running in Kubenet is disabled by default due to the security related nature of AAD Pod Identity and the risks of IP spoofing." + } + }, + "podIdentityProfileEnable": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether the pod identity addon is enabled." + } + }, + "podIdentityProfileUserAssignedIdentities": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The pod identities to use in the cluster." + } + }, + "podIdentityProfileUserAssignedIdentityExceptions": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The pod identity exceptions to allow." + } + }, + "enableOidcIssuerProfile": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether the The OIDC issuer profile of the Managed Cluster is enabled." + } + }, + "enableWorkloadIdentity": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to enable Workload Identity. Requires OIDC issuer profile to be enabled." + } + }, + "enableAzureDefender": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to enable Azure Defender." + } + }, + "enableImageCleaner": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to enable Image Cleaner for Kubernetes." + } + }, + "imageCleanerIntervalHours": { + "type": "int", + "defaultValue": 24, + "minValue": 24, + "metadata": { + "description": "Optional. The interval in hours Image Cleaner will run. The maximum value is three months." + } + }, + "enablePodSecurityPolicy": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to enable Kubernetes pod security policy. Requires enabling the pod security policy feature flag on the subscription." + } + }, + "enableStorageProfileBlobCSIDriver": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether the AzureBlob CSI Driver for the storage profile is enabled." + } + }, + "enableStorageProfileDiskCSIDriver": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether the AzureDisk CSI Driver for the storage profile is enabled." + } + }, + "enableStorageProfileFileCSIDriver": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether the AzureFile CSI Driver for the storage profile is enabled." + } + }, + "enableStorageProfileSnapshotController": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether the snapshot controller for the storage profile is enabled." + } + }, + "supportPlan": { + "type": "string", + "defaultValue": "KubernetesOfficial", + "allowedValues": [ + "AKSLongTermSupport", + "KubernetesOfficial" + ], + "metadata": { + "description": "Optional. The support plan for the Managed Cluster." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "omsAgentEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the OMS agent is enabled." + } + }, + "omsAgentUseAADAuth": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifies whether the OMS agent is using managed identity authentication." + } + }, + "monitoringWorkspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the monitoring log analytics workspace." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.ContainerService/managedClusters@2025-05-01#properties/tags" + }, + "description": "Optional. Tags of the resource." + }, + "nullable": true + }, + "diskEncryptionSetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the disc encryption set to apply to the cluster. For security reasons, this value should be provided." + } + }, + "fluxExtension": { + "$ref": "#/definitions/extensionType", + "nullable": true, + "metadata": { + "description": "Optional. Settings and configurations for the flux extension." + } + }, + "httpProxyConfig": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.ContainerService/managedClusters@2025-05-01#properties/properties/properties/httpProxyConfig" + }, + "description": "Optional. Configurations for provisioning the cluster with HTTP proxy servers." + }, + "nullable": true + }, + "identityProfile": { + "type": "object", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.ContainerService/managedClusters@2025-05-01#properties/properties/properties/identityProfile" + }, + "description": "Optional. Identities associated with the cluster." + }, + "nullable": true + }, + "kedaAddon": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables Kubernetes Event-driven Autoscaling (KEDA)." + } + }, + "vpaAddon": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to enable VPA add-on in cluster. Default value is false." + } + }, + "enableAzureMonitorProfileMetrics": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether the metric state of the kubenetes cluster is enabled." + } + }, + "enableContainerInsights": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates if Azure Monitor Container Insights Logs Addon is enabled." + } + }, + "disableCustomMetrics": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether custom metrics collection has to be disabled or not. If not specified the default is false. No custom metrics will be emitted if this field is false but the container insights enabled field is false." + } + }, + "disablePrometheusMetricsScraping": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether prometheus metrics scraping is disabled or not. If not specified the default is false. No prometheus metrics will be emitted if this field is false but the container insights enabled field is false." + } + }, + "syslogPort": { + "type": "int", + "defaultValue": 28330, + "metadata": { + "description": "Optional. The syslog host port. If not specified, the default port is 28330." + } }, - "suffix": { + "metricLabelsAllowlist": { "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id)]" + "defaultValue": "", + "metadata": { + "description": "Optional. A comma-separated list of kubernetes cluster metrics labels." + } }, - "name": { + "metricAnnotationsAllowList": { "type": "string", - "minLength": 2, - "maxLength": 60, + "defaultValue": "", "metadata": { - "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." + "description": "Optional. A comma-separated list of Kubernetes cluster metrics annotations." } }, - "sku": { - "type": "string", - "defaultValue": "standard", - "allowedValues": [ - "free", - "basic", - "standard", - "standard2", - "standard3", - "storage_optimized_l1", - "storage_optimized_l2" - ], + "istioServiceMeshEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "The pricing tier of the search service you want to create (for example, basic or standard)." + "description": "Optional. Specifies whether the Istio ServiceMesh add-on is enabled or not." } }, - "replicaCount": { - "type": "int", - "defaultValue": 1, - "minValue": 1, - "maxValue": 12, + "istioServiceMeshRevisions": { + "type": "array", + "nullable": true, "metadata": { - "description": "Replicas distribute search workloads across the service. You need at least two replicas to support high availability of query workloads (not applicable to the free tier)." + "description": "Optional. The list of revisions of the Istio control plane. When an upgrade is not in progress, this holds one value. When canary upgrade is in progress, this can only hold two consecutive values." } }, - "partitionCount": { - "type": "int", - "defaultValue": 1, - "allowedValues": [ - 1, - 2, - 3, - 4, - 6, - 12 - ], + "istioServiceMeshInternalIngressGatewayEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Partitions allow for scaling of document count as well as faster indexing by sharding your index over multiple search units." + "description": "Optional. Specifies whether the Internal Istio Ingress Gateway is enabled or not." } }, - "hostingMode": { - "type": "string", - "defaultValue": "default", - "allowedValues": [ - "default", - "highDensity" - ], + "istioServiceMeshExternalIngressGatewayEnabled": { + "type": "bool", + "defaultValue": false, "metadata": { - "description": "Applicable only for SKUs set to standard3. You can set this property to enable a single, high density partition that allows up to 1000 indexes, which is much higher than the maximum indexes allowed for any other SKU." + "description": "Optional. Specifies whether the External Istio Ingress Gateway is enabled or not." } }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", + "istioServiceMeshCertificateAuthority": { + "$ref": "#/definitions/istioServiceMeshCertificateAuthorityType", + "nullable": true, "metadata": { - "description": "Location for all resources." + "description": "Optional. The Istio Certificate Authority definition." } } }, - "resources": [ - { - "type": "Microsoft.Search/searchServices", - "apiVersion": "2023-11-01", + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "enableReferencedModulesTelemetry": false, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Azure Kubernetes Fleet Manager Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '63bb64ad-9799-4770-b5c3-24ed299a07bf')]", + "Azure Kubernetes Fleet Manager RBAC Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '434fb43a-c01c-447e-9f67-c3ad923cfaba')]", + "Azure Kubernetes Fleet Manager RBAC Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18ab4d3d-a1bf-4477-8ad9-8359bc988f69')]", + "Azure Kubernetes Fleet Manager RBAC Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '30b27cfc-9c84-438e-b0ce-70e35255df80')]", + "Azure Kubernetes Fleet Manager RBAC Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5af6afb3-c06c-4fa4-8848-71a8aee05683')]", + "Azure Kubernetes Service Cluster Admin Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0ab0b1a8-8aac-4efd-b8c2-3ee1fb270be8')]", + "Azure Kubernetes Service Cluster Monitoring User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1afdec4b-e479-420e-99e7-f82237c7c5e6')]", + "Azure Kubernetes Service Cluster User Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4abbcc35-e782-43d8-92c5-2d3f1bd2253f')]", + "Azure Kubernetes Service Contributor Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ed7f3fbd-7b88-4dd4-9017-9adb7ce333f8')]", + "Azure Kubernetes Service RBAC Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3498e952-d568-435e-9b2c-8d77e338d7f7')]", + "Azure Kubernetes Service RBAC Cluster Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b1ff04bb-8a4e-4dc4-8eb5-8693973ce19b')]", + "Azure Kubernetes Service RBAC Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f6c6a51-bcf8-42ba-9220-52d62157d7db')]", + "Azure Kubernetes Service RBAC Writer": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7ffa36f-339b-4b5c-8bdf-e2c188b2c0eb')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Kubernetes Agentless Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'd5a2ae44-610b-4500-93be-660a0c5f5ca6')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.containerservice-managedcluster.{0}.{1}', replace('0.10.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "managedCluster": { + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2024-09-02-preview", "name": "[parameters('name')]", "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", "sku": { - "name": "[parameters('sku')]" + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" }, "properties": { - "replicaCount": "[parameters('replicaCount')]", - "partitionCount": "[parameters('partitionCount')]", - "hostingMode": "[parameters('hostingMode')]", - "authOptions": { - "aadOrApiKey": {} - } + "agentPoolProfiles": "[map(parameters('primaryAgentPoolProfiles'), lambda('profile', createObject('name', lambdaVariables('profile').name, 'count', coalesce(tryGet(lambdaVariables('profile'), 'count'), 1), 'availabilityZones', map(coalesce(tryGet(lambdaVariables('profile'), 'availabilityZones'), createArray(1, 2, 3)), lambda('zone', format('{0}', lambdaVariables('zone')))), 'creationData', if(not(empty(tryGet(lambdaVariables('profile'), 'sourceResourceId'))), createObject('sourceResourceId', tryGet(lambdaVariables('profile'), 'sourceResourceId')), null()), 'enableAutoScaling', coalesce(tryGet(lambdaVariables('profile'), 'enableAutoScaling'), false()), 'enableEncryptionAtHost', coalesce(tryGet(lambdaVariables('profile'), 'enableEncryptionAtHost'), false()), 'enableFIPS', coalesce(tryGet(lambdaVariables('profile'), 'enableFIPS'), false()), 'enableNodePublicIP', coalesce(tryGet(lambdaVariables('profile'), 'enableNodePublicIP'), false()), 'enableUltraSSD', coalesce(tryGet(lambdaVariables('profile'), 'enableUltraSSD'), false()), 'gpuInstanceProfile', tryGet(lambdaVariables('profile'), 'gpuInstanceProfile'), 'kubeletDiskType', tryGet(lambdaVariables('profile'), 'kubeletDiskType'), 'maxCount', tryGet(lambdaVariables('profile'), 'maxCount'), 'maxPods', tryGet(lambdaVariables('profile'), 'maxPods'), 'minCount', tryGet(lambdaVariables('profile'), 'minCount'), 'mode', tryGet(lambdaVariables('profile'), 'mode'), 'nodeLabels', tryGet(lambdaVariables('profile'), 'nodeLabels'), 'nodePublicIPPrefixID', tryGet(lambdaVariables('profile'), 'nodePublicIpPrefixResourceId'), 'nodeTaints', tryGet(lambdaVariables('profile'), 'nodeTaints'), 'orchestratorVersion', tryGet(lambdaVariables('profile'), 'orchestratorVersion'), 'osDiskSizeGB', tryGet(lambdaVariables('profile'), 'osDiskSizeGB'), 'osDiskType', tryGet(lambdaVariables('profile'), 'osDiskType'), 'osType', coalesce(tryGet(lambdaVariables('profile'), 'osType'), 'Linux'), 'osSKU', tryGet(lambdaVariables('profile'), 'osSKU'), 'podSubnetID', tryGet(lambdaVariables('profile'), 'podSubnetResourceId'), 'proximityPlacementGroupID', tryGet(lambdaVariables('profile'), 'proximityPlacementGroupResourceId'), 'scaleDownMode', coalesce(tryGet(lambdaVariables('profile'), 'scaleDownMode'), 'Delete'), 'scaleSetEvictionPolicy', coalesce(tryGet(lambdaVariables('profile'), 'scaleSetEvictionPolicy'), 'Delete'), 'scaleSetPriority', tryGet(lambdaVariables('profile'), 'scaleSetPriority'), 'securityProfile', createObject('enableSecureBoot', coalesce(tryGet(lambdaVariables('profile'), 'enableSecureBoot'), false()), 'enableVTPM', coalesce(tryGet(lambdaVariables('profile'), 'enableVTPM'), false()), 'sshAccess', if(equals(parameters('skuName'), 'Automatic'), 'Disabled', 'LocalUser')), 'spotMaxPrice', tryGet(lambdaVariables('profile'), 'spotMaxPrice'), 'tags', tryGet(lambdaVariables('profile'), 'tags'), 'type', tryGet(lambdaVariables('profile'), 'type'), 'upgradeSettings', createObject('maxSurge', tryGet(lambdaVariables('profile'), 'maxSurge')), 'vmSize', coalesce(tryGet(lambdaVariables('profile'), 'vmSize'), 'Standard_D2s_v3'), 'vnetSubnetID', tryGet(lambdaVariables('profile'), 'vnetSubnetResourceId'), 'workloadRuntime', tryGet(lambdaVariables('profile'), 'workloadRuntime'))))]", + "httpProxyConfig": "[parameters('httpProxyConfig')]", + "identityProfile": "[parameters('identityProfile')]", + "diskEncryptionSetID": "[parameters('diskEncryptionSetResourceId')]", + "kubernetesVersion": "[parameters('kubernetesVersion')]", + "dnsPrefix": "[parameters('dnsPrefix')]", + "linuxProfile": "[if(not(empty(parameters('sshPublicKey'))), createObject('adminUsername', parameters('adminUsername'), 'ssh', createObject('publicKeys', createArray(createObject('keyData', coalesce(parameters('sshPublicKey'), ''))))), null())]", + "servicePrincipalProfile": "[parameters('aksServicePrincipalProfile')]", + "metricsProfile": { + "costAnalysis": { + "enabled": "[if(equals(parameters('skuTier'), 'free'), false(), parameters('costAnalysisEnabled'))]" + } + }, + "ingressProfile": { + "webAppRouting": { + "enabled": "[parameters('webApplicationRoutingEnabled')]", + "dnsZoneResourceIds": "[if(not(empty(parameters('dnsZoneResourceId'))), createArray(parameters('dnsZoneResourceId')), null())]", + "nginx": "[if(not(empty(parameters('defaultIngressControllerType'))), createObject('defaultIngressControllerType', parameters('defaultIngressControllerType')), null())]" + } + }, + "addonProfiles": { + "httpApplicationRouting": { + "enabled": "[parameters('httpApplicationRoutingEnabled')]" + }, + "ingressApplicationGateway": { + "enabled": "[and(parameters('ingressApplicationGatewayEnabled'), not(empty(parameters('appGatewayResourceId'))))]", + "config": "[if(and(parameters('ingressApplicationGatewayEnabled'), not(empty(parameters('appGatewayResourceId')))), createObject('applicationGatewayId', parameters('appGatewayResourceId'), 'effectiveApplicationGatewayId', parameters('appGatewayResourceId')), null())]" + }, + "omsagent": { + "enabled": "[and(parameters('omsAgentEnabled'), not(empty(parameters('monitoringWorkspaceResourceId'))))]", + "config": "[if(and(parameters('omsAgentEnabled'), not(empty(parameters('monitoringWorkspaceResourceId')))), shallowMerge(createArray(createObject('logAnalyticsWorkspaceResourceID', parameters('monitoringWorkspaceResourceId')), if(parameters('omsAgentUseAADAuth'), createObject('useAADAuth', 'true'), createObject()))), null())]" + }, + "aciConnectorLinux": { + "enabled": "[parameters('aciConnectorLinuxEnabled')]" + }, + "azurepolicy": { + "enabled": "[parameters('azurePolicyEnabled')]", + "config": "[if(parameters('azurePolicyEnabled'), createObject('version', parameters('azurePolicyVersion')), null())]" + }, + "openServiceMesh": { + "enabled": "[parameters('openServiceMeshEnabled')]", + "config": "[if(parameters('openServiceMeshEnabled'), createObject(), null())]" + }, + "kubeDashboard": { + "enabled": "[parameters('kubeDashboardEnabled')]" + }, + "azureKeyvaultSecretsProvider": { + "enabled": "[parameters('enableKeyvaultSecretsProvider')]", + "config": "[if(parameters('enableKeyvaultSecretsProvider'), createObject('enableSecretRotation', toLower(string(parameters('enableSecretRotation')))), null())]" + } + }, + "oidcIssuerProfile": "[if(parameters('enableOidcIssuerProfile'), createObject('enabled', parameters('enableOidcIssuerProfile')), null())]", + "enableRBAC": "[parameters('enableRBAC')]", + "disableLocalAccounts": "[parameters('disableLocalAccounts')]", + "nodeResourceGroup": "[parameters('nodeResourceGroup')]", + "nodeResourceGroupProfile": "[parameters('nodeResourceGroupProfile')]", + "nodeProvisioningProfile": "[if(not(empty(parameters('nodeProvisioningProfileMode'))), createObject('mode', parameters('nodeProvisioningProfileMode')), null())]", + "enablePodSecurityPolicy": "[parameters('enablePodSecurityPolicy')]", + "workloadAutoScalerProfile": { + "keda": { + "enabled": "[parameters('kedaAddon')]" + }, + "verticalPodAutoscaler": { + "enabled": "[parameters('vpaAddon')]" + } + }, + "networkProfile": { + "networkDataplane": "[parameters('networkDataplane')]", + "networkPlugin": "[parameters('networkPlugin')]", + "networkPluginMode": "[if(equals(parameters('networkDataplane'), 'cilium'), 'overlay', parameters('networkPluginMode'))]", + "networkPolicy": "[if(equals(parameters('networkDataplane'), 'cilium'), 'cilium', parameters('networkPolicy'))]", + "podCidr": "[parameters('podCidr')]", + "serviceCidr": "[parameters('serviceCidr')]", + "dnsServiceIP": "[parameters('dnsServiceIP')]", + "outboundType": "[parameters('outboundType')]", + "loadBalancerSku": "[parameters('loadBalancerSku')]", + "loadBalancerProfile": { + "allocatedOutboundPorts": "[parameters('allocatedOutboundPorts')]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "managedOutboundIPs": "[if(not(equals(parameters('managedOutboundIPCount'), 0)), createObject('count', parameters('managedOutboundIPCount')), null())]", + "effectiveOutboundIPs": [], + "backendPoolType": "[parameters('backendPoolType')]", + "outboundIPPrefixes": "[if(not(empty(parameters('outboundPublicIPPrefixResourceIds'))), createObject('publicIPPrefixes', map(coalesce(parameters('outboundPublicIPPrefixResourceIds'), createArray()), lambda('id', createObject('id', lambdaVariables('id'))))), null())]", + "outboundIPs": "[if(not(empty(parameters('outboundPublicIPResourceIds'))), createObject('publicIPs', map(coalesce(parameters('outboundPublicIPResourceIds'), createArray()), lambda('id', createObject('id', lambdaVariables('id'))))), null())]" + } + }, + "publicNetworkAccess": "[parameters('publicNetworkAccess')]", + "aadProfile": "[if(not(empty(parameters('aadProfile'))), createObject('clientAppID', tryGet(parameters('aadProfile'), 'aadProfileClientAppID'), 'serverAppID', tryGet(parameters('aadProfile'), 'aadProfileServerAppID'), 'serverAppSecret', tryGet(parameters('aadProfile'), 'aadProfileServerAppSecret'), 'managed', tryGet(parameters('aadProfile'), 'aadProfileManaged'), 'enableAzureRBAC', tryGet(parameters('aadProfile'), 'aadProfileEnableAzureRBAC'), 'adminGroupObjectIDs', tryGet(parameters('aadProfile'), 'aadProfileAdminGroupObjectIDs'), 'tenantID', tryGet(parameters('aadProfile'), 'aadProfileTenantId')), null())]", + "autoScalerProfile": { + "balance-similar-node-groups": "[toLower(string(parameters('autoScalerProfileBalanceSimilarNodeGroups')))]", + "expander": "[parameters('autoScalerProfileExpander')]", + "max-empty-bulk-delete": "[format('{0}', parameters('autoScalerProfileMaxEmptyBulkDelete'))]", + "max-graceful-termination-sec": "[format('{0}', parameters('autoScalerProfileMaxGracefulTerminationSec'))]", + "max-node-provision-time": "[parameters('autoScalerProfileMaxNodeProvisionTime')]", + "max-total-unready-percentage": "[format('{0}', parameters('autoScalerProfileMaxTotalUnreadyPercentage'))]", + "new-pod-scale-up-delay": "[parameters('autoScalerProfileNewPodScaleUpDelay')]", + "ok-total-unready-count": "[format('{0}', parameters('autoScalerProfileOkTotalUnreadyCount'))]", + "scale-down-delay-after-add": "[parameters('autoScalerProfileScaleDownDelayAfterAdd')]", + "scale-down-delay-after-delete": "[parameters('autoScalerProfileScaleDownDelayAfterDelete')]", + "scale-down-delay-after-failure": "[parameters('autoScalerProfileScaleDownDelayAfterFailure')]", + "scale-down-unneeded-time": "[parameters('autoScalerProfileScaleDownUnneededTime')]", + "scale-down-unready-time": "[parameters('autoScalerProfileScaleDownUnreadyTime')]", + "scale-down-utilization-threshold": "[parameters('autoScalerProfileUtilizationThreshold')]", + "scan-interval": "[parameters('autoScalerProfileScanInterval')]", + "skip-nodes-with-local-storage": "[toLower(string(parameters('autoScalerProfileSkipNodesWithLocalStorage')))]", + "skip-nodes-with-system-pods": "[toLower(string(parameters('autoScalerProfileSkipNodesWithSystemPods')))]" + }, + "autoUpgradeProfile": { + "upgradeChannel": "[parameters('autoUpgradeProfileUpgradeChannel')]", + "nodeOSUpgradeChannel": "[parameters('autoNodeOsUpgradeProfileUpgradeChannel')]" + }, + "apiServerAccessProfile": { + "authorizedIPRanges": "[parameters('authorizedIPRanges')]", + "disableRunCommand": "[parameters('disableRunCommand')]", + "enablePrivateCluster": "[parameters('enablePrivateCluster')]", + "enablePrivateClusterPublicFQDN": "[parameters('enablePrivateClusterPublicFQDN')]", + "privateDNSZone": "[parameters('privateDNSZone')]" + }, + "azureMonitorProfile": { + "containerInsights": "[if(parameters('enableContainerInsights'), createObject('enabled', parameters('enableContainerInsights'), 'logAnalyticsWorkspaceResourceId', if(not(empty(parameters('monitoringWorkspaceResourceId'))), parameters('monitoringWorkspaceResourceId'), null()), 'disableCustomMetrics', parameters('disableCustomMetrics'), 'disablePrometheusMetricsScraping', parameters('disablePrometheusMetricsScraping'), 'syslogPort', parameters('syslogPort')), null())]", + "metrics": "[if(parameters('enableAzureMonitorProfileMetrics'), createObject('enabled', parameters('enableAzureMonitorProfileMetrics'), 'kubeStateMetrics', createObject('metricLabelsAllowlist', parameters('metricLabelsAllowlist'), 'metricAnnotationsAllowList', parameters('metricAnnotationsAllowList'))), null())]" + }, + "podIdentityProfile": { + "allowNetworkPluginKubenet": "[parameters('podIdentityProfileAllowNetworkPluginKubenet')]", + "enabled": "[parameters('podIdentityProfileEnable')]", + "userAssignedIdentities": "[parameters('podIdentityProfileUserAssignedIdentities')]", + "userAssignedIdentityExceptions": "[parameters('podIdentityProfileUserAssignedIdentityExceptions')]" + }, + "securityProfile": { + "defender": "[if(parameters('enableAzureDefender'), createObject('securityMonitoring', createObject('enabled', parameters('enableAzureDefender')), 'logAnalyticsWorkspaceResourceId', parameters('monitoringWorkspaceResourceId')), null())]", + "workloadIdentity": "[if(parameters('enableWorkloadIdentity'), createObject('enabled', parameters('enableWorkloadIdentity')), null())]", + "imageCleaner": "[if(parameters('enableImageCleaner'), createObject('enabled', parameters('enableImageCleaner'), 'intervalHours', parameters('imageCleanerIntervalHours')), null())]" + }, + "storageProfile": { + "blobCSIDriver": { + "enabled": "[parameters('enableStorageProfileBlobCSIDriver')]" + }, + "diskCSIDriver": { + "enabled": "[if(and(equals(parameters('costAnalysisEnabled'), true()), not(equals(parameters('skuTier'), 'free'))), true(), parameters('enableStorageProfileDiskCSIDriver'))]" + }, + "fileCSIDriver": { + "enabled": "[parameters('enableStorageProfileFileCSIDriver')]" + }, + "snapshotController": { + "enabled": "[parameters('enableStorageProfileSnapshotController')]" + } + }, + "supportPlan": "[parameters('supportPlan')]", + "serviceMeshProfile": "[if(parameters('istioServiceMeshEnabled'), createObject('istio', createObject('revisions', if(not(empty(parameters('istioServiceMeshRevisions'))), parameters('istioServiceMeshRevisions'), null()), 'components', createObject('ingressGateways', createArray(createObject('enabled', parameters('istioServiceMeshInternalIngressGatewayEnabled'), 'mode', 'Internal'), createObject('enabled', parameters('istioServiceMeshExternalIngressGatewayEnabled'), 'mode', 'External'))), 'certificateAuthority', if(not(empty(parameters('istioServiceMeshCertificateAuthority'))), createObject('plugin', createObject('certChainObjectName', tryGet(parameters('istioServiceMeshCertificateAuthority'), 'certChainObjectName'), 'certObjectName', tryGet(parameters('istioServiceMeshCertificateAuthority'), 'certObjectName'), 'keyObjectName', tryGet(parameters('istioServiceMeshCertificateAuthority'), 'keyObjectName'), 'keyVaultId', tryGet(parameters('istioServiceMeshCertificateAuthority'), 'keyVaultResourceId'), 'rootCertObjectName', tryGet(parameters('istioServiceMeshCertificateAuthority'), 'rootCertObjectName'))), null())), 'mode', 'Istio'), null())]" } }, - { + "managedCluster_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[coalesce(tryGet(parameters('lock'), 'notes'), if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.'))]" + }, + "dependsOn": [ + "managedCluster" + ] + }, + "managedCluster_diagnosticSettings": { + "copy": { + "name": "managedCluster_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "managedCluster" + ] + }, + "managedCluster_roleAssignments": { + "copy": { + "name": "managedCluster_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", - "name": "[guid(format('Search Index Data Contributor-{0}', parameters('suffix')))]", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerService/managedClusters', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" + "managedCluster" ] }, - { + "dnsZone": { + "condition": "[and(and(equals(parameters('enableDnsZoneContributorRoleAssignment'), true()), not(equals(parameters('dnsZoneResourceId'), null()))), parameters('webApplicationRoutingEnabled'))]", + "existing": true, + "type": "Microsoft.Network/dnsZones", + "apiVersion": "2018-05-01", + "name": "[last(split(if(not(empty(parameters('dnsZoneResourceId'))), parameters('dnsZoneResourceId'), '/dummmyZone'), '/'))]" + }, + "dnsZone_roleAssignment": { + "condition": "[and(and(equals(parameters('enableDnsZoneContributorRoleAssignment'), true()), not(equals(parameters('dnsZoneResourceId'), null()))), parameters('webApplicationRoutingEnabled'))]", "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('name'))]", - "name": "[guid(format('Search Service Contributor-{0}', parameters('suffix')))]", + "scope": "[format('Microsoft.Network/dnsZones/{0}', last(split(if(not(empty(parameters('dnsZoneResourceId'))), parameters('dnsZoneResourceId'), '/dummmyZone'), '/')))]", + "name": "[guid(resourceId('Microsoft.Network/dnsZones', last(split(if(not(empty(parameters('dnsZoneResourceId'))), parameters('dnsZoneResourceId'), '/dummmyZone'), '/'))), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314'), 'DNS Zone Contributor')]", "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", + "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "principalId": "[reference('managedCluster').ingressProfile.webAppRouting.identity.objectId]", "principalType": "ServicePrincipal" }, "dependsOn": [ - "[resourceId('Microsoft.Search/searchServices', parameters('name'))]" + "managedCluster" + ] + }, + "managedCluster_maintenanceConfigurations": { + "copy": { + "name": "managedCluster_maintenanceConfigurations", + "count": "[length(coalesce(parameters('maintenanceConfigurations'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ManagedCluster-MaintenanceCfg-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('maintenanceConfigurations'), createArray())[copyIndex()].name]" + }, + "maintenanceWindow": { + "value": "[coalesce(parameters('maintenanceConfigurations'), createArray())[copyIndex()].maintenanceWindow]" + }, + "managedClusterName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "15214623384404752025" + }, + "name": "Azure Kubernetes Service (AKS) Managed Cluster Maintenance Configurations", + "description": "This module deploys an Azure Kubernetes Service (AKS) Managed Cluster Maintenance Configurations." + }, + "parameters": { + "maintenanceWindow": { + "type": "object", + "metadata": { + "description": "Required. Maintenance window for the maintenance configuration." + } + }, + "managedClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "aksManagedAutoUpgradeSchedule", + "metadata": { + "description": "Optional. Name of the maintenance configuration." + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerService/managedClusters/maintenanceConfigurations", + "apiVersion": "2023-10-01", + "name": "[format('{0}/{1}', parameters('managedClusterName'), parameters('name'))]", + "properties": { + "maintenanceWindow": "[parameters('maintenanceWindow')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the maintenance configuration." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the maintenance configuration." + }, + "value": "[resourceId('Microsoft.ContainerService/managedClusters/maintenanceConfigurations', parameters('managedClusterName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the agent pool was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedCluster" + ] + }, + "managedCluster_agentPools": { + "copy": { + "name": "managedCluster_agentPools", + "count": "[length(coalesce(parameters('agentPools'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ManagedCluster-AgentPool-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "managedClusterName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('agentPools'), createArray())[copyIndex()].name]" + }, + "availabilityZones": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'availabilityZones')]" + }, + "count": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'count')]" + }, + "sourceResourceId": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'sourceResourceId')]" + }, + "enableAutoScaling": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'enableAutoScaling')]" + }, + "enableEncryptionAtHost": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'enableEncryptionAtHost')]" + }, + "enableFIPS": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'enableFIPS')]" + }, + "enableNodePublicIP": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'enableNodePublicIP')]" + }, + "enableUltraSSD": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'enableUltraSSD')]" + }, + "gpuInstanceProfile": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'gpuInstanceProfile')]" + }, + "kubeletDiskType": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'kubeletDiskType')]" + }, + "maxCount": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'maxCount')]" + }, + "maxPods": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'maxPods')]" + }, + "minCount": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'minCount')]" + }, + "mode": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'mode')]" + }, + "nodeLabels": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'nodeLabels')]" + }, + "nodePublicIpPrefixResourceId": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'nodePublicIpPrefixResourceId')]" + }, + "nodeTaints": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'nodeTaints')]" + }, + "orchestratorVersion": { + "value": "[coalesce(tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'orchestratorVersion'), parameters('kubernetesVersion'))]" + }, + "osDiskSizeGB": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'osDiskSizeGB')]" + }, + "osDiskType": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'osDiskType')]" + }, + "osSKU": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'osSKU')]" + }, + "osType": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'osType')]" + }, + "podSubnetResourceId": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'podSubnetResourceId')]" + }, + "proximityPlacementGroupResourceId": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'proximityPlacementGroupResourceId')]" + }, + "scaleDownMode": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'scaleDownMode')]" + }, + "scaleSetEvictionPolicy": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'scaleSetEvictionPolicy')]" + }, + "scaleSetPriority": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'scaleSetPriority')]" + }, + "spotMaxPrice": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'spotMaxPrice')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "type": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'type')]" + }, + "maxSurge": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'maxSurge')]" + }, + "vmSize": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'vmSize')]" + }, + "vnetSubnetResourceId": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'vnetSubnetResourceId')]" + }, + "workloadRuntime": { + "value": "[tryGet(coalesce(parameters('agentPools'), createArray())[copyIndex()], 'workloadRuntime')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "7002414833582441484" + }, + "name": "Azure Kubernetes Service (AKS) Managed Cluster Agent Pools", + "description": "This module deploys an Azure Kubernetes Service (AKS) Managed Cluster Agent Pool." + }, + "parameters": { + "managedClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent managed cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the agent pool." + } + }, + "availabilityZones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. The list of Availability zones to use for nodes. This can only be specified if the AgentPoolType property is \"VirtualMachineScaleSets\"." + } + }, + "count": { + "type": "int", + "defaultValue": 1, + "minValue": 0, + "maxValue": 1000, + "metadata": { + "description": "Optional. Desired Number of agents (VMs) specified to host docker containers. Allowed values must be in the range of 0 to 1000 (inclusive) for user pools and in the range of 1 to 1000 (inclusive) for system pools. The default value is 1." + } + }, + "sourceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. This is the ARM ID of the source object to be used to create the target object." + } + }, + "enableAutoScaling": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to enable auto-scaler." + } + }, + "enableEncryptionAtHost": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This is only supported on certain VM sizes and in certain Azure regions. For more information, see: /azure/aks/enable-host-encryption. For security reasons, this setting should be enabled." + } + }, + "enableFIPS": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. See Add a FIPS-enabled node pool (https://learn.microsoft.com/en-us/azure/aks/use-multiple-node-pools#add-a-fips-enabled-node-pool-preview) for more details." + } + }, + "enableNodePublicIP": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Some scenarios may require nodes in a node pool to receive their own dedicated public IP addresses. A common scenario is for gaming workloads, where a console needs to make a direct connection to a cloud virtual machine to minimize hops. For more information see assigning a public IP per node (https://learn.microsoft.com/en-us/azure/aks/use-multiple-node-pools#assign-a-public-ip-per-node-for-your-node-pools)." + } + }, + "enableUltraSSD": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether to enable UltraSSD." + } + }, + "gpuInstanceProfile": { + "type": "string", + "nullable": true, + "allowedValues": [ + "MIG1g", + "MIG2g", + "MIG3g", + "MIG4g", + "MIG7g" + ], + "metadata": { + "description": "Optional. GPUInstanceProfile to be used to specify GPU MIG instance profile for supported GPU VM SKU." + } + }, + "kubeletDiskType": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Determines the placement of emptyDir volumes, container runtime data root, and Kubelet ephemeral storage." + } + }, + "maxCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The maximum number of nodes for auto-scaling." + } + }, + "maxPods": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The maximum number of pods that can run on a node." + } + }, + "minCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The minimum number of nodes for auto-scaling." + } + }, + "mode": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A cluster must have at least one \"System\" Agent Pool at all times. For additional information on agent pool restrictions and best practices, see: /azure/aks/use-system-pools." + } + }, + "nodeLabels": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The node labels to be persisted across all nodes in agent pool." + } + }, + "nodePublicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. ResourceId of the node PublicIPPrefix." + } + }, + "nodeTaints": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The taints added to new nodes during node pool create and scale. For example, key=value:NoSchedule." + } + }, + "orchestratorVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. As a best practice, you should upgrade all node pools in an AKS cluster to the same Kubernetes version. The node pool version must have the same major version as the control plane. The node pool minor version must be within two minor versions of the control plane version. The node pool version cannot be greater than the control plane version. For more information see upgrading a node pool (https://learn.microsoft.com/en-us/azure/aks/use-multiple-node-pools#upgrade-a-node-pool)." + } + }, + "osDiskSizeGB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. OS Disk Size in GB to be used to specify the disk size for every machine in the master/agent pool. If you specify 0, it will apply the default osDisk size according to the vmSize specified." + } + }, + "osDiskType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Ephemeral", + "Managed" + ], + "metadata": { + "description": "Optional. The default is \"Ephemeral\" if the VM supports it and has a cache disk larger than the requested OSDiskSizeGB. Otherwise, defaults to \"Managed\". May not be changed after creation. For more information see Ephemeral OS (https://learn.microsoft.com/en-us/azure/aks/cluster-configuration#ephemeral-os)." + } + }, + "osSKU": { + "type": "string", + "nullable": true, + "allowedValues": [ + "AzureLinux", + "CBLMariner", + "Ubuntu", + "Windows2019", + "Windows2022" + ], + "metadata": { + "description": "Optional. Specifies the OS SKU used by the agent pool. The default is Ubuntu if OSType is Linux. The default is Windows2019 when Kubernetes <= 1.24 or Windows2022 when Kubernetes >= 1.25 if OSType is Windows." + } + }, + "osType": { + "type": "string", + "defaultValue": "Linux", + "allowedValues": [ + "Linux", + "Windows" + ], + "metadata": { + "description": "Optional. The operating system type. The default is Linux." + } + }, + "podSubnetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Subnet resource ID for the pod IPs. If omitted, pod IPs are statically assigned on the node subnet (see vnetSubnetID for more details). This is of the form: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{virtualNetworkName}/subnets/{subnetName}." + } + }, + "proximityPlacementGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The ID for the Proximity Placement Group." + } + }, + "scaleDownMode": { + "type": "string", + "defaultValue": "Delete", + "allowedValues": [ + "Deallocate", + "Delete" + ], + "metadata": { + "description": "Optional. Describes how VMs are added to or removed from Agent Pools. See [billing states](https://learn.microsoft.com/en-us/azure/virtual-machines/states-billing)." + } + }, + "scaleSetEvictionPolicy": { + "type": "string", + "defaultValue": "Delete", + "allowedValues": [ + "Deallocate", + "Delete" + ], + "metadata": { + "description": "Optional. The eviction policy specifies what to do with the VM when it is evicted. The default is Delete. For more information about eviction see spot VMs." + } + }, + "scaleSetPriority": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Regular", + "Spot" + ], + "metadata": { + "description": "Optional. The Virtual Machine Scale Set priority." + } + }, + "enableSecureBoot": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Secure Boot is a feature of Trusted Launch which ensures that only signed operating systems and drivers can boot. For more details, see aka.ms/aks/trustedlaunch." + } + }, + "enableVTPM": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. vTPM is a Trusted Launch feature for configuring a dedicated secure vault for keys and measurements held locally on the node. For more details, see aka.ms/aks/trustedlaunch." + } + }, + "spotMaxPrice": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Possible values are any decimal value greater than zero or -1 which indicates the willingness to pay any on-demand price. For more details on spot pricing, see spot VMs pricing (https://learn.microsoft.com/en-us/azure/virtual-machines/spot-vms#pricing)." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "type": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The type of Agent Pool." + } + }, + "maxSurge": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. This can either be set to an integer (e.g. \"5\") or a percentage (e.g. \"50%\"). If a percentage is specified, it is the percentage of the total agent pool size at the time of the upgrade. For percentages, fractional nodes are rounded up. If not specified, the default is 1. For more information, including best practices, see: /azure/aks/upgrade-cluster#customize-node-surge-upgrade." + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D2s_v3", + "metadata": { + "description": "Optional. VM size. VM size availability varies by region. If a node contains insufficient compute resources (memory, cpu, etc) pods might fail to run correctly. For more details on restricted VM sizes, see: /azure/aks/quotas-skus-regions." + } + }, + "vnetSubnetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Node Subnet ID. If this is not specified, a VNET and subnet will be generated and used. If no podSubnetID is specified, this applies to nodes and pods, otherwise it applies to just nodes. This is of the form: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworks/{virtualNetworkName}/subnets/{subnetName}." + } + }, + "workloadRuntime": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Determines the type of workload a node can run." + } + } + }, + "resources": { + "managedCluster": { + "existing": true, + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2024-09-01", + "name": "[parameters('managedClusterName')]" + }, + "agentPool": { + "type": "Microsoft.ContainerService/managedClusters/agentPools", + "apiVersion": "2024-09-01", + "name": "[format('{0}/{1}', parameters('managedClusterName'), parameters('name'))]", + "properties": { + "availabilityZones": "[map(coalesce(parameters('availabilityZones'), createArray()), lambda('zone', format('{0}', lambdaVariables('zone'))))]", + "count": "[parameters('count')]", + "creationData": "[if(not(empty(parameters('sourceResourceId'))), createObject('sourceResourceId', parameters('sourceResourceId')), null())]", + "enableAutoScaling": "[parameters('enableAutoScaling')]", + "enableEncryptionAtHost": "[parameters('enableEncryptionAtHost')]", + "enableFIPS": "[parameters('enableFIPS')]", + "enableNodePublicIP": "[parameters('enableNodePublicIP')]", + "enableUltraSSD": "[parameters('enableUltraSSD')]", + "gpuInstanceProfile": "[parameters('gpuInstanceProfile')]", + "kubeletDiskType": "[parameters('kubeletDiskType')]", + "maxCount": "[parameters('maxCount')]", + "maxPods": "[parameters('maxPods')]", + "minCount": "[parameters('minCount')]", + "mode": "[parameters('mode')]", + "nodeLabels": "[parameters('nodeLabels')]", + "nodePublicIPPrefixID": "[parameters('nodePublicIpPrefixResourceId')]", + "nodeTaints": "[parameters('nodeTaints')]", + "orchestratorVersion": "[parameters('orchestratorVersion')]", + "osDiskSizeGB": "[parameters('osDiskSizeGB')]", + "osDiskType": "[parameters('osDiskType')]", + "osSKU": "[parameters('osSKU')]", + "osType": "[parameters('osType')]", + "podSubnetID": "[parameters('podSubnetResourceId')]", + "proximityPlacementGroupID": "[parameters('proximityPlacementGroupResourceId')]", + "scaleDownMode": "[parameters('scaleDownMode')]", + "scaleSetEvictionPolicy": "[parameters('scaleSetEvictionPolicy')]", + "scaleSetPriority": "[parameters('scaleSetPriority')]", + "securityProfile": { + "enableSecureBoot": "[parameters('enableSecureBoot')]", + "enableVTPM": "[parameters('enableVTPM')]" + }, + "spotMaxPrice": "[parameters('spotMaxPrice')]", + "tags": "[parameters('tags')]", + "type": "[parameters('type')]", + "upgradeSettings": { + "maxSurge": "[parameters('maxSurge')]" + }, + "vmSize": "[parameters('vmSize')]", + "vnetSubnetID": "[parameters('vnetSubnetResourceId')]", + "workloadRuntime": "[parameters('workloadRuntime')]" + } + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the agent pool." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the agent pool." + }, + "value": "[resourceId('Microsoft.ContainerService/managedClusters/agentPools', parameters('managedClusterName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the agent pool was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedCluster" + ] + }, + "managedCluster_extension": { + "condition": "[not(empty(parameters('fluxExtension')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ManagedCluster-FluxExtension', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "clusterName": { + "value": "[parameters('name')]" + }, + "configurationProtectedSettings": { + "value": "[tryGet(parameters('fluxExtension'), 'configurationProtectedSettings')]" + }, + "configurationSettings": { + "value": "[tryGet(parameters('fluxExtension'), 'configurationSettings')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "extensionType": { + "value": "microsoft.flux" + }, + "fluxConfigurations": { + "value": "[tryGet(parameters('fluxExtension'), 'configurations')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "name": { + "value": "[coalesce(tryGet(parameters('fluxExtension'), 'name'), 'flux')]" + }, + "releaseNamespace": { + "value": "[coalesce(tryGet(parameters('fluxExtension'), 'releaseNamespace'), 'flux-system')]" + }, + "releaseTrain": { + "value": "[coalesce(tryGet(parameters('fluxExtension'), 'releaseTrain'), 'Stable')]" + }, + "version": { + "value": "[tryGet(parameters('fluxExtension'), 'version')]" + }, + "targetNamespace": { + "value": "[tryGet(parameters('fluxExtension'), 'targetNamespace')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "13174696940078832071" + }, + "name": "Kubernetes Configuration Extensions", + "description": "This module deploys a Kubernetes Configuration Extension." + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Flux Configuration." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "clusterName": { + "type": "string", + "metadata": { + "description": "Required. The name of the AKS cluster that should be configured." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "configurationProtectedSettings": { + "type": "secureObject", + "nullable": true, + "metadata": { + "description": "Optional. Configuration settings that are sensitive, as name-value pairs for configuring this extension." + } + }, + "configurationSettings": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Configuration settings, as name-value pairs for configuring this extension." + } + }, + "extensionType": { + "type": "string", + "metadata": { + "description": "Required. Type of the extension, of which this resource is an instance of. It must be one of the Extension Types registered with Microsoft.KubernetesConfiguration by the extension publisher." + } + }, + "releaseTrain": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. ReleaseTrain this extension participates in for auto-upgrade (e.g. Stable, Preview, etc.) - only if autoUpgradeMinorVersion is \"true\"." + } + }, + "releaseNamespace": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Namespace where the extension Release must be placed, for a Cluster scoped extension. If this namespace does not exist, it will be created." + } + }, + "targetNamespace": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Namespace where the extension will be created for an Namespace scoped extension. If this namespace does not exist, it will be created." + } + }, + "version": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Version of the extension for this extension, if it is \"pinned\" to a specific version." + } + }, + "fluxConfigurations": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. A list of flux configuraitons." + } + } + }, + "variables": { + "enableReferencedModulesTelemetry": false + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.kubernetesconfiguration-extension.{0}.{1}', replace('0.3.6', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "managedCluster": { + "existing": true, + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2022-07-01", + "name": "[parameters('clusterName')]" + }, + "extension": { + "type": "Microsoft.KubernetesConfiguration/extensions", + "apiVersion": "2022-03-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[parameters('name')]", + "properties": { + "autoUpgradeMinorVersion": "[if(not(empty(parameters('version'))), false(), true())]", + "configurationProtectedSettings": "[parameters('configurationProtectedSettings')]", + "configurationSettings": "[parameters('configurationSettings')]", + "extensionType": "[parameters('extensionType')]", + "releaseTrain": "[parameters('releaseTrain')]", + "scope": { + "cluster": "[if(not(empty(coalesce(parameters('releaseNamespace'), ''))), createObject('releaseNamespace', parameters('releaseNamespace')), null())]", + "namespace": "[if(not(empty(coalesce(parameters('targetNamespace'), ''))), createObject('targetNamespace', parameters('targetNamespace')), null())]" + }, + "version": "[parameters('version')]" + } + }, + "fluxConfiguration": { + "copy": { + "name": "fluxConfiguration", + "count": "[length(coalesce(parameters('fluxConfigurations'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ManagedCluster-FluxConfiguration{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" + }, + "clusterName": { + "value": "[parameters('clusterName')]" + }, + "scope": { + "value": "[coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()].scope]" + }, + "namespace": { + "value": "[coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()].namespace]" + }, + "sourceKind": "[if(contains(coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()], 'gitRepository'), createObject('value', 'GitRepository'), createObject('value', 'Bucket'))]", + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()], 'name'), toLower(format('{0}-fluxconfiguration{1}', parameters('clusterName'), copyIndex())))]" + }, + "bucket": { + "value": "[tryGet(coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()], 'bucket')]" + }, + "configurationProtectedSettings": { + "value": "[tryGet(coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()], 'configurationProtectedSettings')]" + }, + "gitRepository": { + "value": "[tryGet(coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()], 'gitRepository')]" + }, + "kustomizations": { + "value": "[coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()].kustomizations]" + }, + "suspend": { + "value": "[tryGet(coalesce(parameters('fluxConfigurations'), createArray())[copyIndex()], 'suspend')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.23.1.45101", + "templateHash": "885928168160399718" + }, + "name": "Kubernetes Configuration Flux Configurations", + "description": "This module deploys a Kubernetes Configuration Flux Configuration.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Flux Configuration." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "clusterName": { + "type": "string", + "metadata": { + "description": "Required. The name of the AKS cluster that should be configured." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "bucket": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Conditional. Parameters to reconcile to the GitRepository source kind type. Required if `sourceKind` is `Bucket`." + } + }, + "configurationProtectedSettings": { + "type": "secureObject", + "nullable": true, + "metadata": { + "description": "Optional. Key-value pairs of protected configuration settings for the configuration." + } + }, + "gitRepository": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Conditional. Parameters to reconcile to the GitRepository source kind type. Required if `sourceKind` is `GitRepository`." + } + }, + "kustomizations": { + "type": "object", + "metadata": { + "description": "Required. Array of kustomizations used to reconcile the artifact pulled by the source type on the cluster." + } + }, + "namespace": { + "type": "string", + "metadata": { + "description": "Required. The namespace to which this configuration is installed to. Maximum of 253 lower case alphanumeric characters, hyphen and period only." + } + }, + "scope": { + "type": "string", + "allowedValues": [ + "cluster", + "namespace" + ], + "metadata": { + "description": "Required. Scope at which the configuration will be installed." + } + }, + "sourceKind": { + "type": "string", + "allowedValues": [ + "Bucket", + "GitRepository" + ], + "metadata": { + "description": "Required. Source Kind to pull the configuration data from." + } + }, + "suspend": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether this configuration should suspend its reconciliation of its kustomizations and sources." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.kubernetesconfiguration-fluxconfig.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "managedCluster": { + "existing": true, + "type": "Microsoft.ContainerService/managedClusters", + "apiVersion": "2022-07-01", + "name": "[parameters('clusterName')]" + }, + "fluxConfiguration": { + "type": "Microsoft.KubernetesConfiguration/fluxConfigurations", + "apiVersion": "2023-05-01", + "scope": "[format('Microsoft.ContainerService/managedClusters/{0}', parameters('clusterName'))]", + "name": "[parameters('name')]", + "properties": { + "bucket": "[parameters('bucket')]", + "configurationProtectedSettings": "[parameters('configurationProtectedSettings')]", + "gitRepository": "[parameters('gitRepository')]", + "kustomizations": "[parameters('kustomizations')]", + "namespace": "[parameters('namespace')]", + "scope": "[parameters('scope')]", + "sourceKind": "[parameters('sourceKind')]", + "suspend": "[parameters('suspend')]" + }, + "dependsOn": [ + "managedCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the flux configuration." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the flux configuration." + }, + "value": "[extensionResourceId(resourceId('Microsoft.ContainerService/managedClusters', parameters('clusterName')), 'Microsoft.KubernetesConfiguration/fluxConfigurations', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the flux configuration was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "extension" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the extension." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the extension." + }, + "value": "[extensionResourceId(resourceId('Microsoft.ContainerService/managedClusters', parameters('clusterName')), 'Microsoft.KubernetesConfiguration/extensions', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the extension was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "managedCluster" ] } - ], + }, "outputs": { - "searchName": { + "resourceId": { "type": "string", - "value": "[parameters('name')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('openai-{0}', parameters('suffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "suffix": { - "value": "[parameters('suffix')]" - }, - "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" - }, - "name": { - "value": "[format('km-openai-{0}', parameters('suffix'))]" - }, - "location": { - "value": "[variables('location')]" - }, - "sku": { - "value": { - "name": "S0" - } - }, - "deployments": { - "value": "[variables('openAiDeployments')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "8195898199308451447" + "metadata": { + "description": "The resource ID of the managed cluster." + }, + "value": "[resourceId('Microsoft.ContainerService/managedClusters', parameters('name'))]" }, - "description": "Creates an Azure Cognitive Services instance." - }, - "parameters": { - "suffix": { + "resourceGroupName": { "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id)]" - }, - "managedIdentityPrincipalId": { - "type": "string" + "metadata": { + "description": "The resource group the managed cluster was deployed into." + }, + "value": "[resourceGroup().name]" }, "name": { - "type": "string" - }, - "location": { "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "tags": { - "type": "object", - "defaultValue": {} + "metadata": { + "description": "The name of the managed cluster." + }, + "value": "[parameters('name')]" }, - "customSubDomainName": { + "controlPlaneFQDN": { "type": "string", - "defaultValue": "[parameters('name')]", "metadata": { - "description": "The custom subdomain name used to access the API. Defaults to the value of the name parameter." - } - }, - "deployments": { - "type": "array", - "defaultValue": [] + "description": "The control plane FQDN of the managed cluster." + }, + "value": "[if(parameters('enablePrivateCluster'), reference('managedCluster').privateFQDN, reference('managedCluster').fqdn)]" }, - "kind": { + "systemAssignedMIPrincipalId": { "type": "string", - "defaultValue": "OpenAI" + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('managedCluster', '2024-09-02-preview', 'full'), 'identity'), 'principalId')]" }, - "publicNetworkAccess": { + "kubeletIdentityClientId": { "type": "string", - "defaultValue": "Enabled", - "allowedValues": [ - "Enabled", - "Disabled" - ] - }, - "sku": { - "type": "object", - "defaultValue": { - "name": "S0" - } - }, - "allowedIpRules": { - "type": "array", - "defaultValue": [] - }, - "networkAcls": { - "type": "object", - "defaultValue": "[if(empty(parameters('allowedIpRules')), createObject('defaultAction', 'Allow'), createObject('ipRules', parameters('allowedIpRules'), 'defaultAction', 'Deny'))]" - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "kind": "[parameters('kind')]", - "properties": { - "customSubDomainName": "[parameters('customSubDomainName')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]", - "networkAcls": "[parameters('networkAcls')]", - "disableLocalAuth": true + "nullable": true, + "metadata": { + "description": "The Client ID of the AKS identity." }, - "sku": "[parameters('sku')]" + "value": "[tryGet(tryGet(tryGet(reference('managedCluster'), 'identityProfile'), 'kubeletidentity'), 'clientId')]" }, - { - "copy": { - "name": "deployment", - "count": "[length(parameters('deployments'))]", - "mode": "serial", - "batchSize": 1 + "kubeletIdentityObjectId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The Object ID of the AKS identity." }, - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2023-05-01", - "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())]" + "value": "[tryGet(tryGet(tryGet(reference('managedCluster'), 'identityProfile'), 'kubeletidentity'), 'objectId')]" + }, + "kubeletIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The Resource ID of the AKS identity." }, - "sku": "[if(contains(parameters('deployments')[copyIndex()], 'sku'), parameters('deployments')[copyIndex()].sku, createObject('name', 'Standard', 'capacity', 1))]", - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" - ] + "value": "[tryGet(tryGet(tryGet(reference('managedCluster'), 'identityProfile'), 'kubeletidentity'), 'resourceId')]" }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[guid(format('Cognitive Services OpenAI Contributor-{0}', parameters('suffix')))]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "omsagentIdentityObjectId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The Object ID of the OMS agent identity." }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" - ] + "value": "[tryGet(tryGet(tryGet(tryGet(reference('managedCluster'), 'addonProfiles'), 'omsagent'), 'identity'), 'objectId')]" }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[guid(format('Cognitive Services OpenAI User-{0}', parameters('suffix')))]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "keyvaultIdentityObjectId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The Object ID of the Key Vault Secrets Provider identity." }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" - ] - } - ], - "outputs": { - "endpoint": { + "value": "[tryGet(tryGet(tryGet(tryGet(reference('managedCluster'), 'addonProfiles'), 'azureKeyvaultSecretsProvider'), 'identity'), 'objectId')]" + }, + "keyvaultIdentityClientId": { "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').endpoint]" + "nullable": true, + "metadata": { + "description": "The Client ID of the Key Vault Secrets Provider identity." + }, + "value": "[tryGet(tryGet(tryGet(tryGet(reference('managedCluster'), 'addonProfiles'), 'azureKeyvaultSecretsProvider'), 'identity'), 'clientId')]" }, - "id": { + "ingressApplicationGatewayIdentityObjectId": { "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" + "nullable": true, + "metadata": { + "description": "The Object ID of Application Gateway Ingress Controller (AGIC) identity." + }, + "value": "[tryGet(tryGet(tryGet(tryGet(reference('managedCluster'), 'addonProfiles'), 'ingressApplicationGateway'), 'identity'), 'objectId')]" }, - "name": { + "location": { "type": "string", - "value": "[parameters('name')]" + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('managedCluster', '2024-09-02-preview', 'full').location]" + }, + "oidcIssuerUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The OIDC token issuer URL." + }, + "value": "[tryGet(tryGet(reference('managedCluster'), 'oidcIssuerProfile'), 'issuerURL')]" + }, + "addonProfiles": { + "type": "object", + "nullable": true, + "metadata": { + "description": "The addonProfiles of the Kubernetes cluster." + }, + "value": "[tryGet(reference('managedCluster'), 'addonProfiles')]" + }, + "webAppRoutingIdentityObjectId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The Object ID of Web Application Routing." + }, + "value": "[tryGet(tryGet(tryGet(tryGet(reference('managedCluster'), 'ingressProfile'), 'webAppRouting'), 'identity'), 'objectId')]" } } } }, "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" + "logAnalyticsWorkspace", + "network", + "userAssignedIdentity" ] }, - { + "applicationInsights": { + "condition": "[parameters('enableMonitoring')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('docIntel-{0}', parameters('suffix'))]", + "name": "[take(format('avm.res.insights.component.{0}', variables('applicationInsightsResourceName')), 64)]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "suffix": { - "value": "[parameters('suffix')]" - }, - "managedIdentityPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix'))), '2022-09-01').outputs.managedIdentityPrincipalId.value]" - }, "name": { - "value": "[format('km-docIntel-{0}', parameters('suffix'))]" + "value": "[variables('applicationInsightsResourceName')]" + }, + "tags": { + "value": "[parameters('tags')]" }, "location": { - "value": "[variables('location')]" + "value": "[variables('solutionLocation')]" }, - "sku": { - "value": { - "name": "S0" - } - } + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "retentionInDays": { + "value": 365 + }, + "kind": { + "value": "web" + }, + "disableIpMasking": { + "value": false + }, + "flowType": { + "value": "Bluefield" + }, + "workspaceResourceId": "[if(parameters('enableMonitoring'), createObject('value', reference('logAnalyticsWorkspace').outputs.resourceId.value), createObject('value', ''))]", + "diagnosticSettings": "[if(parameters('enableMonitoring'), createObject('value', createArray(createObject('workspaceResourceId', reference('logAnalyticsWorkspace').outputs.resourceId.value))), createObject('value', null()))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3584343383825170510" + "version": "0.33.93.31351", + "templateHash": "5735496719243704506" }, - "description": "Creates an Azure Document Intelligence (form recognizer) instance." + "name": "Application Insights", + "description": "This component deploys an Application Insights instance." }, - "parameters": { - "suffix": { - "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id)]" + "definitions": { + "diagnosticSettingFullType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } }, - "managedIdentityPrincipalId": { - "type": "string" + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a lock.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + } + } }, + "roleAssignmentType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a role assignment.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.3.0" + } + } + } + }, + "parameters": { "name": { - "type": "string" + "type": "string", + "metadata": { + "description": "Required. Name of the Application Insights." + } }, - "location": { + "applicationType": { "type": "string", - "defaultValue": "[resourceGroup().location]" + "defaultValue": "web", + "allowedValues": [ + "web", + "other" + ], + "metadata": { + "description": "Optional. Application type." + } }, - "customSubDomainName": { + "workspaceResourceId": { "type": "string", - "defaultValue": "[parameters('name')]", "metadata": { - "description": "The custom subdomain name used to access the API. Defaults to the value of the name parameter." + "description": "Required. Resource ID of the log analytics workspace which the data will be ingested to. This property is required to create an application with this API version. Applications from older versions will not have this property." } }, - "kind": { + "disableIpMasking": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Disable IP masking. Default value is set to true." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Disable Non-AAD based Auth. Default value is set to false." + } + }, + "forceCustomerStorageForProfiler": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Force users to create their own storage account for profiler and debugger." + } + }, + "linkedStorageAccountResourceId": { "type": "string", - "defaultValue": "FormRecognizer" + "nullable": true, + "metadata": { + "description": "Optional. Linked storage account resource ID." + } }, - "publicNetworkAccess": { + "publicNetworkAccessForIngestion": { "type": "string", "defaultValue": "Enabled", "allowedValues": [ "Enabled", "Disabled" - ] + ], + "metadata": { + "description": "Optional. The network access type for accessing Application Insights ingestion. - Enabled or Disabled." + } }, - "sku": { - "type": "object", - "defaultValue": { - "name": "S0" + "publicNetworkAccessForQuery": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The network access type for accessing Application Insights query. - Enabled or Disabled." } }, - "allowedIpRules": { - "type": "array", - "defaultValue": [] + "retentionInDays": { + "type": "int", + "defaultValue": 365, + "allowedValues": [ + 30, + 60, + 90, + 120, + 180, + 270, + 365, + 550, + 730 + ], + "metadata": { + "description": "Optional. Retention period in days." + } }, - "networkAcls": { - "type": "object", - "defaultValue": "[if(empty(parameters('allowedIpRules')), createObject('defaultAction', 'Allow'), createObject('ipRules', parameters('allowedIpRules'), 'defaultAction', 'Deny'))]" - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2023-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "kind": "[parameters('kind')]", - "properties": { - "customSubDomainName": "[parameters('customSubDomainName')]", - "publicNetworkAccess": "[parameters('publicNetworkAccess')]", - "networkAcls": "[parameters('networkAcls')]", - "disableLocalAuth": true - }, - "sku": "[parameters('sku')]" + "samplingPercentage": { + "type": "int", + "defaultValue": 100, + "minValue": 0, + "maxValue": 100, + "metadata": { + "description": "Optional. Percentage of the data produced by the application being monitored that is being sampled for Application Insights telemetry." + } }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", - "name": "[guid(format('Cognitive Services User-{0}', parameters('suffix')))]", - "properties": { - "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", - "principalId": "[parameters('managedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]" - ] - } - ], - "outputs": { - "endpoint": { + "flowType": { "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').endpoint]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('managedidentity-{0}', parameters('suffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('containerAppsEnvironment-{0}', parameters('suffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[variables('location')]" - }, - "suffix": { - "value": "[parameters('suffix')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "15906627962917124587" - } - }, - "parameters": { - "suffix": { + "nullable": true, + "metadata": { + "description": "Optional. Used by the Application Insights system to determine what kind of flow this component was created by. This is to be set to 'Bluefield' when creating/updating a component via the REST API." + } + }, + "requestSource": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Describes what tool created this Application Insights component. Customers using this API should set this to the default 'rest'." + } + }, + "kind": { "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id)]" + "defaultValue": "", + "metadata": { + "description": "Optional. The kind of application that this component refers to, used to customize UI. This value is a freeform string, values should typically be one of the following: web, ios, other, store, java, phone." + } }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "The location where the resources will be created." + "description": "Optional. Location for all Resources." } }, - "tags": { - "type": "object", - "defaultValue": {}, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { - "description": "Optional. The tags to be assigned to the created resources." + "description": "Optional. The lock settings of the service." } }, - "containerAppsEnvironmentName": { - "type": "string", - "defaultValue": "[format('km-cae-{0}', parameters('suffix'))]", + "roleAssignments": { + "type": "array", + "items": { + "$ref": "#/definitions/roleAssignmentType" + }, + "nullable": true, "metadata": { - "description": "The name of the container apps environment. If set, it overrides the name generated by the template." + "description": "Optional. Array of role assignments to create." } }, - "logAnalyticsWorkspaceName": { - "type": "string", - "defaultValue": "[format('km-log-{0}', parameters('suffix'))]", + "tags": { + "type": "object", + "nullable": true, "metadata": { - "description": "The name of the log analytics workspace. If set, it overrides the name generated by the template." + "description": "Optional. Tags of the resource." } }, - "applicationInsightsName": { - "type": "string", - "defaultValue": "[format('km-appins-{0}', parameters('suffix'))]", + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "type": "array", + "items": { + "$ref": "#/definitions/diagnosticSettingFullType" + }, + "nullable": true, "metadata": { - "description": " The name of the application insights. If set, it overrides the name generated by the template." + "description": "Optional. The diagnostic settings of the service." } } }, - "resources": [ - { - "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-06-01", - "name": "[parameters('logAnalyticsWorkspaceName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "Application Insights Component Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ae349356-3a1b-4a5e-921d-050484c6347e')]", + "Application Insights Snapshot Debugger": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '08954f03-6346-4c2e-81c0-ec3a5cfae23b')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.insights-component.{0}.{1}', replace('0.6.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { - "features": { - "searchVersion": 1 - }, - "sku": { - "name": "PerGB2018" - }, - "retentionInDays": 30 + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } } }, - { + "appInsights": { "type": "Microsoft.Insights/components", "apiVersion": "2020-02-02", - "name": "[parameters('applicationInsightsName')]", + "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", - "kind": "web", + "kind": "[parameters('kind')]", + "properties": { + "Application_Type": "[parameters('applicationType')]", + "DisableIpMasking": "[parameters('disableIpMasking')]", + "DisableLocalAuth": "[parameters('disableLocalAuth')]", + "ForceCustomerStorageForProfiler": "[parameters('forceCustomerStorageForProfiler')]", + "WorkspaceResourceId": "[parameters('workspaceResourceId')]", + "publicNetworkAccessForIngestion": "[parameters('publicNetworkAccessForIngestion')]", + "publicNetworkAccessForQuery": "[parameters('publicNetworkAccessForQuery')]", + "RetentionInDays": "[parameters('retentionInDays')]", + "SamplingPercentage": "[parameters('samplingPercentage')]", + "Flow_Type": "[parameters('flowType')]", + "Request_Source": "[parameters('requestSource')]" + } + }, + "appInsights_roleAssignments": { + "copy": { + "name": "appInsights_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/components', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "Application_Type": "web", - "WorkspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ - "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" + "appInsights" ] }, - { - "type": "Microsoft.App/managedEnvironments", - "apiVersion": "2022-10-01", - "name": "[parameters('containerAppsEnvironmentName')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "sku": { - "name": "Consumption" - }, + "appInsights_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { - "daprAIInstrumentationKey": "[reference(resourceId('Microsoft.Insights/components', parameters('applicationInsightsName')), '2020-02-02').InstrumentationKey]", - "appLogsConfiguration": { - "destination": "log-analytics", - "logAnalyticsConfiguration": { - "customerId": "[reference(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2021-06-01').customerId]", - "sharedKey": "[listKeys(resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName')), '2021-06-01').primarySharedKey]" - } - } + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "[resourceId('Microsoft.Insights/components', parameters('applicationInsightsName'))]", - "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('logAnalyticsWorkspaceName'))]" + "appInsights" ] - } - ], - "outputs": { - "containerAppsEnvironmentName": { - "type": "string", - "value": "[parameters('containerAppsEnvironmentName')]" - }, - "containerAppsEnvironmentId": { - "type": "string", - "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('containerAppsEnvironmentName'))]" - }, - "logAnalyticsWorkspaceName": { - "type": "string", - "value": "[parameters('logAnalyticsWorkspaceName')]" }, - "applicationInsightsName": { - "type": "string", - "metadata": { - "description": "The name of the application insights." + "appInsights_diagnosticSettings": { + "copy": { + "name": "appInsights_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" }, - "value": "[parameters('applicationInsightsName')]" - }, - "applicationInsightsInstrumentationKey": { - "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]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('containerAppService-{0}', parameters('suffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[variables('location')]" - }, - "suffix": { - "value": "[parameters('suffix')]" - }, - "containerAppsEnvironmentId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.containerAppsEnvironmentId.value]" - }, - "appInsightsInstrumentationKey": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.applicationInsightsInstrumentationKey.value]" - }, - "applicationInsightsConnectionString": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppsEnvironment-{0}', parameters('suffix'))), '2022-09-01').outputs.applicationInsightsConnectionString.value]" - }, - "managedIdentityId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('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]" - }, - "KernelMemory__ServiceAuthorization__AccessKey1": { - "value": "[parameters('WebServiceAuthorizationKey1')]" - }, - "KernelMemory__ServiceAuthorization__AccessKey2": { - "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)]" - }, - "AzureBlobs_Account": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('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]" - }, - "AzureQueues_QueueName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('storage-{0}', parameters('suffix'))), '2022-09-01').outputs.queueName.value]" - }, - "AzureOpenAIEmbedding_Deployment": { - "value": "[variables('embedding').deploymentName]" - }, - "AzureOpenAIEmbedding_Endpoint": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('openai-{0}', parameters('suffix'))), '2022-09-01').outputs.endpoint.value]" - }, - "AzureOpenAIText_Deployment": { - "value": "[variables('chatGpt').deploymentName]" - }, - "AzureOpenAIText_Endpoint": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('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]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "1087801445927599681" - } - }, - "parameters": { - "suffix": { - "type": "string", - "defaultValue": "[uniqueString(resourceGroup().id)]" - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "managedIdentityId": { - "type": "string" - }, - "managedIdentityClientId": { - "type": "string" - }, - "kmServiceName": { - "type": "string", - "defaultValue": "[format('km-service-{0}', parameters('suffix'))]" - }, - "containerAppsEnvironmentId": { - "type": "string" - }, - "appInsightsInstrumentationKey": { - "type": "string" - }, - "applicationInsightsConnectionString": { - "type": "string" - }, - "AzureBlobs_Account": { - "type": "string" - }, - "AzureQueues_Account": { - "type": "string" - }, - "AzureQueues_QueueName": { - "type": "string" - }, - "AzureAISearch_Endpoint": { - "type": "string" - }, - "AzureOpenAIText_Endpoint": { - "type": "string" - }, - "AzureOpenAIText_Deployment": { - "type": "string" - }, - "AzureOpenAIEmbedding_Endpoint": { - "type": "string" - }, - "AzureOpenAIEmbedding_Deployment": { - "type": "string" - }, - "AzureAIDocIntel_Endpoint": { - "type": "string" - }, - "KernelMemory__ServiceAuthorization__AccessKey1": { - "type": "string" - }, - "KernelMemory__ServiceAuthorization__AccessKey2": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.App/containerApps", - "apiVersion": "2023-05-01", - "name": "[parameters('kmServiceName')]", - "location": "[parameters('location')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { - "environmentId": "[parameters('containerAppsEnvironmentId')]", - "configuration": { - "secrets": [ - { - "name": "appinsights-key", - "value": "[parameters('appInsightsInstrumentationKey')]" + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null } - ], - "registries": [], - "activeRevisionsMode": "Single", - "ingress": { - "external": true, - "transport": "Auto", - "allowInsecure": false, - "targetPort": 9001, - "stickySessions": { - "affinity": "none" + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" } } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "appInsights" + ] + }, + "linkedStorageAccount": { + "condition": "[not(empty(parameters('linkedStorageAccountResourceId')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-appInsights-linkedStorageAccount', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appInsightsName": { + "value": "[parameters('name')]" + }, + "storageAccountResourceId": { + "value": "[coalesce(parameters('linkedStorageAccountResourceId'), '')]" + } }, "template": { - "containers": [ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "10861379689695100897" + }, + "name": "Application Insights Linked Storage Account", + "description": "This component deploys an Application Insights Linked Storage Account." + }, + "parameters": { + "appInsightsName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Application Insights instance. Required if the template is used in a standalone deployment." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. Linked storage account resource ID." + } + } + }, + "resources": [ { - "name": "kernelmemory-service", - "image": "docker.io/kernelmemory/service:latest", - "command": [], - "resources": { - "cpu": "[json('0.25')]", - "memory": "0.5Gi" - }, - "env": [ - { - "name": "ASPNETCORE_ENVIRONMENT", - "value": "Production" - }, - { - "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[parameters('applicationInsightsConnectionString')]" - }, - { - "name": "AZURE_CLIENT_ID", - "value": "[parameters('managedIdentityClientId')]" - }, - { - "name": "KernelMemory__Service__OpenApiEnabled", - "value": "true" - }, - { - "name": "KernelMemory__DocumentStorageType", - "value": "AzureBlobs" - }, - { - "name": "KernelMemory__TextGeneratorType", - "value": "AzureOpenAIText" - }, - { - "name": "KernelMemory__DefaultIndexName", - "value": "default" - }, - { - "name": "KernelMemory__ServiceAuthorization__Enabled", - "value": "true" - }, - { - "name": "KernelMemory__ServiceAuthorization__AuthenticationType", - "value": "APIKey" - }, - { - "name": "KernelMemory__ServiceAuthorization__HttpHeaderName", - "value": "Authorization" - }, - { - "name": "KernelMemory__ServiceAuthorization__AccessKey1", - "value": "[parameters('KernelMemory__ServiceAuthorization__AccessKey1')]" - }, - { - "name": "KernelMemory__ServiceAuthorization__AccessKey2", - "value": "[parameters('KernelMemory__ServiceAuthorization__AccessKey2')]" - }, - { - "name": "KernelMemory__DataIngestion__DistributedOrchestration__QueueType", - "value": "AzureQueues" - }, - { - "name": "KernelMemory__DataIngestion__EmbeddingGeneratorTypes__0", - "value": "AzureOpenAIEmbedding" - }, - { - "name": "KernelMemory__DataIngestion__MemoryDbTypes__0", - "value": "AzureAISearch" - }, - { - "name": "KernelMemory__DataIngestion__ImageOcrType", - "value": "AzureAIDocIntel" - }, - { - "name": "KernelMemory__Retrieval__EmbeddingGeneratorType", - "value": "AzureOpenAIEmbedding" - }, - { - "name": "KernelMemory__Retrieval__MemoryDbType", - "value": "AzureAISearch" - }, - { - "name": "KernelMemory__Services__AzureBlobs__Account", - "value": "[parameters('AzureBlobs_Account')]" - }, - { - "name": "KernelMemory__Services__AzureQueues__Account", - "value": "[parameters('AzureQueues_Account')]" - }, - { - "name": "KernelMemory__Services__AzureQueues__QueueName", - "value": "[parameters('AzureQueues_QueueName')]" - }, - { - "name": "KernelMemory__Services__AzureAISearch__Endpoint", - "value": "[parameters('AzureAISearch_Endpoint')]" - }, - { - "name": "KernelMemory__Services__AzureOpenAIText__Endpoint", - "value": "[parameters('AzureOpenAIText_Endpoint')]" - }, - { - "name": "KernelMemory__Services__AzureOpenAIText__Deployment", - "value": "[parameters('AzureOpenAIText_Deployment')]" - }, - { - "name": "KernelMemory__Services__AzureOpenAIEmbedding__Endpoint", - "value": "[parameters('AzureOpenAIEmbedding_Endpoint')]" - }, - { - "name": "KernelMemory__Services__AzureOpenAIEmbedding__Deployment", - "value": "[parameters('AzureOpenAIEmbedding_Deployment')]" - }, - { - "name": "KernelMemory__Services__AzureAIDocIntel__Endpoint", - "value": "[parameters('AzureAIDocIntel_Endpoint')]" - } - ] + "type": "microsoft.insights/components/linkedStorageAccounts", + "apiVersion": "2020-03-01-preview", + "name": "[format('{0}/{1}', parameters('appInsightsName'), 'ServiceProfiler')]", + "properties": { + "linkedStorageAccount": "[parameters('storageAccountResourceId')]" + } } ], - "scale": { - "minReplicas": 1, - "maxReplicas": 1 + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Linked Storage Account." + }, + "value": "ServiceProfiler" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Linked Storage Account." + }, + "value": "[resourceId('microsoft.insights/components/linkedStorageAccounts', parameters('appInsightsName'), 'ServiceProfiler')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the agent pool was deployed into." + }, + "value": "[resourceGroup().name]" + } } } }, - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', parameters('managedIdentityId'))]": {} - } - } + "dependsOn": [ + "appInsights" + ] } - ], + }, "outputs": { - "kmServiceName": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the application insights component." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the application insights component." + }, + "value": "[resourceId('Microsoft.Insights/components', parameters('name'))]" + }, + "resourceGroupName": { "type": "string", - "value": "[parameters('kmServiceName')]" + "metadata": { + "description": "The resource group the application insights component was deployed into." + }, + "value": "[resourceGroup().name]" }, - "kmServiceId": { + "applicationId": { "type": "string", - "value": "[resourceId('Microsoft.App/containerApps', parameters('kmServiceName'))]" + "metadata": { + "description": "The application ID of the application insights component." + }, + "value": "[reference('appInsights').AppId]" }, - "kmServiceAccessKey1": { + "location": { "type": "string", - "value": "[parameters('KernelMemory__ServiceAuthorization__AccessKey1')]" + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('appInsights', '2020-02-02', 'full').location]" }, - "kmServiceAccessKey2": { + "instrumentationKey": { "type": "string", - "value": "[parameters('KernelMemory__ServiceAuthorization__AccessKey2')]" + "metadata": { + "description": "Application Insights Instrumentation key. A read-only value that applications can use to identify the destination for all telemetry sent to Azure Application Insights. This value will be supplied upon construction of each new Application Insights component." + }, + "value": "[reference('appInsights').InstrumentationKey]" }, - "kmServiceFQDN": { + "connectionString": { "type": "string", "metadata": { - "description": "The FQDN of the frontend web app service." + "description": "Application Insights Connection String." }, - "value": "[reference(resourceId('Microsoft.App/containerApps', parameters('kmServiceName')), '2023-05-01').configuration.ingress.fqdn]" + "value": "[reference('appInsights').ConnectionString]" } } } }, "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')))]" + "logAnalyticsWorkspace" ] } - ], + }, "outputs": { - "kmServiceEndpoint": { + "AZURE_TENANT_ID": { + "type": "string", + "metadata": { + "description": "Contains Azure Tenant ID." + }, + "value": "[subscription().tenantId]" + }, + "SOLUTION_NAME": { + "type": "string", + "metadata": { + "description": "Contains Solution Name." + }, + "value": "[variables('solutionSuffix')]" + }, + "RESOURCE_GROUP_NAME": { + "type": "string", + "metadata": { + "description": "Contains Resource Group Name." + }, + "value": "[resourceGroup().name]" + }, + "RESOURCE_GROUP_LOCATION": { + "type": "string", + "metadata": { + "description": "Contains Resource Group Location." + }, + "value": "[variables('solutionLocation')]" + }, + "AZURE_RESOURCE_GROUP_ID": { + "type": "string", + "metadata": { + "description": "Contains Resource Group ID." + }, + "value": "[resourceGroup().id]" + }, + "AZURE_APP_CONFIG_NAME": { + "type": "string", + "metadata": { + "description": "Contains Azure App Configuration Name." + }, + "value": "[reference('avmAppConfig').outputs.name.value]" + }, + "AZURE_APP_CONFIG_ENDPOINT": { + "type": "string", + "metadata": { + "description": "Contains Azure App Configuration Endpoint." + }, + "value": "[reference('avmAppConfig').outputs.endpoint.value]" + }, + "STORAGE_ACCOUNT_NAME": { + "type": "string", + "metadata": { + "description": "Contains Storage Account Name." + }, + "value": "[reference('avmStorageAccount').outputs.name.value]" + }, + "AZURE_COSMOSDB_NAME": { + "type": "string", + "metadata": { + "description": "Contains Cosmos DB Name." + }, + "value": "[reference('avmCosmosDB').outputs.name.value]" + }, + "AZURE_COGNITIVE_SERVICE_NAME": { + "type": "string", + "metadata": { + "description": "Contains Cognitive Service Name." + }, + "value": "[reference('documentIntelligence').outputs.name.value]" + }, + "AZURE_COGNITIVE_SERVICE_ENDPOINT": { + "type": "string", + "metadata": { + "description": "Contains Azure Cognitive Service Endpoint." + }, + "value": "[reference('documentIntelligence').outputs.endpoint.value]" + }, + "AZURE_SEARCH_SERVICE_NAME": { + "type": "string", + "metadata": { + "description": "Contains Azure Search Service Name." + }, + "value": "[reference('avmSearchSearchServices').outputs.name.value]" + }, + "AZURE_AKS_NAME": { + "type": "string", + "metadata": { + "description": "Contains Azure AKS Name." + }, + "value": "[reference('managedCluster').outputs.name.value]" + }, + "AZURE_AKS_MI_ID": { + "type": "string", + "metadata": { + "description": "Contains Azure AKS Managed Identity ID." + }, + "value": "[reference('managedCluster').outputs.systemAssignedMIPrincipalId.value]" + }, + "AZURE_CONTAINER_REGISTRY_NAME": { + "type": "string", + "metadata": { + "description": "Contains Azure Container Registry Name." + }, + "value": "[reference('avmContainerRegistry').outputs.name.value]" + }, + "AZURE_OPENAI_SERVICE_NAME": { + "type": "string", + "metadata": { + "description": "Contains Azure OpenAI Service Name." + }, + "value": "[reference('avmOpenAi').outputs.name.value]" + }, + "AZURE_OPENAI_SERVICE_ENDPOINT": { + "type": "string", + "metadata": { + "description": "Contains Azure OpenAI Service Endpoint." + }, + "value": "[reference('avmOpenAi').outputs.endpoint.value]" + }, + "AZ_SEARCH_SERVICE_ENDPOINT": { + "type": "string", + "metadata": { + "description": "Contains Azure Search Service Endpoint." + }, + "value": "[reference('avmSearchSearchServices').outputs.name.value]" + }, + "AZ_GPT4O_MODEL_ID": { + "type": "string", + "metadata": { + "description": "Contains Azure GPT-4o Model Deployment Name." + }, + "value": "[variables('gptModelDeployment').deploymentName]" + }, + "AZ_GPT4O_MODEL_NAME": { "type": "string", "metadata": { - "description": "The FQDN of the frontend web app service." + "description": "Contains Azure GPT-4o Model Name." }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceFQDN.value]" + "value": "[variables('gptModelDeployment').modelName]" }, - "kmServiceAccessKey1": { + "AZ_GPT_EMBEDDING_MODEL_NAME": { "type": "string", "metadata": { - "description": "Service Access Key 1." + "description": "Contains Azure OpenAI Embedding Model Name." }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceAccessKey1.value]" + "value": "[variables('embeddingModelDeployment').modelName]" }, - "kmServiceAccessKey2": { + "AZ_GPT_EMBEDDING_MODEL_ID": { "type": "string", "metadata": { - "description": "Service Access Key 2." + "description": "Contains Azure OpenAI Embedding Model Deployment Name." }, - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('containerAppService-{0}', parameters('suffix'))), '2022-09-01').outputs.kmServiceAccessKey2.value]" + "value": "[variables('embeddingModelDeployment').deploymentName]" } } } \ No newline at end of file diff --git a/infra/main.parameters.json b/infra/main.parameters.json index c41d73d7..a649cdda 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -7,6 +7,38 @@ }, "location": { "value": "${AZURE_LOCATION}" + }, + "gptModelDeploymentType": { + "value": "${AZURE_ENV_MODEL_DEPLOYMENT_TYPE}" + }, + "gptModelName": { + "value": "${AZURE_ENV_MODEL_NAME}" + }, + "gptModelCapacity": { + "value": "${AZURE_ENV_MODEL_CAPACITY}" + }, + "gptModelVersion": { + "value": "${AZURE_ENV_MODEL_VERSION}" + }, + "embeddingModelName": { + "value": "${AZURE_ENV_EMBEDDING_MODEL_NAME}" + }, + "embeddingModelVersion": { + "value": "${AZURE_ENV_EMBEDDING_MODEL_VERSION}" + }, + "embeddingModelCapacity": { + "value": "${AZURE_ENV_EMBEDDING_MODEL_CAPACITY}" + }, + "existingLogAnalyticsWorkspaceId": { + "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}" + }, + "enableTelemetry": { + "value": "${AZURE_ENV_ENABLE_TELEMETRY}" + }, + "tags": { + "value": { + "solutionName": "${AZURE_ENV_NAME}" + } } } } \ No newline at end of file diff --git a/infra/main.waf.parameters.json b/infra/main.waf.parameters.json new file mode 100644 index 00000000..6700f98f --- /dev/null +++ b/infra/main.waf.parameters.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "solutionName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + }, + "gptModelDeploymentType": { + "value": "${AZURE_ENV_MODEL_DEPLOYMENT_TYPE}" + }, + "gptModelName": { + "value": "${AZURE_ENV_MODEL_NAME}" + }, + "gptModelCapacity": { + "value": "${AZURE_ENV_MODEL_CAPACITY}" + }, + "gptModelVersion": { + "value": "${AZURE_ENV_MODEL_VERSION}" + }, + "embeddingModelName": { + "value": "${AZURE_ENV_EMBEDDING_MODEL_NAME}" + }, + "embeddingModelVersion": { + "value": "${AZURE_ENV_EMBEDDING_MODEL_VERSION}" + }, + "embeddingModelCapacity": { + "value": "${AZURE_ENV_EMBEDDING_MODEL_CAPACITY}" + }, + "existingLogAnalyticsWorkspaceId": { + "value": "${AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID}" + }, + "tags": { + "value": { + "solutionName": "${AZURE_ENV_NAME}" + } + }, + "vmAdminUsername": { + "value": "${AZURE_ENV_VM_ADMIN_USERNAME}" + }, + "vmAdminPassword": { + "value": "${AZURE_ENV_VM_ADMIN_PASSWORD}" + }, + "enableTelemetry": { + "value": "${AZURE_ENV_ENABLE_TELEMETRY}" + }, + "enableMonitoring": { + "value": true + }, + "enablePrivateNetworking": { + "value": true + }, + "enableScalability": { + "value": true + } + } +} \ No newline at end of file diff --git a/infra/modules/network.bicep b/infra/modules/network.bicep index a4a3b7ba..c4d5b0aa 100644 --- a/infra/modules/network.bicep +++ b/infra/modules/network.bicep @@ -25,7 +25,6 @@ param vmAdminPassword string @description('Required. VM size for the Jumpbox VM.') param vmSize string - // VM Size Notes: // 1 B-series VMs (like Standard_B2ms) do not support accelerated networking. // 2 Pick a VM size that does support accelerated networking (the usual jump-box candidates): diff --git a/infra/modules/network/bastionHost.bicep b/infra/modules/network/bastionHost.bicep index cc1987e5..f7ea7c92 100644 --- a/infra/modules/network/bastionHost.bicep +++ b/infra/modules/network/bastionHost.bicep @@ -49,7 +49,7 @@ module bastionSubnet 'br/public:avm/res/network/virtual-network/subnet:0.1.2' = virtualNetworkName: vnetName name: 'AzureBastionSubnet' // this name required as is for Azure Bastion Host subnet addressPrefixes: subnet.?addressPrefixes - networkSecurityGroupResourceId: nsg.outputs.resourceId + networkSecurityGroupResourceId: nsg!.outputs.resourceId enableTelemetry: enableTelemetry } } @@ -90,8 +90,8 @@ module bastionHost 'br/public:avm/res/network/bastion-host:0.6.1' = { output resourceId string = bastionHost.outputs.resourceId output name string = bastionHost.outputs.name -output subnetId string = bastionSubnet.outputs.resourceId -output subnetName string = bastionSubnet.outputs.name +output subnetId string = bastionSubnet!.outputs.resourceId +output subnetName string = bastionSubnet!.outputs.name @export() @description('Custom type definition for establishing Bastion Host for remote connection.') diff --git a/infra/modules/network/jumpbox.bicep b/infra/modules/network/jumpbox.bicep index 9cdd1d7e..409880f9 100644 --- a/infra/modules/network/jumpbox.bicep +++ b/infra/modules/network/jumpbox.bicep @@ -129,10 +129,10 @@ output resourceId string = vm.outputs.resourceId output name string = vm.outputs.name output location string = vm.outputs.location -output subnetId string = subnetResource.outputs.resourceId -output subnetName string = subnetResource.outputs.name -output nsgId string = nsg.outputs.resourceId -output nsgName string = nsg.outputs.name +output subnetId string = subnetResource!.outputs.resourceId +output subnetName string = subnetResource!.outputs.name +output nsgId string = nsg!.outputs.resourceId +output nsgName string = nsg!.outputs.name @export() @description('Custom type definition for establishing Jumpbox Virtual Machine and its associated resources.') diff --git a/infra/modules/network/main.bicep b/infra/modules/network/main.bicep index 3d54481e..92f761e7 100644 --- a/infra/modules/network/main.bicep +++ b/infra/modules/network/main.bicep @@ -92,12 +92,12 @@ output vnetResourceId string = virtualNetwork.outputs.resourceId import { subnetOutputType } from 'virtualNetwork.bicep' output subnets subnetOutputType[] = virtualNetwork.outputs.subnets // This one holds critical info for subnets, including NSGs -output bastionSubnetId string = bastionHost.outputs.subnetId -output bastionSubnetName string = bastionHost.outputs.subnetName -output bastionHostId string = bastionHost.outputs.resourceId -output bastionHostName string = bastionHost.outputs.name - -output jumpboxSubnetName string = jumpbox.outputs.subnetName -output jumpboxSubnetId string = jumpbox.outputs.subnetId -output jumpboxName string = jumpbox.outputs.name -output jumpboxResourceId string = jumpbox.outputs.resourceId +output bastionSubnetId string = bastionHost!.outputs.subnetId +output bastionSubnetName string = bastionHost!.outputs.subnetName +output bastionHostId string = bastionHost!.outputs.resourceId +output bastionHostName string = bastionHost!.outputs.name + +output jumpboxSubnetName string = jumpbox!.outputs.subnetName +output jumpboxSubnetId string = jumpbox!.outputs.subnetId +output jumpboxName string = jumpbox!.outputs.name +output jumpboxResourceId string = jumpbox!.outputs.resourceId diff --git a/infra/modules/network/virtualNetwork.bicep b/infra/modules/network/virtualNetwork.bicep index 2400dfe1..151f406c 100644 --- a/infra/modules/network/virtualNetwork.bicep +++ b/infra/modules/network/virtualNetwork.bicep @@ -92,7 +92,7 @@ output subnets subnetOutputType[] = [ name: subnet.name resourceId: virtualNetwork.outputs.subnetResourceIds[i] nsgName: !empty(subnet.?networkSecurityGroup) ? subnet.?networkSecurityGroup.name : null - nsgResourceId: !empty(subnet.?networkSecurityGroup) ? nsgs[i].outputs.resourceId : null + nsgResourceId: !empty(subnet.?networkSecurityGroup) ? nsgs[i]!.outputs.resourceId : null } ] From e3c42bced12fde5e6c37357fba31f66d71984d04 Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Thu, 11 Sep 2025 17:33:26 +0530 Subject: [PATCH 20/29] feat: added customizingAzdParameters.md and changes for DeploymentGuide --- docs/CustomizingAzdParameters.md | 43 +++++++++++++++++ docs/DeploymentGuide.md | 81 +++++++++++++++++++++++++++----- 2 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 docs/CustomizingAzdParameters.md diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md new file mode 100644 index 00000000..c51c2d64 --- /dev/null +++ b/docs/CustomizingAzdParameters.md @@ -0,0 +1,43 @@ +## [Optional]: Customizing resource names + +By default this template will use the environment name as the prefix to prevent naming collisions within Azure. The parameters below show the default values. You only need to run the statements below if you need to change the values. + +> To override any of the parameters, run `azd env set ` before running `azd up`. On the first azd command, it will prompt you for the environment name. Be sure to choose 3-20 characters alphanumeric unique name. + +## Parameters + +| Name | Type | Default Value | Purpose | +| ------------------------------- | ------ | ----------------- | --------------------------------------------------------------------------------------------------- | +| `AZURE_ENV_NAME` | string | `dkm` | Used as a prefix for all resource names to ensure uniqueness across environments. | +| `AZURE_LOCATION` | string | `` | Location of the Azure resources. Controls where the infrastructure will be deployed. | +| `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Defines the deployment type for the AI model (e.g., Standard, GlobalStandard). | +| `AZURE_ENV_MODEL_NAME` | string | `gpt-4.1` | Specifies the name of the GPT model to be deployed. | +| `AZURE_ENV_MODEL_CAPACITY` | int | `150` | Sets the GPT model capacity. | +| `AZURE_ENV_MODEL_VERSION` | string | `2024-08-06` | Version of the GPT model to be used for deployment. | +| `AZURE_ENV_EMBEDDING_MODEL_NAME` | string | text-embedding-3-large | Sets the name of the embedding model to use. | +| `AZURE_ENV_EMBEDDING_MODEL_VERSION` | string | text-embedding-3-large | *(empty)* | +| `AZURE_ENV_EMBEDDING_MODEL_CAPACITY` | string | Configure capacity for embedding models (in thousands). | Set the capacity for embedding models (in thousands). | +| `AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID` | string | Guide to get your [Existing Workspace ID](/docs/re-use-log-analytics.md) | Set this if you want to reuse an existing Log Analytics Workspace instead of creating a new one. | +| `AZURE_ENV_VM_ADMIN_USERNAME` | string | `take(newGuid(), 20)` | The administrator username for the virtual machine. | +| `AZURE_ENV_VM_ADMIN_PASSWORD` | string | `newGuid()` | The administrator password for the virtual machine. | +| `AZURE_ENV_ENABLE_TELEMETRY` | bool | `true` | Enables telemetry for monitoring and diagnostics. | +--- + +## How to Set a Parameter + +To customize any of the above values, run the following command **before** `azd up`: + +```bash +azd env set +``` + +Set the Log Analytics Workspace Id if you need to reuse the existing workspace which is already existing +```shell +azd env set AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID '/subscriptions//resourceGroups//providers/Microsoft.OperationalInsights/workspaces/' +``` + +**Example:** + +```bash +azd env set AZURE_LOCATION westus2 +``` diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index bc729a96..1a48694a 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -42,7 +42,70 @@
ResourceProvider -## Deployment +## Deployment Options & Steps + +### Sandbox or WAF Aligned Deployment Options + +The [`infra`](../infra) folder of the Multi Agent Solution Accelerator contains the [`main.bicep`](../infra/main.bicep) Bicep script, which defines all Azure infrastructure components for this solution. + +By default, the `azd up` command uses the [`main.parameters.json`](../infra/main.parameters.json) file to deploy the solution. This file is pre-configured for a **sandbox environment** — ideal for development and proof-of-concept scenarios, with minimal security and cost controls for rapid iteration. + +For **production deployments**, the repository also provides [`main.waf.parameters.json`](../infra/main.waf.parameters.json), which applies a [Well-Architected Framework (WAF) aligned](https://learn.microsoft.com/en-us/azure/well-architected/) configuration. This option enables additional Azure best practices for reliability, security, cost optimization, operational excellence, and performance efficiency, such as: + + - Enhanced network security (e.g., Network protection with private endpoints) + - Stricter access controls and managed identities + - Logging, monitoring, and diagnostics enabled by default + - Resource tagging and cost management recommendations + +**How to choose your deployment configuration:** + +* Use the default `main.parameters.json` file for a **sandbox/dev environment** +* For a **WAF-aligned, production-ready deployment**, copy the contents of `main.waf.parameters.json` into `main.parameters.json` before running `azd up` + +--- + +### VM Credentials Configuration + +By default, the solution sets the VM administrator username and password from environment variables. +If you do not configure these values, a randomly generated GUID will be used for both the username and password. + +To set your own VM credentials before deployment, use: + +```sh +azd env set AZURE_ENV_VM_ADMIN_USERNAME +azd env set AZURE_ENV_VM_ADMIN_PASSWORD +``` + +> [!TIP] +> Always review and adjust parameter values (such as region, capacity, security settings and log analytics workspace configuration) to match your organization’s requirements before deploying. For production, ensure you have sufficient quota and follow the principle of least privilege for all identities and role assignments. + + +> [!IMPORTANT] +> The WAF-aligned configuration is under active development. More Azure Well-Architected recommendations will be added in future updates. + +## Deployment Steps + +Consider the following settings during your deployment to modify specific settings: + +
+ Configurable Deployment Settings + +When you start the deployment, most parameters will have **default values**, but you can update the following settings [here](../docs/CustomizingAzdParameters.md): + +| **Setting** | **Description** | **Default value** | +| ------------------------------ | ------------------------------------------------------------------------------------ | ----------------- | +| **Environment Name** | Used as a prefix for all resource names to ensure uniqueness across environments. | dkm | +| **Azure Region** | Location of the Azure resources. Controls where the infrastructure will be deployed. | australiaeast | +| **Model Deployment Type** | Defines the deployment type for the AI model (e.g., Standard, GlobalStandard). | GlobalStandard | +| **GPT Model Name** | Specifies the name of the GPT model to be deployed. | gpt-4.1 | +| **GPT Model Version** | Version of the GPT model to be used for deployment. | 2024-08-06 | +| **GPT Model Capacity** | Sets the GPT model capacity. | 100K | +| **Embedding Model** | Default: **text-embedding-ada-002**. | text-embedding-3-large | +| **Embedding Model Capacity** | Set the capacity for **embedding models** (in thousands). | 200k | +| **Enable Telemetry** | Enables telemetry for monitoring and diagnostics. | true | +| **Existing Log Analytics Workspace** | To reuse an existing Log Analytics Workspace ID instead of creating a new one. | *(none)* | + +
### Deploying with AZD @@ -83,9 +146,7 @@ Once you've opened the project in [Codespaces](#github-codespaces), [Dev Contain -- This deployment will take *7-10 minutes* to provision the resources in your account and set up the solution with sample data. - If you encounter an error or timeout during deployment, changing the location may help, as there could be availability constraints for the resources. -6. Once the deployment has completed successfully, open the [Azure Portal](https://portal.azure.com/), go to the deployed resource group, find the App Service, and get the app URL from `Default domain`. - -7. If you are done trying out the application, you can delete the resources by running `azd down`. +6. If you are done trying out the application, you can delete the resources by running `azd down`. ### Post Deployment Script: @@ -93,13 +154,11 @@ The post deployment process is very straightforward and simplified via a single ### Automated Deployment Steps: 1. Deploy Azure resources. -2. Get secret information from Azure resources. -3. Update application configuration files with secrets. -4. Set Application Configuration in Azure App Configuration. -4. Compile application, build image, and push to Azure Container Registry. -5. Configure Kubernetes cluster infrastructure. -6. Update Kubernetes configuration files. -7. Deploy certificates, ingress controller and then application images from Azure Container Registry. +2. Configure Kubernetes Infrastructure. +3. Update Kubernetes configuration files with the FQDN, Container Image Path and Email address for the certificate management. +4. Configure AKS (deploy Cert Manager, Ingress Controller) and Deploy Images on the kubernetes cluster. +5. Docker build and push container images to Azure Container Registry. +6. Display the deployment result and following instructions. Open PowerShell, change directory where you code cloned, then run the deploy script: From dca79eef11a8e0c1c8ec16407d897c5eb174bddb Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Thu, 11 Sep 2025 17:34:32 +0530 Subject: [PATCH 21/29] changed model value --- docs/CustomizingAzdParameters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md index c51c2d64..12ba128c 100644 --- a/docs/CustomizingAzdParameters.md +++ b/docs/CustomizingAzdParameters.md @@ -12,7 +12,7 @@ By default this template will use the environment name as the prefix to prevent | `AZURE_LOCATION` | string | `` | Location of the Azure resources. Controls where the infrastructure will be deployed. | | `AZURE_ENV_MODEL_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Defines the deployment type for the AI model (e.g., Standard, GlobalStandard). | | `AZURE_ENV_MODEL_NAME` | string | `gpt-4.1` | Specifies the name of the GPT model to be deployed. | -| `AZURE_ENV_MODEL_CAPACITY` | int | `150` | Sets the GPT model capacity. | +| `AZURE_ENV_MODEL_CAPACITY` | int | `100` | Sets the GPT model capacity. | | `AZURE_ENV_MODEL_VERSION` | string | `2024-08-06` | Version of the GPT model to be used for deployment. | | `AZURE_ENV_EMBEDDING_MODEL_NAME` | string | text-embedding-3-large | Sets the name of the embedding model to use. | | `AZURE_ENV_EMBEDDING_MODEL_VERSION` | string | text-embedding-3-large | *(empty)* | From 58289cd07962b059ddea61320f3d7b91567f1ccd Mon Sep 17 00:00:00 2001 From: Kingshuk-Microsoft Date: Thu, 11 Sep 2025 17:35:58 +0530 Subject: [PATCH 22/29] changed default value of embedding model --- docs/CustomizingAzdParameters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md index 12ba128c..15625e6b 100644 --- a/docs/CustomizingAzdParameters.md +++ b/docs/CustomizingAzdParameters.md @@ -15,7 +15,7 @@ By default this template will use the environment name as the prefix to prevent | `AZURE_ENV_MODEL_CAPACITY` | int | `100` | Sets the GPT model capacity. | | `AZURE_ENV_MODEL_VERSION` | string | `2024-08-06` | Version of the GPT model to be used for deployment. | | `AZURE_ENV_EMBEDDING_MODEL_NAME` | string | text-embedding-3-large | Sets the name of the embedding model to use. | -| `AZURE_ENV_EMBEDDING_MODEL_VERSION` | string | text-embedding-3-large | *(empty)* | +| `AZURE_ENV_EMBEDDING_MODEL_VERSION` | string | `200` | *(empty)* | | `AZURE_ENV_EMBEDDING_MODEL_CAPACITY` | string | Configure capacity for embedding models (in thousands). | Set the capacity for embedding models (in thousands). | | `AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID` | string | Guide to get your [Existing Workspace ID](/docs/re-use-log-analytics.md) | Set this if you want to reuse an existing Log Analytics Workspace instead of creating a new one. | | `AZURE_ENV_VM_ADMIN_USERNAME` | string | `take(newGuid(), 20)` | The administrator username for the virtual machine. | From 57ded83d3def569caf54e6ec27ad977265fe2e61 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Thu, 11 Sep 2025 19:02:29 +0530 Subject: [PATCH 23/29] fix: WAF + EXP issue with bicep --- infra/main.bicep | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index e8d197ee..f44fd334 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -895,8 +895,8 @@ module applicationInsights 'br/public:avm/res/insights/component:0.6.0' = if (en disableIpMasking: false flowType: 'Bluefield' // WAF aligned configuration for Monitoring - workspaceResourceId: enableMonitoring ? logAnalyticsWorkspace!.outputs.resourceId : '' - diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspace!.outputs.resourceId }] : null + workspaceResourceId: enableMonitoring ? logAnalyticsWorkspaceResourceId : '' + diagnosticSettings: enableMonitoring ? [{ workspaceResourceId: logAnalyticsWorkspaceResourceId }] : null } } From afe3ea2af75c400b785e00bd02ba038fb4f65c43 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Fri, 12 Sep 2025 10:11:08 +0530 Subject: [PATCH 24/29] docs: Updated the Deployment Guide --- docs/DeploymentGuide.md | 18 ++++++++---------- .../images/deployment/Deployment_Login_02.png | Bin 0 -> 123971 bytes infra/main.bicep | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) create mode 100644 docs/images/deployment/Deployment_Login_02.png diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 1a48694a..eb7246f8 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -6,8 +6,8 @@ ## Contents * [Prerequisites](#prerequisites) -* [Regional Availability](#regional-availability) -* [Deployment](#deployment) +* [Deployment Options](#deployment-options--steps) +* [Deployment](#deployment-steps) * [Next Steps](#next-steps) ## Prerequisites @@ -153,12 +153,11 @@ Once you've opened the project in [Codespaces](#github-codespaces), [Dev Contain The post deployment process is very straightforward and simplified via a single [deployment script](../Deployment/resourcedeployment.ps1) that completes in approximately 20-30 minutes: ### Automated Deployment Steps: -1. Deploy Azure resources. -2. Configure Kubernetes Infrastructure. -3. Update Kubernetes configuration files with the FQDN, Container Image Path and Email address for the certificate management. -4. Configure AKS (deploy Cert Manager, Ingress Controller) and Deploy Images on the kubernetes cluster. -5. Docker build and push container images to Azure Container Registry. -6. Display the deployment result and following instructions. +1. Configure Kubernetes Infrastructure. +2. Update Kubernetes configuration files with the FQDN, Container Image Path and Email address for the certificate management. +3. Configure AKS (deploy Cert Manager, Ingress Controller) and Deploy Images on the kubernetes cluster. +4. Docker build and push container images to Azure Container Registry. +5. Display the deployment result and following instructions. Open PowerShell, change directory where you code cloned, then run the deploy script: @@ -180,11 +179,10 @@ You will be prompted for the following parameters with this Screen : 1. **Email** - used for issuing certificates in Kubernetes clusters from the [Let's Encrypt](https://letsencrypt.org/) service. Email address should be valid. + 2. You will be prompted to Login, Select a account and proceed to Login. 3. **GO !** - Post Deployment Script executes Azure Infrastructure configuration, Application code compile and publish into Kubernetes Cluster. - - ### Manual Deployment Steps: **Create Content Filter** - Please follow below steps > * Navigate to project in Azure OpenAI, then go to Azure AI Foundry, select Safety + security diff --git a/docs/images/deployment/Deployment_Login_02.png b/docs/images/deployment/Deployment_Login_02.png new file mode 100644 index 0000000000000000000000000000000000000000..d07d3f0a1e6fc5a4f8585476cfe2e16255d845f7 GIT binary patch literal 123971 zcmZ6ybyQT{`#uafA|MTt(!&7KAzgw>H`3iG-Cfe%-6$-zvrA1JX2#{c4U{J(FKgz?vz$L-Jz(yj#16MXhU0(tJ5UoU2 zZDC+uV?F=D!X&5S0T*FyG`&CmFy8QTssIUD=?n3k|?cLRJ&5qm+R&Xn4y>PT_)PBq7Zw5 zC!uQ1LHqRHd(n4s(HS5E2r#I6BOA|;F+qhwMQ|-Rt7X#$KZw_r5;hr1g&67TAPR|z zdhk!rL+q6V10#eNMBHpD(pnkKEj@^==|qQ#@OG3i+{{5N2a zn*r@L$Cf)yI2J51a1_0IvSIels_6K{#P^z(heOVX^NGzX;5C3@)ne@Tr&fb$OJF3b znZ$t$)rzJ#I`W)0Aro)2uUeiSIIk9LH-8Id>1-Q9N}B)vD9I0RNP{6^u1J+JEXbg*2Q3yi;>VM{#^&1vf273xpMZaZ&YW6tgIcQEoWO4y{o zqe!m_f;DrBIrbK1fSWOA<+}!XMvN-UuXbiduhbR|DgL_;htd`{aAV0->FlQQeiY=0 z=>juibOhpK@hk<>8QQgN;#d;kLW`@I){No&WS%_#}lu8n=UcuviSRgZir1_tWmcP!X>YH ziw2a%#YLN*uPl(jM81q}`}T6YqB{5BE(GmOx%u)bHdh$7aQHSWNZ zD`;|6==65S^Ei(ip;l*oR0GEI(UcP4R=0J+W$FwO<5X*Zl1x5Iq@nii(1{0`aO!ZyFM zh5g4P3YY8X{nQfAM`%U@+U~cKI{1v+KLy}X-W9JGCoA94*5cJI+Oh(O-<77>PpOVi zN}nPaU`F2eD>swl4`IphVmv_wp1xapKvQZ@+Fw)VB$?&K0>{@3Da#neSzqg6Pgz2? z4%n{MW6viZkLwPS{%yq~7=`?x|12{ZQ;)9C-0o&GVLgx0o#t}hQb5Cg@(U&tX3ovF zz!U!R!;vGUiQ84fGCjUTa*Rz&M)b}yyx^!zuQ@j&g{>Ct zr^TwFeN-8-zrcv)acrJ3ovFd)E*D=BKbtb-bA^X(89h5>z#5q6e~d}6dW*U^j14dl z{gx=Pg-c()ovN`qX?r!DR!K}H^?h*s9KojP)!Kase>hS7>%_$T8YmmFhZVvVFI(t@ z@}YlW7MzK3o}mP^b|bh&r)M!`A6|)7$86Y_x~91~rLa!W@Tr-xadJ{?Zrc5# zWJW5;@Tk{7{;2!c-QuY2BwZ#?H^Lt~_3&&ASZ2vaD){dBCBtluj-so@ zqj(KRd4e`@6gT5Z;dPShlE77?0eCO;=z|dR?rF@$Lx@k6a!Se0#vT)fXrP ze_Rhtu(UFO5hP~FWuuD5ru3!AV?b97`^TG?!`mNa!wtAq$b$w80-LE_o^I{GwKiGM zk?eGCbfaWjDt&B${#h^?Hq;*3oC6_<>mCxkamZ{@fGOIN>7I@DC9Vg(%OQV_D7hs21%Evh%;pKZ#q*@B%CVQG|?#s$y3DGA=2Qen;4|e z{`jc88wO5Y0_)WcB%d6Owa$lX-}rt(HeY$Ki-q{w`_mku?c~odXTm~g=E>9Dd>fC70I3X>e6w4I z#IuDD3F6c&hbSfJV=F(cC^fFwQ|Zh*kW3ho08<85{_h8!h3TqFpRpjP$W=7){Z_u^ z`e}{(D=MODDt_C;7bYY-V%2QVnbFo46NZA>^zYJ_&zTCQn1Mw_?FUHUpxJkhbL^7( z8%L*QLYY>|>7|h98=i%T5gY%W+ebXLuWvzhrR$|V}Oid;h?zmLLXLtMGyek6p zm}6*UF2yJkM#N;Ne+48%-z6sWv<4fBPMGrDaud<2;$&P_+x0G*hnJ)zm-OmGa-zTP z3po$D*FIjpI!W$`Xnj-T2K%Oks1RfViSA7WrwqNuwMH5`KT>M{>k5Gf2}g=GtlxNG zU}3GpVOQkOcKnayLtiPEf*A-PdE3s1hO&PCr9t=W4;Z!f4tdpIJFB%jKDMV9`^P;i z6-&wIoL5YwB~K^cNIDo@UB?7Q88x;ZF7F=;FBM=3mC_zM3U~*xJ<7wfDH5?DB2~ zTh(CInhk8XeI!QA0Cu<%cW-1?634F7pisknrbZp{PEvw3rCg+AG--8WVxsn-V!&!G zh>HBbC)Y^`LnfdV)VR~S7kikuuWE#*psUl)Uane5apTleXm85RthnT<^$z!#xobsG zMbBlgd_8&S(Kk=vwaF#8B!l*`Aqn5GR#^Rhf1C1hUBuft#?EBp?2f+Um)DE@HeoGq z>VL7n*73Z)tU&=9>Fb45#INMjW?Vi+z^F|+BLFe)?*@w#Fx=C;>4`jEd|mL1n^<$# zM~CSiUbiP|f0n37njeR#kOm{3Y2+X>T1?QWLaKwC2ct zsBa?tB|XCJ3yZ>ql|Oq8!kg*R>aOPn_t7wTulGMVwEx-o!|V5)q<^K{wfuLE zez9p?Q)tY2*bCJTOlPgJuC5wEF{`DK#@+c@8sTS8;Rx23|1+-N(dRY)?KO^4^w_`E z|9inX;lI-8pPjQDNB=FjfCv3GjV;apvS!|WN>%H>$ARVPfA1Kd(esMp3%Z(!*FlVfY7uz3XIxyeXE#Xmf)%RF zqK5J&5Wmm5Tm`U95#DbGB8?XOGjr$%)+UzX{qbKFM#xBiiNyS_9}X`XgOH}E$e?HD zCWM+slAIYgDA{9nct$`@UgK9np|L%B~EiF&#_ z96*$8idKBZ!A&(~DZgx7fF1G)@jwhg7CkU=fH~D}!;n2CZkaYN7(^X%qcUPF-+Otr z>?x4`bibX`&_Fm6i|({gYfVvKU;kAQJh@B}!RrGQb&Wfp7=OZM0sfd$Y~7vZ!-43t zx5%bogQqX{H3$DY-4O53x)Ku1CBXxc{~B1iRLcZdBLU9q`_S7k?8B%d+RphVpgf;44=N-H4 zj0O{_ybF25_ZC;vilN!#;Q>!$>b2g7>C)1*PW_SgI^$+cVVUqhzg55ORzLsDt={&x za8rGt`2IsN>FG{$!26uzN(BU+M<9jl7I?amNd>A0`Y!r}0k~e{4aXODjYXUsblJPfQV+j}%Xu{In||e!BgYoP5#LB+&)bQNvg78v zHQbhKKTxzB*ZHXM=|+r2eY2rI%`XI^l!7$hFXx8qXl@!u) zu69!Q%uKBfv|1zGZ;Xs#(Qn+5-_E29bsQ%dBPEp~0?(!SOeH!>R9I)E?Gv;Tgsj8w|?{6Ll=np36P z5saQU1DrP-G35gc4J%2QE@VLU%(tA3H9A(jyZfLtU+JCvzM2C_9Y z0w#+jc1RYGR7Y?vIQX~A>}{}09Ks9IoiwhoxXUksiD8wBDp3b`PpvKs${jB7+)$h* z-@f;+!b1t*xVBNu)t$tJea`H(l!}^(%-(SJXVPb2ec)0{Gh)x+uBvfFnBGR1GEeNT zXxkLc!V|C2ntAkwjlwPEIdKu{5jVx&4+y5u<55rnmTJbETox~RKH<$^3L`8579%5^ zsQChEyV6G&EzOkQ@fH%l>j0-v@az? zII`D&&J&N6A`&$@XBBNf)hxMuqtmR%w!Rs-#DHDx#KW`|lUyZoT<{(N>RhLN_2qZR zVG$AQZLdf=^AfbaP6Rfi%evL$q`elEKi>3C6kB(R@V8bSKy%*_zP|E&a(vAacE_Jw z5VYQ%$8;W~zb`8AuF^Fk|3&1-(z=%QesLB)`gF&KhwBY00aMSI^ouQC`Nwm;sF7{` zr!pprzjdio5mepRU&Q#7>O&5M!A)uOfm&sq_YM^XyLw_tP8S|ED zV}8yUc|Iu}2kqhW1$(&0$Ps-3uror$8px;$?M-TNXse3UW_&zVLHpx4ri>uat}Ay8 z))hCw++c1>-_MuS_!uHfMKKm;O>w=Nr9s>hc}aoRf%pkI2t ztkHjZxat911Ceu>7k;@_#^ZSbWf|}F%6IG2E?kLB>p|uol}M3`>&lD=y2;P|Axse=WK}hpwk;nkk{Ir_@aVY;xfQO+Md%hECb*Tb!jBx);-N|D?iX@$fG@jn9=r*qTII8T?BPyQ|Un~az<7+!aO3iv4} za+h-{y#GK)1V~DqhrZ?VnG$qt^Ivfxo=^`0^RmCcooT#55)O->2B$&pUdZb zTyQ0#9SbkdjfSWrx2wruq8$g$)HvPQhm9EA7kfq^W}Tf zoDsJzR`~)0Ia%_QhUcVk!3{_X8z+l}yNpp@bt<(_iG4uzqUiV*NyM1R{Ai9o#i;wx zjm>}cJ4CMW2{ZL-lBdkAIs_fWGRIU!Y+1L1dBAr5x)FMBsW*G}p)6^MZd>6M{p&Bd zuirShI1$}O@V;1nS`n)~xdy=1_c1C{={=CBPl}duG*V#{O%JqIry??)xJyun1rEi} zB*lWIxF2A~e#i0hUl!Ark64zrm6O!rrBh2N3@{SRWyL*C1=A_kr?jB&P#A^ceS#A@ zC%XX zD&*ZxwLK94P+rUpbSri8#+MuNkeVXeSYp^D8Mig8GmbnCPR_s=XuPuZMq+`xU>%3H zm$)lHCK4CAM_;o*ZV!e;tfBv)u=?8R#hQalADewVt?8I{F9nm-NU~(?fUyJRcg#!;T1LAS;v)5nZ7U zxtGEf>x7LorN3r7-Blcy7jgFcIOSzbK0P|jeFw=SX~COFbe+6#DdsaS4fYuewR^Vg|MfU3XZSXK_!Z}8FF zEfY+AAaI|%#TjK*V~V}4!c9;UyR8gOI&6_*08p!N4d7ILNO1ZgiK^cRz3O@7xmtg% z-He(PzNA`=goao=+=X89*j9XfuP)PCo$$!W9`9b2H=949%NqNO`s#eW1*asRXOySx zEzw$*=3?w;m0uT-@%@ASu8tb*Fp*_z@RHp*|8?(Xi_n#h_C3f`F8mRdgv0Z$MXtYE zzsQ^adQ%+gW~9lZ6B651A=>^6I|8ZhqnL=AlaMXRdgrfThYwk>8XzZXO+DsaVOt#2 zlGXRq8VZ#cn9R1FpQCAK%k03Ock{~zZ!wb{#h~vP>o%h6H%Z+!s>k^9y1is=&?ef+ zY_2dlDIgKPTY8u)*?#W>h#V48WRM}6R4}5uNqk&k=rE2~J8Wx5i(;+O{GCWZwQ^*m z22+EXpid_{WvFNq)BhgQx(=M4i$N9&_oS=r=c+X^8 z>_k~Nu%vbUI2{Jj5)O9Rp%gMIlz3J1R@*OxcMki9h`@m$6O3LLC+cxnu#>2kS!o=j35LHXE+cL+6z!1|9k_mo-V(l_|^CXX4`QSDRR#3R`@8vGcpoaM~yeo#f1nrBT%3`EqMWcQ6GD0h% zt&HBhHiuZ1VnwTHoGUPOEa^B$qXB|ULt}3V2%;^%Rz%U1(!!fz{h(}!;y2L8gd51E z^;Xs+61xHzP+VLsC$eYmMD)%B$lwxmA`^vQkng(uDVCbTm#8} zZ&?4)RMQr=T!)mW!66T2TXITbcT$j9{h8t!@M75sExn}=rc$bSJ zZF=0*#&x3O$j~^|4I#Rf0qsYdx|8n(|0;;yYxat%NhCc-B8xy$k(wBGrF(v z)3;a%tVBqYy&zJbGP}Oyz8)H5n|LE?!cWCqUo!fhbtL9M$c%bd^fJeVEYoa8cRXRw zYn<{#nzl>ex(!WS={?fhFj)_Xdj-+%Tg{D+x(LWre?779gk+;cNX@O-Tbp6T9b#wn z>7@&qa|TV~eQteX`WgCc#b5oC7&5k%?X=LYuL$B{bZ^Wmihz(v$b)UH!fS`z@gkJ?xGBUSwCZp&%Y?JCwq1ot!(26 zHw_ujHfr#cZDF`Q#rK>Z7o-JSF1VofPfF(+I~}G7@bJvR$=9)rLw8_E z?7|T!HG6(9@woFRW+bcnu`GcRt97Lz8grbY*18ABB^eRI_4t%MI62NLy)6RwsAcgxQD7VjJhL49Rg*xLn+g(U;-X23Df3@04)Sj zfeMN|F`Xn|JO%LpL00;Yz>4a4~0ef__-PBP- zs?SIqcL*Lel#46zZ0t<&~dZs0>*wiUTQ*ptymFjrYE$IEQ>Yrrr$ zKIm!Hfn3(M6z&G|<}4|Rnbm|QXyd%F9yzejEZ4R9 z=@Scs$OVT4;pE%-IQMNjJm_2DO=*afY)>i{X}`Ws7fY^bIz_u7Az?-&Ox~ld9rK_0 z`}iQ7k+8fv9)-l(N?+;ql?=3t`kebr!KGzN95HVo88Hsc}DwUeq85pU$&6(~~ zQYDY;A`r@&7m`?-3YiB8`piP8-x*0$zzW{ho+9`r-*qM@?aO6~kV4x5R5b%RT zydmdK4U-`8tj+V_&8Wb`d30h;o!2^UhQ%Dav;L*qR4bSDWvO!kIEzqJ(*`1&$%z6C z;VFbawYDD-|BQ97vXMl2(bWZ~NEId7)ZP4!PQQ7`A$brjSty9{N9avD3^aKX!<>d^ zh%8QU)bL=1Z8ae{I^Ibgs=YV*Dg%V+L_fqUvF`;e_JVWN@!=!y#HX@70=`n3rD5j| zB*M=ji8J@yPNLB+VRy-LNEQ4Z9dq=_4;J7^TWUI4s}x*LDp9PILGq z{v6k-(GsOuNMc18t)tm@w~QMX^}q_T83y<#@ye3CFr+T+;N3+o*}e^FUcU4 ziVzNM?s%y*^py(w1QFH;r)EP)r0Ow$9pA3)tYO0Bukoc+ziRO9Wlak`ZhoYCJkGDt zU4F2fzd5^*X{!6>?0B5CEk|EdL*np!K6hT+a+PSH#(vcWx|E@cnG1m7$G=1QkiTsx z%TUsBoAr?pIGXpI~aob+-SNvE-bN~g6ec%HZo}g1m+lg zgojNj(6>2$Q~L}rzMLvm{|EOWH&;%3pA8!FpIy3N*mrf{nOzc>-cU-ZQG=1WLlIW^KwOghF@g&}|$8Mty+P7)8 z2}sJ0aJ3nwlTg6eD)VV}jIgJ603FFtAJ_nYl8koF+Pa=pJNT^5=x*X(6prOZ;j8Ry zD2Fkv(LNam+WMm=@{#7cQTRmOSQEg-7(9X}XJ>CRE-c1XC(WB&S9T~ayZ3sCKWA-p z4PSkIZ`P>D#4Th@O!8f`8hW~-J&iOPmmW|iF7FeJk7HS}F?4XGNVuH5-nbJp6f6Uw z)X3jwQqAR(F=s_q`4SptbKrTxJmWl%M0`8HV3&DsnkA}O8mg1bVhSg?-peTXyli4w z#{(z+(=C`X0++ZL?3991)zY&&lIIEiWf$XlKS}Qpegu^mGA3}FIqk_QIs?3L$V=-I z)Z(3ao4Yr^47n9cGR;^&=5`Fe2@y988Ddx%{!v`>dvmkh$K>t;v{T?QrjcS+f!0^y z3^$W+J(|^{GrbMt`Os7Lhb(5ObEoxuEwf7FZ52Y}v=pK@#qH=N52MRiNExLlMa{jN z7p*wO@S8>zFZCQ72n-;5Vh>|;i=!WC^K?wD-{Z71MDqFcb68@=sSwGL8O4Q4f763U ztlG$<{T;{ZTtP&2dh`8-)L4douVdK zR~?NsLwq`TfYQP4|9yPtX^5L}yVWt{1oz*J~ciXj4z? z{P*@IZ%Y^Sxq=mJ(^96hQG?7*$w$N)6|EPnsr1rxm^~k7KrZvFVkysDH*jb|qUnhZ zY&OQ?js=|!kiOJu>#j#FtV{Y9>A0H`%$D51d@tUQ?2V_7Yp0&8S;nQ+W)CFMJk@l% z(Q-mW{5z}YUL1%zXRjnhNB+rGfl|)C^T-GGq16#V$?+g|^E%jrh1n3QM z_C%PoHlG+W@oDuo5vjcRoEbe_metNLH==KR|Jxcf>*#JF1d0Q-F1xHB(mb%|OU{xW zCRp1#x7Z2BJli(d#*euCcJ^CV+cK8zci{pgubQ3E1+FJJZ(X56H5D3i2YaLOmK{4g z;7i_{Fctsb*L{|c-X)j(z#@-*NYnY)!;X|cuw9lsmfuJ76OvCqo89@Rt1&kv5T`;j zZ?{^DKA|<9AB~Eng4W`3FbPk_QOEhUaULU5RKX**+&iUjx9CJBkz%eXL?+ULe+8hu z_Y(S%G%`$L6jUAx<~JTDiMbSVX?zIkX`%uIk(y{z^S#D7xQaglB)@ z<+9KWMyA9XwHxuo>l|iy`VUyRVDa0HDO-)~%Lp?s`k7aMi+CHB92Ej&~f4eB|8~bS3yp#=fu%hd0Gi#Y;~JQE70H)Q}ib*BpGmXl$>-K|qxm z_bkDX`hC$icH#7x)dTZ%`=X={8nL6v5@@t#FzDEl=&mBep!V%wGO5y6xGNs=ius?Y zgHl`=cr_)QqxGO5Yi3jyHnqlG|1_Kx$}~^d;g+44nXCpcMCP5Uptj4^R{>Td!X2FM zlNN~t@4TjzEB{X~W~Om_-I16~Ao7{X%|tKHgjKgr=PaIKx3fmzRIBm+EaY^q>8f)t z{JYf{m9pMWUzA|L5zFIxlQH?ln()b0h6lcO^O57(&d2V1e>|OLyZ_0TWadJ1|DS9L zJ4690j|+`Ds^t1bo41Rx;ev*J`QW5r?w(ocW9fDQ_x8%tF-Nm(qfXJ5Zp-Z|WHx|a zl(+s^_iy@!e?I{4CaPdzEU|hq-+mQMhtD7adi8?MA(`<4X(upEL$i6)` zG4B^!pdlAZ<4~ZurDrA8!$OLsSN09-h}}jYzJJnRJ(~K;rn<09>z!Y=Xbbh(!Qt-r zgkf>qYHgHi404FI-nwtCQrpZmUZWew+{CZdn|)~E3ub{B1w&_FZFZv6$99n51?>bL z;XL(-Pks~jwi+%p=K0mIB+}*5g=J)H&C@h?^o94C*;PNdiV)&m8E>Ggn;O3+sSa(g ziU=>UuyZiza2LH=@7t{?>tbbHbgec{kdcuIabYxXoh3PhgITUO_*9ceQ#U(nnRQOJAqHzd~NwVjV@=o?M4RMd2!d7 z=#Kh!-Z6yxsd*PmOX6?Szu>RxnMapt1WlVJb<%&}{-tBtYNgGZvPHbIq=>4ki)Abp zNc)uvcZC(JFE3-M=aV7*@z3rQ$gk*7k_N?{k<6PI+hFg^zGp1ovWDWaiWX!f)hYRg ziFcWvQc|><)cZ$8UcDrQU9BE4l7Gz-^_27{E1yAJHCOfJ1RUO|UWimVy*%xqZY5en z=4G*-8$ctVaf7r@M`{m`M8+1AJi9wC?KZcw9;&lD?!{SeyZy4hb?Gtd{*}Ui+)^dD zt?JK1+;Da1`73V8A3bxPheF_*zJOjNiaTbxilP0C69>cK3ai7rOS}S^D#By1KvDfN zbcwRbJJQ9SYW^ZAZT;4%w@;;RSvoN})n%(gT5?d~ z&ioU=nm&iAY6DXQ7E@Qu7H%?)zjf4Da-oNMf|CQKK zq+I_$ zVEx{`w@+85@W%@CG^g7~o>S3sk^-U@%)Bs)m61OZtvJ*p+;k=YJP~=jys6y@ zqT6}};pxIdL|aHaQz))Gm_^|1S0&?e)gi~fnQP;4^%_vigzNktO3O2?5g;j2y1?1= z*(8tcv-Jg}>#pe?IzOpJLQ_(j!+K_s zQtf8aXE-Y`Y`Z`q9|G#@{}-QZu&tk)3tmy=4r{+gnEIvqUC56jF7=O|A^$;xY9GX+ zB}I*Wb1bU)IG|Yss&&w#qEs}QyeyZNv-$H6o&R$aMfzdIj}Jz_Ilcd^9C3aym-&^_PQIZ4D>Q~OSF>?o@ft|Z^hNM!D>39q|>U$)~fxn zLD$S6n^LyS+6k}W5bEM~AZ)DLaQO}*(I=>D0L-PXFW;<1y#jrmkD1vU(C!R)U{Mpk zF>}IhrDxA_$PmxVeWc28W8nt?ul{AlaGN@_4(IfMzCJOx1AUSX?WbGi+zC^&Ld=(c zLYh#q#0<(sp`HMw=$4F*ncL4oyO6w!Un6>#T-PTB&0ZWt^*o;gJ`34vC2HnNu;AI#?C(s;#KFd)TAGo%>&{gi7(ib)4wgr z@|T>joD1zFFx+3Wl-d+hw@k!;>FgZB?GW`k2&4#aZCHM=xJl!G?mak7-R2c}43loi zbTiiDS+}#rpGX@2#x6xs3Up0a13GhE0BJAVHDtlRKeY7hc%!UA))5LwIPRe%J;xqb z4fqqq%H6ZE4RXY+c_b*?aNRD>a_&*ZEw_`UdW-e;n0A&H2X;V^z#6}vR7lr}{>+dO z6Qb?5QSSDMaXfTB=Va%)vEsU_Ca?H)l5}iJ>Q4maUjeDtgOFd^^!inngl`|u4o_N? zJ>i8-I*=^4Y||CYHK|MM(54qJT^lb0^{qQd7(|SwY8A6*%~M`it6l**TQ57Ge5X#V2{dXQ&y?UnQ7or-m9xH-7qrT=SeL0SBnRZL3sYX%dNa!Jv`;K-z zD$zRkmmj_7tfmk_BQdz48-sO!fB#Xc10!FR*79=ZBs4@K!y8}^Ue8;$G?ghUv3v(P zk+>c(^FG~-B((a&pu-YC6MXZlWz~QlkK#~$_UPlCoz@8%fv~o=4~X!HYzofo77NsK zS84~1&|w@pIbPe04L|{tV6F5^g{&h|%W>JAgC8LD*Uu+p5^h{?e~gZ}AK3zD#ls8G zMSa}+cv?Mg!Gp+2gS`9URxT-fli9&(BT6Lq@$m4F1yz&cM0{0aQL?TuG=l&3N7b7= znd%&@xgUJ8w}ARxrNSTxS;CYL;Q7xSWn>~GBMyWO=Un!S4bu%?=zSHt0-A@CNFMS} z&f$ce73M(HVlv|oGM=|s!<_5L_*>WO5ed`mW;3MgayMTZB=ZR=qXTJCGJbR(Tzkmb zWm`*zcysWv6sf{ z<2@ue-)A{l^w1$}zyGC^&uAclFMSTa_JhVmn$%rdNVE7mm6qmUtO&l>Bau1Olk>L$7Of2<57>~On!Sb!_7jVw<9wIP{9ddw%P)jw z3hffk4XnD}dv-YRT&!$1oM+3Pokn=xclh|-pu-8D{ZONa2cse*i(awRWg}j{b>4`o z-H4VrnO}ayDM)wu36g#8L^%aCgvxN_97pp@m+XDynAha_=Kw`dmhN^Y)(D*cZerI1 zZ@_$s9sq^bMX4u!8-zKEiZaW81JO<1C3eJE;7;{^Pq`&p_o#LqwNT(8pDCYr z`RVbBQZU~JE7NUPLOYnyqV@ya!n4f4<{hke>$lP-rTDhmLlTc`-6R2TF1TW4ZTI(D z&tef961S5!r=6?-u&^X)Wa~LV9MZO3K65?Ks-{lu$1_~j%hgp~I|qmUb3j7s*|_(m zwEdeFl;9p{XG61SzA*x5E6b?cq_Y@4&*z?@LDrGZgqC|lKsAyPwds27x*VeEF#9QA zWLddXD-2lYb*JfuCBNrXiVh_rE>|wNJk;QmIOUuQS48>}LoV+ofsSN3L0*N^PE7&5 zx)5F4h9dqhayeRIe8&DnLkincnnK5BlCmHJapuOje>a-b6?8S5ZSK)*h29GqW-?5xzLA0gmZ zo$g_`b+&VGRAsNB9WZw}+Sn{-x-gu6W2asT1fKX+%J~{OuRNAH8<1c>wdCE-&faHZ zktr7QQ(ao}p|8T#zURY66}Hkflb1Z|?u+$5gR5tjX5Wn0c-*~VL~mc@V`pa&WT-Um z?#bI%AUxjrD5Bn7pCG2(XAsO!AN|H&D;~xl2EJSoN(SW4HmDeX@a!7pB>y6_qC|Zou^cd#UiCwwa>G0``Ai>eoEDJ{cx_& z@d0LT&r%OyzB!=Q01}T{P=$fMc!(D*bBV#c3_!g)E#ECs?MEIx*Jrw>y#_8?IGbzS zr};|l6>Y^)r@rde7kES#`XqblRvdXa;Hr&`?ys zi^A6|N|$gUs2_|d(f(4u*F++&8r9jtu5v}X`^^h6?rHxj?Y6lBl88x1=)?!)IiI+{ zRad>U9LIw7m*~4`EQTBhY^s2h(x_k%B5U4$US+m4{^u^3`3Vlv1%Gml-=up^fA-X6 z0^FLV7LssuIfCuH#&lOLjJVD(kfvjfC&!dy)qOP?7tR*;y9GT9@R)wujHhb9DH-ue zIhqOO{xe5L4vn{%X^w~p%9N_UkZsvH!eB6F5Y*A~nFVS~GiV()gu?>;kn9IXf9m;Q z5IZ)?{@ukq8HN@#%A)Ad(c#vAzT?b$n801?j?zpes9Iaj^2SZtf=a37TQDz6`+I(V zUyP)ZAejlIAMRF340S4qjf2}rrt3HyLM5tk90?(YODFyuE6yXX=^)Kcm*|Ehyy2X1 zZgDsqJ*Yj2uv9Nv$m3<<=!R_YH zz(w6PZB$|B$n#D3EEPh^z)MMVr{uhhu+8f-#I5gJPGuB5a_vb3^jLwW+GYC8O_zdh;az;+|%=BU+33Z*vMNBo+_ z2kLsBO8fI-(a}%aYOi#m|Bbm!ytkVYcAsC~sECdH+X;WzNS;|T-z6R!q@Ogj^^s>} z3i9qJk8o>}V6j#vt9HetlKN_az(e7Klo>uOJ{&#hhl$}O75^#X?$pCrga_E3^fIb7 z43_q&Q4G$yn^HD3p%b7yb`skqCo1g^@G_76K;$sHltVFEA%W zOAImLA~vL{n4!#Kfqr&LQkZZyU=SRQ`*@O_PX5Bt zTSd6$4S1?naWjV5KZBdOrm!H8SfFZdBh6dpSTG1oLvJ(kR~zF3=dG!iNJFB>7WUZ9 zaLyd!z4OVtB*i()K)pY}7lyXHvC|Gy1wy*#bNvkU`cH2D|2yjn<4kdvFg^s?$(U9EqikyTyHVU&?f!Qnpek z(-$8=63QFUgSJpHyTZa|(!oMpJDD^9@`V5YSsRzDVC-CW0Ny`%=8#mV2;ikH-dV zMU30)=|}J!aqjv1WP>O>N+9m02}vL+YHU( zO4(?KByh|In2B9dUhx4~4Vyr0>vqc0Ii!%}ffACKPd&;m1)@WA!h9{MKp=|TAhk&` z6~nKH1Dr{wg2I=eXhcz}XM*J`&^8I4j@C{?KMJ*Pf7$P=9{ihF=^p=o$%$j^D4+?c zLiA&ugoyvNjzR1ep9>TlSoFs8kdI6Sm!x>(DEk3c*A0&^NL=sa01$@V4U~V)&0IyS z-gK|xwmPx>(^$-Kf=>d$4TCegE*#_(sCqv;;sL=8jzs4XDN01_jgm<4Lh`0nGZ3=S zhSLTmFPtNQFeyYV%*7+Th5_LkwPq90?V7Gxb)JRhF3R#9%w~B8D8b_y4uDn?88|Ht zDL8vK+XL6uL6*Bfuqj<=X)6fjak76uB$aL&0LfC`G- zTSre8(Zpl*wRWo?t`@aD*gvYhRwyK-*CLk+&}3LixQxDYv0 z?8D~Myuf~Wyr!s>QZl4jH$`@7Gh%=S2F+VCIYQhya`ZLqZ2X=NjDQqlrp@R;2TJ0YMdve5waJXfTk4`@-@zI4_1A{(W{H=o)6qdl?fv3Yti1D{9Jsi0 zw9^W*?Qs4|qz7$o=@nq}_#Lp!qzl6Y+*`LJIaU%|Y^__hn%x@Kz@PJuM2b0O)y9Rd zXFR1+^Nz4RW?jN99HZ-tDbMGFKR#<&qGtJ>7fJtG4ped_Tj9Ip1iQigUl= zI8sN|jT-=wLH_rvMPVrL7!WCUeW{*n z-Ev`IMRr>(FoNrzCxs{8E>70qbW+-X$91acsD`5s-LOiyE^A?`55=pOH^ZdUt65=M ziZkNNf6v!nU9lPrz&E?^nNOXk?Usu=DTkTd2!ddVfaD(yNP+UGNK-Zrb<3z;% znd%lLLO`VcfBUKpRTrgly?^Fxhwu%bGgWVgIhQL|<*h)0hk>CPXvN~;>8>+w*26)t zmhG~O2oo_Gg(+nFiA4j{e+}o%w9unBO>l_~gmB%4pmk+7BCGh&k+0o}NAuAcf}-6FYyH&sLia6;BtVK=!nB=0{pd9dI3?)U1ePZy60h_rG5x$s zebNx2Le_Qc-w6&5!3}z!w50UTf(Sv_tpeg!8plWI21!5(ORv5DE~~bkr-L4fT1MQ& ze(bO5?_S*1dKiV{xjNq~+m!cL@a3PZ|4NMrHlD~j3^zNkh}g-l!+3U}cs_vc?oeso zNl5Xn?ss#T7*zXL#hHHbldK}zCXvvQqZYX5`)xN%mZUU5M!Vy|r3Xh@?vr19a$LqR zC&%b6m2z%UtT*d#cu^>w%K2pR8me|Iore$0uE2!h&JEG-7;SSKAG~1yt7)!g_=PcG z8RZ+CwzWUw?x^8PbQd(b84CUOr*Wvo`nN?Nc=tLUq^o8pbLeW##KKBLleGCMF7%S* zM_4doNK~_%W(O-?+7Y7IM>0kVx3kpX43ov|=S;k`_1RmlC3+}#)n-nQqy6Rbwz|5% z-G)GfeltX~j5~e0wxIjy^Cu~t`nl06l7&oLhN?@fiICf$rH!*ttg`mp>+X(x!FOkH{dDpgPUk}NmGQTFmx(KG z2j|y|l%+N+lCB0!jz?n`+ou?KE-dX+?Pws$R?Qg2Sr?4lm$HX_`mE*p47r`&O2$P}qGJ zbc*$;r$BkhPiBJYdj{sp4zr~-4RjQSrpIC}1mGD1(((E4a#lkoIex@JL}qo4Ma?b- z>spWQdmpw>tS7MGLPdM$-s1I?88|E~is>vVOJ}a|2d*ZRC!6s=2BkP3-jm=A*A_lo zI95VL@wpjV+YBuaS!3Fpyl~Kz0=de>agvuM)z-dcFQK& z<0>9GCZcX_zAz6l*Yj)#GbUnOHNWn-Q#sBW;`JGZ$!uQ;KEmJ)g6L8}^|7M2{J4Q& zNYO078>ES5rKUoqFoU3-<;vd)-)O4cpO*|Y{V$C#D|GJLEVJXi8Gi~tFTmocd{~n1 zCoC~E-Q8N>Yjbtp^-RI17*o@^-rYvDz1f|(_iVvq$LZSz;LuwH?4duA}xKi)T= z-=F19*z>;YaoBLsX5BwRAKkJ~p$|rN!z``(vA`62YWF>od$6zOX3AW>V3bddK@OH^ zOD^>-SON}d@8LF`W$@Z1e7-L6FBPw<-^fhUX&@}%Svfl5y8X`xdxcQUZt)yTS^)H! z!J+u%FtppdoLtxmtG29`!qhHVbqbPx=9ONhRH_6YMMMpRV)+&YbivDDu~i7FDvI2; zPvb#+b8TWJ2PR?8@eExeE|XxiAxkUDX0v;J8VFa_JI(^s5@XHMc6-Yx?6GH0qaKWUN8euWQyb@E9 zv6zM^XRcJ{k=|-xWp05dAzJS_Wt+d%W>pEPinOD27~=G!YYyMwMY3drCE(pA0CPyW zF)Vm)<+G3Z5x*CA1N0E|6qmQq>Xk?#g54|Uj7KP7o{HED%mDnx; zPjSsRmN15N%ERU9|tf`(Dm0|Be)Doha1qu3>{ z5}*8JTiGlw0$;P3StjgKrB!HLvxN=jYX(IQ#1v}p^S2inGrPN8??rYdY^YXU+hKN^ zC;*$%MH>pBgS0)Wq42SW)ZRo`gW*bfxy?Mc;|OP9^NF;W-T{z`lTC*~ITenfF8_wB zkUT28^R>`$m|Es4@xb)B$yuwRW#p_;07PfpLZ>XZ!L0DMX!SIR;BC&`6*?9=V>cL~ z^%Uj81jx1*39`A2>dpRH!56ndmj+Jv2hKdJ;(0|ZTBZGE~TvO>4utxIOt8POlP^Sw=|e2A2Mggg^D_VH!Q`M7DS#=mzw_@H-HXG1eb#YExU z`lP%cH|V&BGJ>c6XIDZDIU??rDJ#%~WtUelTvqGIuXvc4|iAE^xO2WA=)J`tEA75-)i*Giu&Wz=P zlaF~-Xt`Xl=b>C6)d(bG#ocWa;RNY;u^ERfF#{ywuyQ{P(A&f8UDphr(^{>&Lq6(7 zr4U8OsbhM@<7Vi1Mdy&G1E`m_TktDdAghgYV)eMHdIgbGG*S8khqCecuNc`x^PBbe zBh}+F{eYs!e4MJy23%pa-eWd5v_Nb&K;K3dIA^ z@5ToSzg9J4wbgZ4MO6a0Qdmi~3}nDKTV_R|AT##2>{tlal_jOFWmSt#&tI@J$|J3~ zfS5@ti4!b2B3decNEVu-ZAL9maT+K&SY{XT5Le^@D;Z7JQfEat(h4U=XM+vgb)A>I zi0I3thYH+VL>Yuu=N-DTo;pjl-Y7fDEK$C=t-@3dow<+oqw zBgjo;eTsZ+Jtk^xWVLTHQ<;ah_qih8r!0AlXB_*^{Oe^s7~VEFOpa5SjL7aZVR_qK zdzc_oZ;j7X$O6l4@F=(~9&NMnwL|Vz@PkgOUhuW&_Vfhwu;B;)G1*PFZs}(<*_%}0 z;EQ~|+7eRsPc{Q_J_O^&P2uN{RYm;>qcYIzUd-Vvmyrpf+Xq9_@#^4eCyOGuXf&k} z4T88$FFypx?FkaSX#-`6DfgnMzKm|a#~&a_g9VDN`-KC%FOEkx5D8Qz4i6Wvuiv0d z$~Egd)gk3L8OinK{G*V9ruD^V>-r`~$sAAORhm4BF+CP9Po z;NJ7I|Ip-L>{mOl9Jx-r*}YZw5;AB7q*yTu&OYc}A$y*{AX=U)gChaskq`4Z+xc*Pc=St7>+}nB ztwgL=Qkab6)TE1N@T6>7+^XZjNMfT-1oUr#YUs;FQ@30Yrv6Q#Tb+>Dmubf1xRD5z zJZoLKRMq}wQK{5FNpDk16;yG?D?ie?PnM_T0L(fHB9RbRde8N>vE&t+x`P4W{v8G{ zJoR@dw?42gQTSPU^XpkAs}v5a(dV>u&Oo={UAZXAmK>;*k#4wqr4FwDa@9TIBbyEB zeO;uAjMi;e!3*znu^Pr>*@$YYlKw|vB>2gu2`eUOLHUXPVt0H6mET|o?y0PsnOkFr zs?q(*d-}A-dEWY@{fC|XRBO_LK;5_j4zp1=D^A$tI!zd(*MJrPe#|%ag_oj7)Id!9 zH69&N4>Z=X$X#5^WS^xo@(=^rc&AS*s7%Y2!G+#`7*{*vu*!Gf}NS zF3?oaF;89MI-rt;`bq&TK?m^j`q@i{n0z7G4v^ZPr&E?xfZX{U3M*Quj8VT{mobBc_(y6?}*Ijw~XTyJFD*@yOwo+IDWrU|r<`)Y6G%j~? z_TwSF0Df7@!-P7`-I%Jy)ldK@<+to~2Kx9!5QkWgqDWHi2uZTo+tO09%;v^3xHp0( zcm|Is2nAWoEM~3Z*Tp2N8pL`V6lQMAw_hhs@ZdIFkyES2r@tqq|LxYxe>CV7F{$F4 z^+q!g>Ltyv$HZ?xQD3{N7K5X@h%EU#Mf^_mJy95npzm{ggW`FXVQ`sOtrZ~>&fQi= z4Jt*S@-yOKvUHdraM;I-_}s0|w{Yx>;iB6)t40$mhC$U4$0UrlgIKf`d-N^7*i!v< zz8jV5hprOp11eOJ8i$myqLC|?smF0D?M%p+atq<_CwwaVX>K;0@0gQ}m<+t~92Z_3 zn7~pjhZZn|v3Jw=@d|Z1sxLsW?+^XVal8|CLUdqp{B8R`#|03z#wTx7)NJ#+wV87L zAXUbb@^j_z{c`!yjQL`0&SRxPJLK8psFj|`+eaAa$2`r(=mkKJ)PWgTi5e?Ih?deL zauWnXfWy79&{;kvDE+q5GcnQ>C|(MNZ@&kE%%JA$EMIop1T$9qELf_`$aESd1*}SI zsC0)cSDUw_$#hdiBo^q4WmI2)&E{W^SbE2hHH8`Hom?;Fjhx^BIFf#FbNdiWjDwhq zC|}=>!)Ac;RcS9#JCGQFG1Qd{KP_U~Xgv>bvE??q;~vA1k~3AjPCzh_tDPsJ9vps5}zcRB1=p?!j$ zJzEEIx{UWHu^~RRbPG$tPkv8Pyh3v*?Lpf!;?zghwFhR$KoRnTF|mD`rRY(;WuAz)hEIIYGfT8xnSL7_2XU6l*kN%P&H?_bW;=R-}$A69c-Ayx`z%~ z6$s?hQHQOHryqZyXRL%W8S(#d`O_6faEzy?i}k19;!H~>tyV}1Pw+ar+~DSQmXJN= zUcar_vta_~(?g5q`eaR|vq9~na>CjAE0LRBZb(B~l_X{AU*<)vuDrtDzp6wxXK0DN z*vg6_G@f_1NseP`%-6{&daba}ThJ4NILG4(C2NlHWMpzC7bB25!NA3D1VH%^1TO&{@2%_2P z9nNl-51^2~7<%JL`$5F3&|P^&2lzSn4MA79e>jnz^dzmpqHm~xAUQi-r6C#wUrGmK z@m0O1e(Xf-KNV4A-2U=a8j~X8bbqO`@-n8J=Vxa_>nxs#0%V?O!xs;po_qIXbjzDF z^Y#50Q<;t*5q2Zm8rvTd+m^>?sRj6QfK7{K#2z|TACc*MEnA@nmro2KnTBJlXyMU$=z?t zi{tCJspLhwG)+gP3zZ-_b?3L?&JGUCL2%=&?bH_Lx6Sc7XS62lfx&@Iq2sA>NXyG0 zCHvI8m7&QI@qb=r+oSsw9BvZ)=J?f%Fp&)T z2Q%li+=?pl8#9l%e(*>N8!fI?S#s;gAg@1MD@;%xygTr$JXClxPJo$OT>u7l z_Ar4-Hjs5<#GRG)_ppt^F#|P8cmCvm$fIl`LeNrG#v^;zBL$r~0>goY8owjgxbfv$4W07NFES5ZP>%*lnA}op{{~ zZe3QEijyJHhgn{a~i8{pc!erTap>=$KXDl^X>u>DU6{4}zj~(ct zRf(|Jkf2_?%k_Q;oKO_T;Njl`Ac} zZ)9V)`d|3HsMYDaUqup7<@pKWz^Gy;eO>DVDm`E)UjbZapuxlK5?Dh|g8DdZR~;(Z zfDzNw)&U-Hh5Tj4Bu%@<&NT_f%1b1FO}%j2jIE$V5Rc}WJC zE>S&9?d2mT?X-I7J-HXb&(_ggW1ZHhhKuScgK&d`PwjIz8^+r7?-j)^0&6oHXzk3`WkBtgQ^S43 z_LEzfQj5u7NSr#PNv`&HkI_#74qT1tBc1-n54mV1_IAE2rVnJUxKthQ&&pSaT;X`3 zr%Q>osTMYUP0v^k{j^oCK^ADMu7Dc;^-W^>*_+Y+k=^3|S$hS|_JbifDbN8s@a7+e z?690%>En;TGXE8Bi{qnd@#WTI4wuwYn&28fIFH{I0E|@BG&7Q@zR3`;^yc5EE0ZgY z287mSrQY-g1Zf^Gl_vE;bpo1NBbQ8G^=xKMXSVc*G)I#=A62cmHp?$wt`lQTL)3^; zdCkBjx-1=<-isB;YLDLzwyQsB`o3IaMtYqOhymfgH1moOvqICMIf(g`Q;}@v$z*&- z^;DGbF^h%%-Xygfpi;HiOJ`I=# zHXXTIZCRfc1%kpjG1a3k2rsNElgV-&AwtdSR*B?yiBVmQwvh&vGLk362ej1^l?ee5{wkGJAn+zmOtMV#k7o7bLzt7~%$U$1-11@*x~lsA??u=p zII?k0Npq3e8ydax5pe+zea1$wRIqzD@X!I@WOph*-v!$K7H@r{>7lDA z@;M47Pr1OLr>D1GIm-wvWWcn%xq3p(ii*wEgQL*^9heL&(|voUkmkIn%JA5yq`&TV%pRGSyFn+H2NxK!P(GjLIfzqRG? zx3;3{fAjy&)z%t#nAk6PYU+&y5-YBngF*Fv#|06I>U=$c;U?y1vm(I%EC2pS+-GVU$dszaK4Re z3^Hck5-&$shA*%Ut93+%LkW18V6b!4AtCa54A+pv&lwejnwO#^r6l}?8}mO{vIpr_ zMY_(b6a~FlYPO9~LIM{UCF>Czzi;ctTDzcfR0yc)I z$JS`!(;pc*jLZ;0Tl=C^|goa+hjULbI>i`BPqR8**vjgy9@A1~<0ZzgDTrnjs$L|fn| zc+#r*IekOcf7MzSvO+vxkPmJ6tcPjAg@KY*`+bXInzF+Y+w5wV3`J73@=!M+Gnwz`}PSFgnfHqSUr|n!;t(YFlY}jKG zQW8T4Tp6xf^~kP&Ah9)ke^k5KR#ZwCJ0^@5oL8=|2g;80m0Egl;G28RiV!DS--edQ z;a4{{zO8K&t9I*Qzkz}EMR!2PFqMBoFH4qKa3oO9dc)nhEvKhKjNURqPlNlJ?R_ZQ zXoJkn-~@<;sLpgqed(GdH4I|ZY%b+C(JB~*8dSvM??)Qe}N^1A>WU zre45-%L~5<&{fHcSX0KKqDlT1Cg?40X|Tu0(2&$RqNu)MR4PIROCJiX%}r|Q9Ahj(n} zhFHek@MzF&_fsum-PM*4mXY?bmwgW{j``Nj5pJ4kOK|NI5b629>v>IwY>54;Tr6z= z|HTEh!^aaqQ=ddJ#KO>VpChn^_7S;y?v1*h`P7zM3K;A|E!(N2${x!@yA^sXVdSC~ z2JIC#lHZaVO1v82h3l0xsy`~)olpvCJxLcLoq+gJCW_RfTJNragSJ(ux_GsZ-DHPn zqyEw)-}GBzm%Ab8h&Q&}ND?+@S(C$}a7u;#Ti?0Iw0J?$=VdRRpxWB6%Q{^Zhqd8q zl#D3D>x{rpj#OYrT)F`-r>&sfZ70r>2@G*hB&n&tDjv2}WR#u@P)EM+Bj??T)6wcu zYaJjPB5&P7xtkNR+uxVSP%|_f4Kx7L6VFIfJrjQnjQI)G1r&1$Z6mIf*ohY zQ&QiH0Vs5QsVS3;FCQfFUZ6{haUG|Mi+$lti#JKoSbuLWcc<`tIt1?_mr>Kp)T&oKk)#Aom0f0e)2<$f6dG>W@3u3C=0AX+ZD&C ztkYc1(;RjtZyp`5h+AT_HI6NYv4lN5)?d~bgTnzBkdE|04DP>;@tw{b#QZiFof=f? zp(E*~ug|it`e6a(rQ)PT5BsB_5^w(E%GUk5@H5B3k)6`dNW3OhlA*|aq4oQOK)T%& z_!k^M%)3H*STNBV+cs?fMnh=zduRp580*2ZI`^N_aryPBX z=&qj9Ou#Ed1hgy;=O~g%V=jK<6<4t%Xr+RRCsG&_+wZsst?8l76AYI1F%AFMLPiX| zzROl!j~sH6`_{4HSAAU?MtC~k<}ApSu{z{eGO0%UT$iA=*CQH7+M}<#LJ;-I%8n`R zU74MDqI>Sst8;$*t@F1}zi&Ip(aR2x&oBaR;don0Yv7MvQC6E1>n4P-?{cZ7p!_mo z%yd@x|5giZ4BOtWZ-C5DZ}qxZ%N`kci+Fyu4KJXbA^h&xbwxzvCxu2StDojWgxzbc zNG_0zdi|!J%FL8d_7tnaV2ltcHzcQkTjvfbZRcws3Vjd+&! zRlen7Nmu!L_IYoyj5qc#S1RRtd0Z>ym(T(}go*m-9YP~S_3cSbI;BO}qV%Sjx ztzB{JlRm2m%4@Uk!XB;`?QzyeD-`2FnVwN83#%j?u#2;(U*0L*H;CbddRF)ib?hCt zpSfgL%4}14lv$*7HKd6bPh4U5GWl%wv!ZgRD9fTi<(Q zK)uP6&~r!&xgr0=P%8O0!+DvQfjYoU(}P|ADPo6d5M!p2s!|Cj+ch!tM586Qaq2++VIQ)>uKDy2F(13Eq%*K zi$7Jawk4{D4mR%GC9RQsYr@Jbsfa*&%`LeeH7itSLO|6;WgVNU9HCh@99gvOduEmm z2m_+#7KQ?HF}Yy!iI>lnLi8`RR_%tPT=FZ+QT|<47p;3TDc;A3t_90K=c0AOW{19B zruvp)Zv9Ud#L5JlCCW2Ri`PNfh35!*i9l5+ip-{uiN%>^Q{QY}bj0J)ly?xu#RKGN z7gbfX1M7a^QtoAP`{mS~Lu2&|F9IUyHU7YJsk5 zE^B5<{ch~Ac1YgKQ+QF+NDg(V(US?|+aRe^a3D6FrUz4zR#*D0`wYR?Y3SM%_BUor zV2$_KKlaC^Z4leGz*Rno{wYw+4Xg zAGIX*yPTR2HLcEiWV&vlJ@1EIh$tbO_gxYXW+&eNBe4Sl3Ez4g0mfzV|1%K78PirC z(z$qDc-HwMFbCOaIumELf6%+mAO~OR6Ay*wvNHSkb|MUHtiH6|UZ2FCA169Y?xQ!c zd3_qPZ*nd&j9?e|;C%5s^Mah2G31ytg8?7mk-!6*MzODwI+zqRD^rWSWbW+hpW+m? zU;GT2=$5HWIOTpt`Ya?Z4t({-RKVjdBf+^*;T3CaB#CK!-%lz8N&-n=7o;2bI8CH< z_`_yHi^jKZ^bw|h-XSsglQ4GIb_CO? zx&DL+FS*dMW-M9UVxBn}GLKorpQyZ}{(j>}Vc!guPs2`Z+F@5CUqL;pIlp#$=Y^g} zGmno-?1d-u`9SqgY`)ugKJ33tH#Rgk`cEHgM(!LFtc7EAoP%4go*T4PrN`uQviXxt zZ4$g(to^*ubiiZ4>$DfT(j1#^KyX-WhFbUHa(%onzd95o_zT@k!Nr3p2UFa8x(=fF z?34v7P~euP2-10NJV>3blqmJmmt3|_Ib6IBXf!Gih^$R?Z061^C;2H4=>0ErJUE;= z4kwn2Dk;|A6r1RK&AX1IY=(lNE(W+h(s(~pkLrh0m>l9y@j5#r*^5LzwV|LAFDWeP zg)c*u#cR76P?T@$Pc_bHjmCVf>B;%2-lLSle1#RIl40}{8N6=ec}47dkek6r1#Vt3 znDhB;QE_K=yhC-S;Sdgd1qXnV9;FP13gTvK7ubT?;Kq44ot3(5a9_CevVF3Z)+E{U zSJZKl_VUydZGcC=MW8p`PW+PGp){9EOP1WRicZP}P^Pf0QYz!AO$P8)#QPcar7 zoGLSl+Fpr);l`OF!V)i$Y+yWXyk6_{9Anl>1H_afOY_n6o@qE1L!B3%i<|?h4?VsU zNKY+n0<2gvI)~|j?V*qMjQzpFb75d-wYnO>Lc1If$R>qK0AmRLvhA(c_SzmhnR}7( zAQ;&s&SgOB7=d*J$lX)Eo?3>pQR)9$`ZBFh}6-E2pYjH+b+Hx*j z9R`-|T0IQ;(W(dIR6ld8c9}#QMIgBQsp5;Coe!*8;dq_xE6dJsM<}t5B(kpkwkM+s zrrmBTiH4c3@Z;7KMu43JY?W$pEUbLZtW-0jl2J^yu)H1J)zy)PtS`<3X9B62vDP>- zX0f(~XiL%3&F{_KKpq~L0Z6q^!=q5}#ek(S)-Y@)TIkSM$~qvi0iRPKILHV4j;*q9 zom}V@dNzVFkW+;i%*zQoTY@20g^!>skHPUfLx*WH^n>4&V6*1E6<K86%Ygmv?37wf*H=4qR z>s5+`hCot(D5%I_)0^D^;wXV2_1L6zFa^hjtAg%#FFvf@KWw95PZ;thd?(!7%HDNC z^dbk!hpUK4Gu*7KgsXGTTLMnjMsB@&Kf%=wp*{`*W3=Yw0GTZeq(iO=t46{zRF``Ep6hMj^J6I`0(tkPm8Aw zxgT$;7y9KQxHdKnJ4c*HWGGzKt?&QI#<^OTm4-hV@@P$FXR9IP6$F`Con1HI@?Iu> z8AoV_7_a{JgqA30@7iDF+49Zbpn4ueWe%;wd)znJuKX(_&-|hB;JM23s+&AqW_QqA zCQEW_PH+zQg>HBn_!0uK>ScP+eB!V2B11tpO06qYgC8nFnoMte5D1y<$ZnjDk6)8# zUR7-0i(@R2k7%qcA^@V_$nYCf4m8);yBM-iTilw#@S+}&Q_7YzUV)t4Bg^2Uokq!< zx=-_HkOk&@7w%f0#y+i(Yr(ad;KAOZwOyHlP}CgE(3D7!h_DOks)2-H1sGa;uCXJ! z(O$nCANO;bQ*bX#NW62JdRg9gTl-&GNkpvLiIHN=F=zEqb)7Fo5an2*EXlB(0sohpSfi@xtZ{lZ!bC&;6SBL{If~W(eR-9L-tRHn zdG6IPc5j4ejJMq;3vb^r^i{*kz-9lG)%z5_O2kt*aeW48#pkr``4PD@#M1piJ6ZrS z`Uo!w1%i|cvOS77AuOrU%9n}P29%4vmjX>zRL(sxmUgwlb10K!0RoT1wob`Xuq~s} zq4}XBcMT%=&>Wvqo?twM$1&j64**N>nSy3{;R034?qVIgjuTM<{y>E}1wm0U;0N<0 zEVCQSQfe){i@9R9#Cc=_q->g|(>x&OGn^OXWn2=qQl|tLTd?c}X1WhxaMQ743`_CE z_$OR2aaGWa&61@{@G33HQdMOKNc5%FD6{87sObHf6*q zU|AjSJRe=fZRg&?y!-X-gg&{}t%o}PTq4&b2B-GQ9qTvf+{mvvp=ky%G2Z&9`^|Lh zVF()@1h2QZLsjw;gSbs-9=Jo&R^}DqH&Sz>r$)8IV)gT$o=OQx63eVW(hjwPgPUTs&#q|(*D!!8A=zG5)rf6`s&c+>O` zh`dbo?UAdw-Hp?&CfOP1CN<5vrX0R97x%IADLh#pF+FWheFP?-zfg4iAYKcQd57kC z9L%1XYWIhP@_QfOaA)ShR#DA4vrEt@?F!4hDJs< zj#ud{U<3{+aqg%!Rf1eimUM29N>@6HAN!05^V`DHqWQ2>qf{IxFg;|Qz`j8KhT)Dd z7Z4}5v?M}GQDID&F%**p;^jUQ`b#Zw5H~u15rXb)rFqx@Dj;1ScaOjt4t8{DG^fhpYY@l(^Qb1*vMiB-hxB{VLql3d~;i_m0P07t{?wpH* zOWx7#nO$NL6(5={I@S2h^pbEA8Az$~4;@iMY;xM$zWxfd9Rg&kJgpMWlHu{eB2*A& zITnu}X39L;Ke%DZW#n9<~_lBc6sa%kP3kKVHM(n<)f30iA zcmsR;hQTOdM7O-Zdqcp9LoCq$8CdM(sKbi4uc8wz=|S)~1aKJ-Gy+`<%K)wNnlT|ytJ?^uBzhCKB-sZF!%eRs*2`|4DuxBg9-c{wbW0-B@e<+ z7ToS%7F?KFBfY1`17MNs)}Z4gLq@-2CCC#+V@L2s*2G0efqAvVDWx^Db=EW64Y~qV z{#JrulalftNP)c0A0phKQBEN{3Xv3c7o?Rlm`)IO6-rmb&Y4lS*>vg00<3fHrKLuk z3<$C(-hA%H&7!r#OrVQUJ4oE|sjW_yYbmDaW~K^ujubxr?ZP$>HiHKU!01}gxoS!W z&X%-~@Yf21<9Ty+bmkwvA#_J`l??Vtxo!O_=9cCW^t8-;lTgK&cXK9G!o?h!T?I77 zmC5FDI@B6)mQ}Vf3VPpYa@oU|UR_h)0=C^mhyw)*j}nH%eEbQ!EOw(R6G~UQgTitb zRGo8wY<*d^iARaG0uUs0gAgwE^M){C01<_pY#-4U1&wFE<1VI%6BrXRs3a+Z`P|R3 zIj4t}23e>dLOn{jDGnDRy90^66V>>KR)#Jt*7-Gdl17@Y7!^wpwe;-6dkWpSlHGKr zW8)c*h0kBT%p5BUj@KG014HCXOI~EbUdH8qyjccA=7)1e<+pZ#k0}}X?;Z1-h$z<- zgNV{oEq!LBwxI8rK#ol8M9d*qH16Hpsz$Ra2e!vZ=Y6%5U>-cqSnrBqA8+nLwjXh* z;sSYe=!4%4razkNEAxzRV?G36KwL zN;Lb-jUMam`h9th2rvbsxagK<$ zRTBZ^~ErN-*m(|6{f`PEfk&k6z5q(TU24ex1!M)=h_cBJJjNKB=;JlN>N} za{s6yTQ58*0Cqe!DJ9xnQVCyKY*mJ%rZg-b-@^lvM)c540a;v!8)<19{ z0RP>x{Sw5^R4xYTlTxh@`x-mFz)~tMaIwI$2Yp3>P~f1i16a*>(?8F7dID><;!slv z;cLDkiDqZ8zB6o?01$YOfL&TNI43OaO~e!-3lxaGogds*0xX=(U{n>EOoYxW8SIGb zPFEh!6;5o)d>`~fZzy=A8It=VktK|mmo6W)Pj#vSJXrc za?J{Y%1HQ%y`uU_fu`X5iRQr$hKvEn_mm|-!AEyV^=AFDLWcK7_Z#%N6u{}YNaYg( z^JU0qU8FaU*6fwYy86oP)8aNf<^XD5Nv#CFZGap#W>DUYc8V|geyLorn%(pC4(5^i zVt89aYPW@cN95G|UCvh)mxZe}qcb!_)8iIj0PTQ;WmO&ZasPe7sty7G8y?osprx8f zCJ&=2V~{cx>3D)dd3q37FOThSO;P`}kQ-?1e2cG*BVl>&`^%z3ou^U`nKHv zBf;1MH9fyBR5}u@GqzZP=0>si@JptFC!F)|$>9JWElSOkyN;*-Kp%(0tu6}l5>qax zMZOuF(Z_JCmE&d&;uKZUta(HjpZ=vIA)?Cki!*^G@@^CZARIL zjxoT!mZ~{dm3P)vn&?E9PERlrsRtM5F1Y zj6lD^NV5y{Izw&;gTK&NK^R0qeUDB!OUHX6@IFHMWm6}xl@77=K$Qu1o^ zr|PnP?bLN;DXP2;^Gre1*aH`YpX?XH@&a7W9C_r4i=}>R*s8r*k{8}SIP$BR5#R%8X zPe5>rGvIp3X0-)=*%nKsZ{FLWDt^-rF=dz2G9Un#elAHCk;F2$ZsT!7`nJftCPo?O z!&?7%dvg0B>(&>HxEdq0J6wSEuyh97FNQ(;y8EGQSb&6X?J+CwXi7Iexb zg4%$!@l@Raa9dHAUtBQdeO7I<_n8=_GtKg38Me96Kl4(K2FY!>K)hPz;T4Pv61`{S z3Dn`($K$74R6p6(La-m;W4Iss3+kI0Dc5#Ay=1#vOXq>V)EYAF&5&EqF?Jsfe)5~R ztGA4sa7X=k-z&UtE#D5a8=ZrNC#EbPWFKwRO4OBOn`s4XWN2>NBMN{dsin<`88`HD z-s#x?l<@ua-9pT6FTyRce2}NB8j{lbnt8=F&B#!htw@>K!@0073$pA$YIHih*5Qep zQ(pH(ka|9#$uu+u#4pUOFYPdFg#*?3r6gxVkS}3*a@S){aj_i!}vsJPS zJ;XM`cZ!y}_aR)l-5J%bvRk0UPGW1KAu^j#)NbY;=W^gNANvunwmN5$aG3{O?fFdR z&bxX$23M}u4hf$+_RlI3dURl}(D+F_`ARp*vuhezp>u-!vxI%&U&}#b*|Stc3B;}$ z_!GL8rP!83hlAWo26P_R2xWB&OYqHSw8tG4ngLjMFUw`sa0tJ`QoF}7=icP^6aJ2t z=J4E7drKyj2%!WAE!Qci$v*9NNBvGw8+}tQ=d}~tjtea^A{|`5*4dV*=&(y#wA0jqYK_OwA9CR1L@Grr)BU>2 zlF<%~M{xhPh+tE35@`DANbz9UFg*$;D!6Ct23U><*f#B}$?oNWc zYj7*v-4hCTcX!tShj*?$&t7ZI{i%My=%Yrzdh1WS519Y6mi$kG`xbQLoNFo)uWIVy z3zq<_CYN$n|I+O-th!b7C~C&AK*4GR+Rxvozg+aZWdA6E0YxLqf#Qh7(-(-^MXW1S z-D%PU_N9QLW^_XqW!6rZ^317tEpi%4tP=c*mORjAtwFW`;J1-!j<_i&rJ5J7rR!9y zc^Xy@%kE&v+WnMcyk#4q%lb)@86BiT0G%{XLK+T*Wpsvn9uFCg^EnIFOW|l6qY^rv z&Hy|H$6U9;PM{Md%NAPp;uMARg@S-N)bg2>8nBRv+c)ig@zcPH#6_MkQb-8XlL=d>4~F5 zCNZt=%S3DL!KcmJgKd*@vp>b@Mvv%Z`S|!EJJ$Z{65hr$wjZC8^s@(-^RQhHpFU2z zyssiX^4wXmQq-X;9_$do)lkX#?>m``qY6hX9DBOWcC@x4sPstK;rDRdBr$){Jc$dT zqCftTF5&`=(oTJ_S514AiSYbZL7u$;9XZUM`khG4=Brpm$vd?a3XK|!P5rGRZ!uBZ zbLZ4_-|^N{+HUXk&$J_7gG1Ar9}5?eMA&5U)`Hwzklb_&&#x&cjt0H_RdnS55UlYR zQln4wBKh(_%QU(1gUv;`#UQ@X>{xndg`p-}Z6d(>}xC$>#ruxZg zTxZRdk*Gwnk?f=~L@CSUfIoDjLCP+CxLm_XQ=}>Q_LepkdeA3>*ZMttYJ(>ls@aeXIQxIzZ`Ux6pxEIlm#ZeY||aO*>PkqpWZjhoDq|i4X=6xOwm#>hMg1w zIn>C9ueeCA_%L-*bcCzzOQL%Cc&|B*7`Owd|qqWv@=Q?vCYY}-F8#n6Z>lm^>c z0zRjkld%nReDw{l-5jycy#^%5v>ZAzGyQO6GHw_J>{TQrz(jcqSn%>9 zNzwJ?pVH~Lb`78Q{mDqcoon6MxzuZ2Gu@kJS>z>xh=Iga!jvk5={BiSSE+`I_2A+63eJZMNp_JdrJzik z6c5mFSt$@Wl^{N)zW&~ohAV(Gr5IL3e?bp5w;8DH$Y+Cvd!R}1iL|ok{WIG-JUXGg zH$#@88a_;zsQ|u~fh8z`;QC`XN&2v^x@Xcr3%5l;r&1enV&&GJa>A92_A|CVHe1w~ zXX5$eAk!Q97e5li3XXn7nBc$^FjnXXE_q(cW^xhz%56|e5B+X~(2j{-k#;@5qkJAh zo?I9`nPIf|6w|*+b~iN=5h`4ffoCi-#%&bT=6#K^Kd-DrQ0&(h+Vv;eGL8g_I7J?xoR*U%g23s z@>$=}8JixdSG*ZKM(FGGtiZC`0d!q2B7*37Lh%f}1@QVlqdU9pnQ32U4DTosd7!-A zFZ&r*1)*V7=3zabz7qc3GvhSO+>Zkb!=mi|!KC+ieO>U|_x0XSKaM@jy$0jbAz13J z*XkFh*t~f26<0>WpFd!I%hK8Ko^JdzklW||gD;{}alm0H>xWC=z~4tBtOJ|zjkNyZ zTmL6nC|yij;UI%CsB!Bb2Yi&ztKj#m$UOb_!!3#Z_Lf34O=N+(USR*C^sdtL?_%Uv z0x21_klVw~+fXZ3wgpMnf(E4p;+})d+0*PzqNl7{SwpB)K2*|Er5Iv>vWIa2bl`|? z%6=%DQ)GeR3(CS5r~?ZSR`l^Q6l;>((oru;cSQBCG0bF~lMtwCxFJ>rNcWP{e2c); zH@37r*{ehYY9!jYc5u!{HX&$^77VkwIBGwr7?7Nqi@1O+*Y$+bil-tTY3rvmTN+k416?^(<1R4sq{_-$1D~ih?@SF|Fhw{2Inh- zWgfnohx0D4B5_(g5IO^3Jt><*YeTD>F40&nKx$I@KT^gXXT)iieS6cTR_3ew^{u9u zXv6WOu&wG2*M5qw7TB05uiu4cU{xj2gOSJPm!Y??k#5F?w_MR0dXB&h+c)4+Yg;!t zlJo_$(qJ4_g~;W3)8-g_=)`hTpW&A!&B=LOjsyiJ;iAuYmY2QVtP4B4|LmBFju#xO}RT5&T%s{K1osykk4|hj!BPheugkxBj4;(ZS=D`PM@! zJ~udvgZ(b<&Tnab{M)?VE2R6bl={^T(XIoKmgosKk~Fr7gV`Hdrn!KGm~7XQZ#DCI zap~zzrK!A-ikgi``vdPFXY%cmP0E%KV`r6OS7WFAp#9bh_t>uToc!Ybv9}pG`*B&$ z{cH&A_q$Bz1|;p*+nZjZ!Zx`09*<)(wbnUd`HiR(M5NWRW=pfHn=cykofdZCBJrr= z(^8f_Z6x(-vqG|j)3K{}vWRqw1o)g1{P=T(a2VdkgdIM~Qk)a~ z2oz4YEvL#)&}>6A;Q_(A{bYXv$?_3p3p+V;AGw}4korZYCI zwVAA$p*h@rP);uK?eV zQJTi~eAzR<`5K0m8-y0KtlH6v#i{Lw+}?ObxhPIz6R7i&0afIuy&45!cWL6aA-h(@5mvPKz*Br3yFJe7_4SkB`e|uu(%?@W?9^gV)9lCH zBW}Q*8=s=F>`l1LMPWr^jva!q4jv{Qbdcq`FB^sXII<{Bb$$DD&Psy?A{2Wp<&Aj z=rh1i>HymR;!L07!OJB({?tOsS&L~e->-<~{o z((}z!B#U2Ld~`z~L}hB2V^s|S)^Tb~YfMMA^~dRq*wxDU?<9{b7rA|7p{u}M1y;}btIjdq_LR85UIGgX){W)r(8(zu#sjB(9r+9LDk*_UP z+N_6)c&PH8Kd|!)u=)PYR*Yfu*B~=0Wy9G04!%j4k&{(imuouAcJ$ zGQd2Pv4VdO73H~F#p_cyQwqYYlwB1_Dac{R&ewO!W+@d+g?$qGGGX`$``9PI8PDaU z2pYmFho{45-jOu+0SnGyrXRX6gORSullfZhNRm{OjGsW#rlM-^EA7Q7CLW8X{NE`EIvv1!swfhu?`@>%xXO`g7E zlV*!|Q_rzfgzE|vS`ViUBM_`eIfKx48k(5RzpdUBGHRaZdS`|mm1vr(&YM4B&-rLn zPQO*20dzhsOrs0cYpJGQ@DQE-ZQ$WL?VYPMa*_@Txw$}9Z*ZA%=QKLB!-!1dTLhM% z)9|67+-*kF2?l3Xag*#C!UBD4P!r@4-RwL@eVSkI;4jkqV!A&zX;VLppc&VB81IBH zr2$6R@IlKpFjjAW*|c4!GcNKXIs?6Q{XT^sGFsQ^v{evL^wXkMd3`u-BZ?gFy#M<5 z;IHzf7+J_J(d~5c&~^M7z3uB7r~S>^zJ6DVO;iR+x`>?_UTV>Da&E^1dZurGwtgYB zx|$#>O{nQG^|i2c_OZ|Syctmq%E5lSU0OHg3yAmw|MK<)zgzz}_?10$BJ?9M zazJB98FIeG_x8Gax4Q_}vrUxT-z@zrWA#s*F z?H~&0`I@dwcut0qSRa9_N`+{n*OmqkjFU_?B?l@FuLr(hQLkzbyOo;$WjVO+ikbr1 z-KuHNRv2#-?9HY2w6o{PxUq7&&&C%?C=_WoCfm;p1~$gnCT5H1n?{gk{n)`uGr&q) zRm?iR5YAE$oqHtk#Jq0FM$#8erO!Q{lST6{m3>KS*;P%>J0B^?8#$9~L{aK5L;5_< z8z!(KRfSA3VPiSQn2E6Ry0nXglL;TCNSgn_>4uH=Qh!}ZijmB`N)hZq#kSz+1m;9y z&h|2bk(+TWx>wM@6Qdg4U`6xcf^xdMQL#}Ttc!upPob%cwka|0aO&c35!&lpb-&5t z^>|!S)hHO5fE247by`t0)s+bVjex6as9u%ZBV}*PSI6uv<7uZ>MLHRj8)45~%3+Of z85U3NKVO-wMw`~ZzumAemTVLFaP_(jpBlhakJBu_$nl1r4#+NswX=T5Q3+{Xe2=Y5 ze~=@C?J}V>y45%CM)Zdq0-Ij3xKK?yK@a={h+Te-mQL)<*4#j-(K! zH^=JiHDj%CuP{7}m-l|gIv;>Ab>UU5dMQ+#aUtOPIDQGuCX72xv#$Ns7Ww9Wp7yol z0pssWvEE=x)pHi#QcP;l)86~x@O^-tH`KY`zJ@ujd08Dl0j?~wzK3iiN>D`m4vU{$ zCdnD)M&b1pq1BQA)%+aV@R!z=U_>bP|C@s2X*Yg0KV?|31{ox&RQvjTq$2jh8RNfB zn(Krl?>YSW=+_h7#xNkzc57g$X;Z&O*?l)b!MP3G??hFopc^~Q%&^12e@uIu-wXnBp_-v#{zuQ-u-vt8MzN>yHed{?^Kus1ex++Q%5~D$iDnLadDYuX zKd>-cNIX=0+R!=nnL;&$h>~jan(_k%X*b)Cezk4eaL?3pz8MemGJVB`GM-C`JsUWO z?L^S$OgWJhd9<0G=`$Qb0291_&OzM`%D1^y%=EKP)eGI$Jl#gs~z) zhE*SHh*Gm$&M|ipanLr4+K|5^|4d&Xm+k4%;c4zkuGLNXD)Pi&sf7|+NqlQ*s|sjm z#8BthavyHS!DW4>9Ii22DuLV7$=bBzjHkl4r@A-u*?ybHB@c?laxT_uP`;BtW%Wdb z06K)2&2;CEP_by6M$4QCBX>;SBOZ>MRnSF+dK+P&PG7P~6z3)(-Pc5j2xVgvkovy! zd;Sp+5Y3<2$&vDkZUgh(?FlxlJnAn)tJ7(}Pe#||>_Wc>N1X=&QO5CGEwPG*#{@cR zHHT|n#eq4_Z;xFG&ZMUOZtv$U0=_Mb1(7BK%iE|iMzt@6Mmv0o(&F~G*<(Qyw5{Rx z9ZA7D%U2#!eWq+MPPA7A2@)Q0jN{Jgn!@Vug#y1yn%CYBWEX0uqR!NSLZa5o{ey_V zVp%`}a7^n_ckEVMC+3K#J1vvh`nrxZc7XAy9H~_2p+6MRu*ZpVA-8(e&pb7s>(D80OT}VsxpJ>V zlyv6j=2k}`W&+Fd{6YlU`|*>b2tmoiU$_OQ3Y5HD9|Z>@M&Yha&to+a_zy+(M|$CS z-7kgU^@5TVnI+jL6cJ(Re;^-NXS>FJHwt;NRU5V}3h#ma%BUWM-%j71XhVl^d|=m; z(PzH2Zt0A8V+8i;9jMnwwhwg%PuJ@AlFyLh<48jiGkO3+$%>+Knk#7jrejpSW7@6Gimt_D0|rfEjW&)C~{9WG?{`CA4V}Pc?^`Fc?6DQ$0ML?ma6H>=Pow zvP5X!IF0YAL3UfJo<$B|kkgoD$qN{zuQtegss+KRvsZ`&%fpIRF>xf<+Q_ENd7L}w z1&c6=+ddoR%?-*ugMK<_sS4^Rv9!x$B!=XR-YE={(*I=)&BrknBVUs8wO89g-=gAUX1L2bHpgY`mjhi zq^wX;G(BH)8&1sfRfW?*{R>PY1ln6_IB2g*F6CCDBetlRds$3nRDvIVr#)&DSFuZ}OAx2yEjEq{y{9*~*tt8=}iv|GwBlkKd*u?+g{%jJbD$$OB z4cmQBBxE{eV+DaE1Q$ho-wYgs&|u!i+Z3WR5EDhxk}GtP>p4fF#aCkJ$GgT*PcG*T ziP-R`SYf%|@5B}Vi*leE<*rB&j^IMQ6T7zcj6xjx zv{VOaB6t<&4{%y>$irlPNI^`J3Y^x#S>M4%{qq1xycyWX(phDCiFph>hbuR~YJ?J$ zB+06`QU@JPAN`&zxHN-VjZ_1qBXufFDYgOzqs`}Nuh#dzV$M(5$F66*uE>iVMZU7H zn@=o_=uemtiPJWAw?{11;=wh}E`i8sn9FAguO$7zqkF$`kF-Q7cIdQ^J#Y=ToiOkx zb*R3+$K%YpBcxfTqah3?ai)g;AA%$pQzQ$r#2KU$^fHj?3d1j>P^$P3Fx&?Ow;S6? zlDtG2#TK%`?e+xn5yH%L0K@X`7W}-ecjVj0s&>&?j`-@8-Wg$P6sfxxmb8@JLN6oG zoZ}Z-gb5K7iottvM`bo7@lQ5L?I^X zhvv``WEZH`=h68}(rm$;Ve-Hv0+g4-JN8rtXvq0Szfz+7YURDmcP)7&I{1jRwS=mV zsfaOyMgHXQUiUOpgQZ9k9PBG;{c&{YWwnHO+j)2L$)y%_?)4PrB?APf@|bSKL5JY+ zHFbE5um)}NL>wdAK5zVhhX;!-d8ilWGUne~J$>(gm-k49i{&+b9oBI9B)kfizuFaS z{=Ij!fPTzgbvP>9o=BqU&l%OBvaV4V`-e=VaLXmtR*o8z+3QVOu~z@`eUoyR>moLx z#<44FCwv2&Z(93+W#{^4mH>=)J_)zKM!!dF62+7rAu%- zB8Z9KbQHedB|SOlzwf%`F#e`p-KovGv6%Rj@$;>^UO(*6OEnahbA47XXRF+5t4dl~ z8%sfNShR=?A&4{@g`}Xks+7-LGkAbHD^otfl~9H)XR(<_ue54RVSil2vu?vpH4ODI zZ`=60I+1RY@)1T&0DTNi{@+-lzoa^OMi^@;o^4MH>jAp5#~4#_SCi|NHrgGt@X;?c zayc4W1YW^64%^?BL{hMuZWH7}j`?sKl18Z}7K2$_82z-`xIox`a*HOs30>a_tx^)_ zXiCtFrbDH)mh|><8?q3YsvyjXIL^G6qBa}@BB#Lv4Y>E|f0(SU*=%MP#?eC**f^~h zUW+Ohe0Oeu#b@L``Hg6mwv^|&e|yf;>wT6%Q}WN!Pl9%>ZyD`hn=&^b1*@O<6aT=T zs8LmW-twq;&)GAFhgQlebn7*uGrk`DeCYLBi+&CrKFFUfVR4XA-u`O$7GBhQwDK7b z{~ee<;j;0WL7*Eat9Ym}SoLh*{UuX9J;8J_cHQ;U1Ok667Vq(4^GdsmC1l*^)DZi; z3ufQv7L8tS?s#Q?ftekdDASp9?OH?I-RqOu+dOYZJ(Ka`5NE}}U6lu?fXg5;*Zpa+ zkxmQpV>(+2Zk5me6y?55whs@z@wzdqwQb;%JeM^a3Rg$;^Q_b#6;pWfyA z+snE;L~AV&@e=yx_jdXB!4eaS5e}?wmpTo#bi}^zL@-!;2hy1k<_*T?a4bh)t8J;i zuXf_KB4UfJ7|bAFjBSiBt0{*zE9zqsodk$yGQr zml1!|{DOS2k)aHCMz@mY0HbZx8>~`l{7#*`apC^uq>H(OK7>Q>$$R)#cm5XD#GD>v zyv%cX^WwG@x8-HObV=uM4=V^|MzOw6x z+l^)905l>UL*o94Rh#tx8Q@%_I!i|87uhNdMk}0i{!#E8_x2a7XkKWcsmtTpkp!y{ zMA&tti2ZuQ&(|tn%@;&7Qchp*Lop?tp-nC2tllmnYiedjHFe1D>E#utS0}vw)5+a} z5T@ zw)nT&pqPQg)xn=jOcjRaKRBG;uPo^k!Pxk>jS9Ds1bMSx+52nDNzf`)#@y-DZlu@I zQfgDhvBKVJL_jP*HOxRp^6G77O3fCo;5IbBt1>Xx-Nm>fqqef|Z54%aF)Y^$;sgxX z5}+(oQ;YvbC4?daL-M*?Y&$kxb=Bfu?GiKO-t?Gb|NO(ED*eNvI{bNlXHh#nEAeK$ zod>dslMg&CwOY*KHTW4k;9mwVQa4*2(hr+G3mHCX&S{s(c>fT zcGamwx4TD%9No;7$Zj7d)(K~fh6~kZ(tmoym&CRb+@AEJy%z^J@6*d8KUz+qf_BGexfHAEL=mi9)l`!-8&I5?o38v0P3T)QW~2IGfV)0?KUBU>9poH8Cx zsMB;i%xOP0X{43keV$Ds60wpEB3`e!;;8P^9G)>_=9Vulal_`Y>8@m224zVlja)ec z)CNT5vpO%48G3DS{g?S?j%-CUT27eiOsD8}<-DezTcx#V zywYDcSZ9;P8%`BU4H8&K2R?-7(ZuB)1#`PtD&P2povxldcfq@{WU#&uJ<3XV)m7o5 zulsTk`wYeXlP_*iq`XN({ScUGZq!6t)$XlmU+V^fCwy2IT6L; z|7SHQr*D$I3{QXIa?qLlTD{M1%X$dy7~lI){6qD7Zo=!G4_E7 zMDWMt;1Z8-KdJe2$`A)8$QXmmU46Nlen-_LPm(ut6Y@vY=+_To*Frj?EprWlAi#-R zLe5WMz8aSgy!wHaghF3z_aariRRoT3A9wkp;mjrTLBl7-0;a{X0=w6e@8h)E%3XSGYq=NAP!Ww5P6RfE zPlfIq;_%Kw4~I>OPf@Q8`THN&*&8xED@~IM@#3(hwnHD&#bmzx;6{^Q^dYU}SccYu z9Br>-J?TCtrw>6)*C{X8VKx6{niYxAvk>|oE3phI=f5gG|Nj9(x5w}Y;Z9hhqe7YQ z>lHq9AxiajT}-N-))?yo_iYzf9R^I(-owy~P1z%oyfTPxNBsz=@ET$YZ=^iCAn8%M zw!(wzLK#p(X^SfQMDv;KNBL-QTs5=Nhp<&jW}adNWmu%c8QE1MgOp-|(8I~8D5JiR z>99O^ZJ|DLAa#UPc{J(SSVX#q~<6Ca=pX$6j&+6);)6I zi11pc6eEE$q1@9Xd-9?JfbesY#`1Zoc9tdrd~U8Yuq3N9_ZYHN!jQ7Z9fg|ZIIIq+ zMu}Ca*QRAqU*)bs1uIX$qV4VT7KRra5cWK@3+7}*{Oo2CSuOWa z?fMI2cIs7}0;iy!i_D~fOp0@V_L*q?UVtXuZ7&T{ZyhpSbL-&k1Bp~0{f|7o%w^~N z$Kct>+3Ak4qyC0nl|}J;l?syLI7Ui_`%a>@@1#VZL4pbKa7mxk4}OPE1U7!;a>Jdk zo`|hH51V4$McpR?H1ynJZ(J4R!8gu+scp)IxX&&0PD(X}T|ko(3rsNuijeDz>uduW zEO7nReefbO;6K#@w~kV0i88IzHDhvRS!|ueCd{zYd8ui^QG>He>M81|eG&bp;c8W= zV?}R63vESm?6ff!Y|<~Ib#I*Lth?Vv-bH0`m%nA5vat8xhAXkJDPi9kmS@+Y z{l%tub4qJ1tj0khr-nf}mP&ro*JsfizPdH7x&T6to5!5Kp(j?VK1)zCp2?V0^M8!x zF|I3eLaz-chd(w7d2JNM=jGG#2ciTjZBC$Ce2+#x+D_ohK-AAmDMH^dsRMiujQO=& zg&Rg2lpWKlyfo*G(g@2In8q6quE*H-K{~Us{`WyT2tn3!2f``uJZO=6RfUJ*sT(_d zaet#P=ZXGXpI?t8QwwUx>gn|oljiiMa9-bdjdpXJR@~x&Vih9}*iOXmN+8Uc;#WFr z;eI}sPCV0by@F|jCThdAN*SCn9J>fn8HMe|CLcS1(S3r=vo^x&%p%TbQCw+pRPE%pqB~)NDw}4smQo7eE#fiP{5tm!se?8wBJxW~MvpO4}d86?)ilrei_|)Pp&q#@>jhI|@P{U((**wRe+{vvZ2nejM-Mx_=KFhe$;xxxqp>nWOhpAUv}|26 zULdo^@aOzzf{x`pIZ8aF5OG@IzuGtviGFe_x0W_C{=*F&+dlK_diB>dZ=$E<*Hxjz zw zhz>{&ihTgPai!DK)8wb!;*8fgz+d>0Be@WD1chKYBbbs26QDN7mT$)5o2d}uA(Yw0 z4AAE*I;Y@%6ED}|I(V#vC6+%fcp!09jsgkolN9p?Y{?+B3DDRGia|EDWKGy34dBBk zACoG~o0$zPh#VycMbSx`>+g;si?L^e=eb^tASU1%gdSYj0v4f=frAmFBZiw_+kvoZ zDXCnOAe(coETet<^v_n$5!YT4{`jzgCGU+I8@5;9tKtBu;sxu&?fo(Z8QITQstW7r zcbjWwnEJ#uIu-odFF-BU{>7da+-E|^PAix9w%) zA*KD>Ti)L~o?nmI1&#*u6Ql^dG8YvM#?d4Oq>(uDSPQ4ytoM!ewwQH(5LJj~&tgRV z{r}{ld(mLLiGA%6-aE1*r`1kFOY;I{-Tqfyr&V1e*cXN0kxjtb{RL06U(9-*rrygHD={fu=%Dls`;VryxUFC z3B=g83Am}^61$0r8Mo>(7Y~EjJ+UP-mW~^?zPN*VX38TT4>M5SVJMt6zdbtUo0$?~ z8djdw`>kX0)!rExT2`HU`uJHgURn8H)OgP-XC7S7?e_*5W6(t~UQY`~F$V7}C1S6Y z&_0^L8PvHz#VR~+CSb7Ng=FtZchsK*A0t0VYlW1M2#Tv5mXH3!fLvARPv%x03yFZJ zjMUE2Z8+&?tsrLzjKuIDMdJ<&&%mMSVhJAq>Bb*&lv>_{qvlXyw8L)8MOkGtFxpjw z6qRKLm;X-U_+JwqA}!m5>}gYx-H0Xsd4oFb<+No4;3=14-!G45CQXT^5*@kr5{6XZ zNQb{keY3!-Uw9Se8JdoeoC3m8Q=q&WJ(N4i+Gx|#+y(lnb+;` zd3v_RUTt8DHS^F;;}I-Vj}wlYDwA6)b=AY!q)q-LN)UHu(g^*+&#giScwmv;d!KVF z$g-q83C1&Da&)fH7SA?VDT$+PBB^0B!7lP~c6Q0Hqo7Oenwv`6PyFv#Gm1Hq$ccRE z2rRRV7F-M{AuE%Xg5e}Z4xVang8O{-3VW_%)nBDiu2yU`+e}v>Y;W2|8+Qkeh~O9O zmyh*s^^-qK*y1H3MmwEs5~8Lm6De7AOn%oP5qtfmslUC+S57RhYje_l7N_H>zh@Nn znoBTinJ*0emAK5clV+wj_RpGKI41M_{$O-NXco# zH~)&Zher?(u>5M)WIcE`0)VvRIFnlnHvQ$xelMg|7$SB6?XcW|Cx;3!rI?IKRN)`Q zBSXHv+S`0rsFHA5FY$KZVLwn<@nEq(P7;Tl`ew3Y7FTs*z_BH;jCJvv5<@VoqDun}9I6i+2p&JR#Qf^hT?IPE< z3Lq*0xE&wPTG>#prm$$?jDBX}sZY11b74uK*L)tUl>IaG=Bp?t9An@C4r!Ka?rn$N5hOrh0X! zloP)=NM+2r$hWl}KejZ-tgx18nQMP*vFNx`n-Ma)(DgWKH;m=?^~hf~qv92^Raara zff5^g@4epCDh|&LHDWTa?hnEQ#-0FIp;&PBXX<3I+qqkNOo)x3avuqJo9hN;jlYH= zR7WgLLzCkYwzx*TR>EVf0C-s+S|69)O{=3xKUkm-um6ESuTg(>f%COkWObhn4VEi;V^V2Y<||hXry5Ms z$^0%ZWRfba?s=f?i7l(liS@;=CF`qNUZe!*fP;#r(p{@pLyN3n!P(;>%)w}T zA#Hnv-~huq{W{wR{M31gE zEWdm6HI4?#G!BEZb9V%OL1Z@0h?p}H0DXS+kKqC)aBC$5Tghh&4ssx5H4HK8uFWrp zd?GumD#wj!jvsv>XlNmR5Cw4tH9k9HOb!IO zoi!&FeF^yOqh5!w!3s=3rgU%6oT>=SRx+m zK+ZyUL=y>QPhriQCIr7C z{mIY(v;f78i{QYdu5U8!3Y7s!vCXDOlRZ@UJpi7LKeT*q6B=yDsF5<41msuAF{4mg zEi@dw$E6I~82E-G9A6FTN1j^lS6i$7XKk#aZJOmFfu6-S6zO4FDe}*i9uD@W#(pC%hiB-+JMN?H36w?)V3A-Ql=a-s08k$v@}cig#x}zU zsaobG6L;nSSV@K#naq)%Oan_fA=SpvmUkv~v$k5{TiC}z6nHIEor5%KF#CT6($BQSH`mi1^Lus4$q zG{ji}m2;vkj-lBH)qf~C)AAd~lrYG3!X&S@P8a`?|DY`?WP4BuwV4t!!9TA~ zsq;ks6_|z=2fv3e?8mW@CC7og~p+tI?hA|NaIq(ua6ZM@GKePaUvc=S$Jo z0uG!@tvqqR+asR!O;N!b82f8(0T}>nIDN}%3w+iD9F`C4q%B~vG|Y0HOiU92_ioxW zK9uPXCcOD0v~Dh}Ps~H3c#A{Rv&eDh4kJ2=Nvye|lE`hh1KN)gU71Qr z5s#s%4=N*t1AJ@WlU*KW@|hdm&J8Tq6{T6J7?2}O*35zd;B9k#NV=?<3^SK(hi0bk8$lX^SRD9 zG;ixA;Hk@#CQ!tA;9gHec|j3un88yQX$&BA7leqD`CIsA2oInfXtQ~1kN!kHG{8e1 zg${vJfv;!Y4t41X0}yH|AUjjjhvauj*!8n%7g;Oe7DJfD@j-poi|kRvx>@{!X>59v z*efw3_%2hmgAM@!8M-jcq)4i{0C?oO7@P-*D>Rk>KE7fWTY2qKWPn?$bjsx3<(c8U zDvt9v9qvX%IkF#1BlW0*>WM574Qa~BTu`Qf_&>{8PX;%fPieTwmP2RQ7@Yh~w%WYU zJ>*QLMUh-Kbebs~P}p&e;ylVZZ0~;#=mQSVaay`Mj2XC=fCig$TR|f12J5ti;bfAUhsi7TlFl_?#^eN<3z50KrSrI^icg#3A$J|9Yo(6J>2}r$jm)H2 zp<}K6jZUP4Vf1+TLban~KMi^rwi&YRL{L92@yeFe&}>?J`Atp)&pdoyMACM9BY752c)8(lDQvPz%rR_ z>#JR#nl*XFwAV)d(7~{-ct>_nm_#KHH1I|+-Z|Vl4i6LMZbNs*qT-ixx5l#IbXAd; zcYXcxX^waouaacYForJJN|4E$C*kpIj+@ZUx5S6Xv%2Jr{_oUQr*)5uIB#TymMNsn zA#5?SE@D~Ie<2G~goz=pkr>w7mKUjJEY1^oQ7}HmXY%K9LY!ld-10|agYOaJ!Oy zIsfiE1BP{&K{YzN^5s6coULTR* zBcw2JB20C0ah!rIHa<3oKK_#LL*NaP;@DSN3M$FU-i&7lq{_Pu`uY|Tqne}4DW{Y( z-@tVL)@7g7If4 z#BxQG(paI<82-u05LJYIDXH{4j$%iI{4BWJK2I?`1Q|A;^31@NX3T5YLJfrzQ(O7- z3jHEXOE!rPgf?_@omRPn8gX8CA!DMDw<{cp+o0>+wwIt5J2-!|6Si&rPq`u8M%cfn z1?93d!w{bWrI}ASp@NuCSQaZbLVB2ZOOI!%_jA2($w^LY*#mTfM*+x1eOjbW5{(EC2Zo+^ZD zykcNDFIqQRZ9+YmW(Ok>^_e$z_$%$KdlJ_>_(=GqUq_(&03O(mNWU)IN5Ib$+Q=^H z&m6E({!TO%tlw6@HATlURkzS-(il^XEO~5KSy#g{o&wguIp2>B^@?k3Hw)^QsY|9q z!IIdNRPJmaSl(=2$J`Fc_m*As@*0BG!R5-*b7&4JKnOBqZC{hS0(DA*WNSJ^VjmpF zc~TAVbs9)v31*~!f06WbuYhzx?L3G`f2MNB#Opnx2An5*@z}NXSr+V+f>>WS&)smf z*#Zh%hVfDzQ1tzoM#P0*QLo)F#?||=YB^2U)S*JZ%^iNBG@sc-$q?=CF>d^}Il302 z(@8nOd_@!GMTP;3tn!FkkS0l5zfL-Gb#Z$HOXw*b#5`;%k$o#Q^lE{MFEVz6>bLQ4j^hgbL zy&q&n`e$qgdcbD7UGgs&AA2mg9Ji!?*6-23Tx$!pKVI!~KcCdf=TbT1s`$EJZ7j4^ z!6x4eEBL~R!+i(i?b@${#>Dr3UVvwxABE+10-?n1XjC~jwtSuK(k9$t9<>YDFymX+ zs?6Nn0AKl^FxyRZ4tD2x!VqHh!k%Ef3h{2F^zLZ{yVia)e>S$v8ZK(%a_)&mup30e z!e2nDb!NS&1~o!nVK~i;<*0|v#Ks|in)4RVPmc08;sX5z6uzs5ycr0^xoFY_rkV9v zI;HK@uo1WNfV<3>Ke*9#s(WhQs8*T`r1s+if2K1F(9IDL?A(rhz?$s1SG4sl^gf|} zDFDeXB@HlGa61ndEpmKujb+#u1^PmtYuq@5A!D=DW4J>%1Jdt^J*>qXgv5ds@-t=I z%-Wr`{AJiy*^av5yc_vLJMmANZfkd-d-E(v8awtRQ+_U@IX_WaS=RGn*^jwDvR&KG z$dzOh`i-_mJ`sCbI2MqpUaL#Fo+20;JWVVoP$Y|YZ6X;OSpGIv{%tblOXhK6+(0?G zn#F>bao1ONAwU884koi{C*p70VuVk|P3{gz8q~010AkW6>*eabW^7vP*f}A^b zlsSSCQPl&xFO>p8CbazgFCMVfVI4 zK4$w1X%f!wi4E@d+EPWyVgnCn;hP ze`1ba(K6^erqW%Fzs!g?qN5xl;!@!MN7h$%#SwLDHZ(Lr8VSKQkN^RKTW|}m!QI{6 zp>cu}+}(m(V65Zv8ic<-!PGjs2k`TB=Xg)x77E+kwlrqZ;_(TdK+Gb^HP^Ng~5CIfvrOH=Ihhz zJJvMy`H1S{a@R&SWojk{nS>if*esG{H(ijj#Bt-9yqMByPeeU;!0Q@phFYVyV}ULObG$%yv|Z1%-jO3;lzN?%>@cDIqGO1YyYCQ+Gia`Rk#Iok*q>#^5L=ER zISd{5+-3}*__>?Vzm(q2-z~(8@m=q_3J}>v0rP)s2tBEGikn0^KS~q={3&}(VQfg9M+d2PC8gq_JS)G5brb8o z2EtpG-LnlK)iE++nx^WFUOMHrC9?(X@(eLF|H!qhv&PUC`*Ly}4lUZ2%pJ4bz#gip zS{77%%(hS_z$APAl~sQ5$n=VT*yXI|HUA)JxVv7;IgZm>RTN~`G2)=M0MOeT_n!~@<5B?Ghr5~OV+CY@} z9S^w>L^we+({p1OX+4M%B0@BP!5~u%_$5#?w~{{{apk#zBJ4{m|N%w*3A|& zijF(KN%TVd!J_iY*gIgB(yH>)lubfe+TfhXT>K<7k32(7%D4rBwO35f#J%(R`XhWU z7?^#MaSSEihn`fQ%w53TJ&OwihbNE~1L*eFl~M^sZO7Cyf4Vq*h*!rP zPjfxJirLJ^#rv+ORC6)1c>Y|MNN}}3w4+)(4a0FnlPRF$g`$*bkwjX4=6Jn31uG zHt<6Q;og~L39smi=6?5WQt97HTq=y|C`VTyKb(_ILcC7UV+xBmO+iHc;^vR$T~96Y z4)Oj+#RorCvM(8~ z#hJ^?g_yzJ6dr9gYS6=qSL+3gf8xQDcaE>4?V9bzi?|13k|f8ERcC51)UIxIOFmqH z>9#%d%G(-r5!K(xo&jwXDproM(o)LJw)>M$+1Kj?7ZaCFDHe$X$g(MQOw`E_94A+q zy(IL{uyk#=sI*Dze9WXVyU4pv+a@|Mq;%~S{F}1;H5`J+i^#+SBjt`$?4y+~FNob( z8!qh6Sj)$D=un-cUe`w7Jm)m)99zKfjxmGMZVL~tzgJ~p&MPhijIGL~#)avbqB}y& z!2-;EHHVm`v3#PnG;sqK#=$|&Xq+kKe(}E=N0w#BK1KAP!@v;`fEneBC&@5v> zG)uLS=aysKQ`6ij436V7#W0P=-VwdTFf^sZI`j1(=o920t_3{ez*Epkv3cVNK_^T9 z-*E=a*4y48Sga?|<=mIwrZrQ5<#b>A}xN~1@c?35qpv$b7r=~U_Q!s_vCU*St&UT zSR_zK5=?%t)+DsV(z@A<+szk!g%~-ih3Y+45tb!%(&;zHu)z<;uk3tGzdKK9ms00q2CgJisOI3Dmp1d08m>^oQCa1w@^Y48 zdE}7c43)V(xlRdEI%w*3!X+*Oo9<0+^JCi%khlu#5wvi7vG^Kc=og~0A$~D) z4Liwu)4~NPBmw08IRiRlEBRHUQM=xm7(1dJ>QPq;-CBY2ck-lubXLwv$==!hF;u7`Uj|~T_lJm@@Yb4 zU%)EwgL8S1&R-KGaPz0~NUT{VMw8-yJBHZ*_3DO8CZ6_CHf-j#{rY=85>~xM;*#L? zFG(4PV)y-?WnLq0FhIaY;L3hYxhf0)q1=_W9Y1=>TcBp9y%z}cY}dGV>?;N}#sQw| z`>`X7iT{`$r9ttUiVq66l|5?Au&3}ifbC_w!nOaF%3otNw=|vT3PD^m~=AINerp~E~7r%np_Z4nX9x|Hf6es zG4O9jq>p-Gud>t zx~_vKu^L;*bYp-7l;EE0L}CbzpK^p;>yLmEhWFMnfS9G_8C{;uwaheGB?vX0cb zc3_909O+hfJ|l1iw>QpKl{X8d6VJp5o+pogLdI%nGN)pcXNLW4dz7Xy1}qiB#J(>e ziMG7{Y&fkRW{f!yV8iOoVw0o8lBbQA%aN^870mx7(yA@LF4=eEUKUQhYJmk6ek|Z< z;l%n1LuuiEeY0yfT~YDVI#xx0JdwU@e;*EJ5I0bbFIKvE0J-e|k~;VJuSQnBhGE?&yXyO&d#<-& ztoWpCw-rqBLl8HU>P_NAKYYNm(_M97OxjhJ78_leSW1&cm?!&y91z%1Zx!rVn#L{w z1+IvbP^G@NWSlcY97DWV7Q|E!#$3f2oDN%-cD4sh2p1~V1XP}#D=fTDVB4ukKf(Xi zJm{%1HO9rj0EHN1{_J_7(lze0fM3ah5OPi+>w-!Sha6~HPvU-?#uTJy;=PcB7&mY& zQ99Ur!g7(g1DIsjd}(YR(Lj5&e#7hEzW$I=fTz{02H+hMaKe#bspX<&N|)1L-s&mz za!w`3agx!~l_JsKyLNpp{eqr4jJ2~iKw zs~N`{sc}-OBNb8Jq5RZ}<$A4Cx(Hj^o-KN61gQ`@drj+>a_YF-drc4-(J95WKzE#@Zf9lG;s)eyw_T zjtyTA)#o+sTHL;Ukl}od7x>S;!oHoen`|`Mh`w9#J3ZZR8=U$}BAMLP1@C{q=)xDw zhlZ;E+VwE5me`n<-nWCl0ojLP|F_EuPzQ{p%!OJ2_um0=&4_XV^l%2~lLoF?FeKt! z86KfyM6=g&A6#XDIyVmI-X%r}NZDQ7;oV?im%ve83;;kG(HITud&8HE2m#Ux>;;|9 zMV4iph(n4ZvNV-9DQ3a^`52;2*AIGHMtH{_X1l{E20e8!@yPdqL!?{kii8R#gjBzQ z3~Z#p9AB|d4S!HQv>uPo>^{arhx@2fmk-|(gkgr1Z#!d*<9Oc-FLFBU-oG1B7Y4;? z@6V9lFr50APrI>NhyTjA;R^!=VXi}L>*burzhbLlD4Bt~2UfpBRs zp1z}fvZJLcmNrA|k4SM?J}o!iulX{Y_~_%Xw%ZppiKBH!LX>**ai!lp6b@LZ6)Ca) z7IykJiW%7tMXXs$kz;y$%S6gf)p7Pw^hHx&=W>xRad|wsQBCh?2o~)n6w{{x1gy01|mFcP@C{$XJxe-2a zd||5qmJN@iAol?)I&jCDBz54npC9k)vTVSy1a?7QDqS#%xtt#Bc=42XTJ!faKg5G z@^<6~3Nr(@5eFmGqFpa9`b`7QdYdFca%F7BpN~`iu$s_k3S=A|_!lXfWlF5Mn0tP$yK$E@E}5f8+CTa33LvMj!k5;#H6SXW zp%_7y(nOQvG2K6-*k@Lwx{2z%6p*;fh@sQn^pA|zST?LfvBm7US!|3hnH8I^U6yrE zQL5>Tv26&9XbA*;!Q>!ts}v{(jTCrj;Xb_6HCJ5zj#AAd_Mez>CdGcl;sxf@bL`UZ zaGXw-i+1z&%&em9PD$z9Y%@5NE||Ys%{XNtmvurOxKKM5PFU&6OUlUmP%$v+P%B|i zfUJpLCw@H~=_mW9C2yhuXFbZ3Tk5nHAL>W#oJ9}9YsOKd8aS#*Yt)nZ=Qo@=+~00& zufXlS!hw|eqJsHT1) zlW<+w3KT|Ha~B`3-ILfAi@+bc{?kbcoyArfXRbnsRt83dZs)e4Z!o~b59a-x^&RYJ zOrixQ!P91=X$y8ptD+QU(xEV*AS;5hMr_wXw@4UE$SggAhex0grjz)-mkwTswtPB1 zOkDhqaZnw@uo8ha#72u(;BhL6%iK>K{Hh0W$(52K&?8f3{{F*(j*4(okRDFoT|QPu zmV-b4ChrIK`f%D88MtK-j zDr62)6W?H|j%7=rqQcphoRO}4oQ2-p)S{d&tf$M>avvsnfyOBttU zS6Fg^BYhTzGd`i%xXMf+VzQZri4F~8Bnrv`n*)67YkG=XOZJ$J9B7Wm;Y_wn=&&Y( z$Ri+Vij}Q`T~;!*c2r7ZKG-Xv)=hT#Xq`EV2!#k?|F6nZGtpdytlk<9r~{g!M95)R zCr;oRnsm-2g0Lh-ANkekB_XnQ%hDF6UO8(Lo;G)i{Ui)F%qf_ zj#3z=Dcu6>+g(}3=8iuY1H@GY%Q08KOU=4z>vd`*PB}Rq{^3dUwI2IN?gw zWEIe5cNRlO0e!naCKXYeu6l@F8&HGGlE8k=Vty5AZKTS8JQ1gB9RXRDS%#7F~BVQ>mmAt?U)NAW2@1 z?XjVQWn8SKJUNF6p8;{T{fIDd%wm~kHU}m<%?^B}IwyjkQSUuzcolk&=JA+dQJjzt?pxlvY^bMowg9U?sl9)Cs15!)zcdeXS4{sD^_zjuqlPY1!lS$RCt*VJ8! zwKEfeVZszn;UAXJ@To_j_mD#ZyJO1EXA)^Do#7rScE1v}lGf*d~;7 zFG3qESs-bM=0c>hKxu`jbo%ci+-7^S%O`*+d(Kqmq{D{GSzX>r>HcYYa{WgmSmo7v z{b)c>lUnXp(#gQw&J;l*C3RbJn^0KC$O7WX$NO&Wg#Y_87 z_w)thLL!6!|FarcCW|^I?9yf|w!^=h<$TXL2Wz}_5%TYP$4kpFek*f02j~(}vkmWi zu&oW%nFDBKaj}X6h!=i~V=2|ZE#u}gz$7;n5h%AnGs>TaH?ToX?Php)34zR%d9!$ zHMwb{p~>IXYuyMkdug5J-^G_x7hmbvtClBohp<%CA%`A2YRv}HV5 zM-(y->|HsQtv;u8r5sWvQjh0=CktkeuoP*)b$;uzVCl<-bM1gdDf9TM+%hpga5$H5O!b5XZe;2XjZY4u|s#xSdvQi~KYTedX z&tny?UY~T*4?!DPwl1*>*=0sJO|f(wZC7VebEdE&E$CXyIYL5~hazQ`l?eIGu?m&a zVVIvJDQ{MgXkor+2NfX9GLRB1n$6^);4v!8Erl5_~GpSIo^WU8?RGRk^kvIAF^pOpb*N^Uoi-fyfC z+9eZ38***!kWk5`IlQwGGSA7JcwDUk3A&Ex381CHQR|>R>&b_9z%VjkIlH5>3c&(YGL3&qoW6)3S=rM|P=A1)T?cXqry%7w`-U>S^oy0KLW&en;HO8Y(nm z4~x8uWxLjUryE<|&M<-)te<+$G3#q}qwd?&(p5q_Z9A=cMP!ne{ZC+Qn z$J+l9Tej?I9t-Xa>*0NIzWLq}+U)atTjo(;u5EUR>Jr$zJ#FIsk>FZ>Iomm79C=0b zWEbiYWH+3DkWi1f4|G<%}ZVxvzE?Qq7P#(p)7x zDAz>22AXkWk2n|SvArk(bO8I{eKN1m$_be{PRi@OH$=}v6U3mYxAtuYpgUV zJut$5{r6Y4YWn0R#x=@yZuGaJ*85Mf2h0ee#YN(dE+!}xA<3cmjVlUz1%95fqnwOb z2ACO=WkS>;D)nKC|A7E{%uDeIxK)VJYMq>#Z>455D2?W*Z7lx2j77>d^_<2(gN&}+9ke>_qVpiUIcpe|~ZDBY)?VdN2e=kxcuPejs_wk9vKJnPn8VcbtYobCTY zT`!VUL{xRgVfw38a@`?CQ8n|4LDEcdKueo0(e8H9WP3sgP3b=+VS(?>+}W&QzpXs!?tP72%WY$Bc)34t`YPSU__!P@E>+f$|R}Xafh6<`s$8Uyz zNs#yMrZ$#CXwOjWCTS8p^l<2pl|=bP4o(ilw}1C%ns;lDgEimnr=f&M54D_EO++3V zixW&X7A-JCgj2@!8PiRJ!$O?ak(ieDmSw z961FCCLT;x8<~ThM1O**cl7<%2;~A5n2a)8FR7x0t3%fJjpeXu7?rl27{c z=|An6Ix4eS?LV)4({v^t7M+IbU9wiN=|-S*769_3_ly#TOt}PquxS!{*rf8i7PBSs z7hR%S{tweKD^p#4%?&4ylQ6{{nHASzq5ZS(!lrKJfhWSo<3V*5Yb`u&)cIui|J;rF zrxDdPXK$MyyY@pnl=FA*rk%Uy6mnhENvx&0tP+z6FGLbhyEvDjhW--4EVtwKiMf96ix`B`XNk2@0gj zy{^@G_YvS&9fn9$Xc1_-8y~4RPsubpt%glu=5zh!Npk1BHtx~MEmh8TSFPlS~ zM{Lt(<$FX;F%Ns2dfd#XsjGt{mi%teJpb3SD6`mfG#GZ$XbdwXJgBIuofO8Vr?_xl zp(DQgiGWmMl$&f~D$N>7>LzxxrT(G0NLy}4%me$Ox+2e#(IFBVORpa-$Lebyxz*9VdLdSD2dYiZCF@|=GK(6IM>bx(MNfANL|sJhMxXYds#Di zwAy!>tVUhr=k@xwphPq_2F22{M&DTChIe&QM{4BTCz-bm0bI2zlu>7XtC~=(ZL>7j zz8@UMnWI&nuCOJvRcx-OE&p4!@wtsE(?X9cMy*=Us+8S5MqT-%>%3dyX4f52z)$OL#wm#ZaH`FH zbC!CpYs4G*!;wU>U-aIRwiH7=e=@OnE5*X5lg)!L-~`8*QQNww9yUhszir+#Te&7P zLBgDgbRK&?*3;NP`Bn0k=R;oSf4?``T~*lpR5`QUPybA|RUh7P218Q37be4O zD~+Z(1cDhBN$>dTb=cF!HlUzbG7I-+#pZcl>yX%@Pm_ zu?SzxfQCe4w321hu=%$g^byr8FwKZfLXf$EScWST-zOP>1Oi$Ov_pM{6^9`dHWb#H zqJqfQxWB3*`NAmd9zqb_4V}~A3@f+ZPAqlNx zZ5OLaOZgLUS*1Fmqh>Jyvy#Gd5N{QmK>M*7rleF-=>>G)*W-qu2nzmby)X1l=2W_? zalA<=u5GSwj7G&~Ls0(Oe)+pQOX(wljv7;Q7;GY?1vRrb&me>#f z6RdJhrXdt;hIs60{KP%wlesu$+9W(aR8^4!EBJwtYq%WW#L7@d z8>(0NW!SLBCj}~6qFpDv7~%=F){qc}vaUrvF1X}RbuOo*r!Oy9uBP^MYt9qpN%%xs z4$C#?hnnGAp_wa?#A7eqxijk=!kx$5mbn5E29uK2s;Av6yMuTY@@3{C<(_w&|Cg!8 z(%u^p&fU8Ynl6L&7u`qJqU$Z4W1J(*E6y`NcJe`}KmOU$Qet22ic&BxqzC$s*axLg zFHg7q=Nf!zo!z1W)q4_Y*DrEE?^E?o`kUvu_)yY}-q2Er!TKO#4`Bc`Bpd`_q? zR+UsMQU2Mg4t)6vQHzITGHO-dJad$0Od(#l7C@~@nL=>YcOUj6wZbdkhj>i`MxRY9 z3-AOnC-Aal+#&No6=z(cF*~g!v%~c^RJ}Eqefa~^)RA`17PKB#Qm_{DHf~e88>@FJ z=~TFZ-+FXK((NxYOTbOkbAahoA}6L z`i8ZO9`64JYlN&A@AToj6_v2NXp^4TThMnJ=p#lh_=tu_g>$J4{}phk4g#Wb;m0Fb z_ei^-=vC665$c-sznia=b>>dpE&S}J(|(3knac3t*SVX>j-f7{S&Fr?gr0(R{Jo%B z0hv0_LU~C3A)K0nSP4-kmdk5(?{TE9Hr)R{#F0D$tzXa1sSn~WFyK~z!4H**34(3k zdC-_@q>u=!Ei}(_zC4xyDEx=j>&PtPkt(cl;w64p44UVY9oB`~r(>QQf)pjDo#?#g zXf310!lI{3b3X!0lK|o{VsF4F&cc(%Qv%E2N7IB^v_qimAF14TuXU{e84H3o|#m8L(_?Z z4SD}JF~kR2)QB~*mdYU1+7Ytf?JC-Rkm8l!G}*g5(y7~^m57WFUX<+d>q z;YyX`dcgwRb_CvDPfRGN|Iz`}be5F2yvts(H^$hOVLfWqHEo+*Q`i0F#k5b=%bUv!9%6L;06aT161pbW4+%WR>xf&+(}g6xjH%AjjO zlC|>s2~mSRt7{Ep0mW7y!nW2Ye%{|+=uKc(i)z+`wuMhJc)?OMt2^8K5g+=4Pu(RD z!tPVc6Qnf-TL8m9_b{6^f?|^QnGhoFlc?-!wzr5u@sX+-QUkvJt~`%IztvHnjH8fA zXYsPNX*w!zN9OM~GhWGp;T4*@&r{8JF~NVkLx@a}BIZM>-B*(yIM>~O2No)%m$$QqAxCo&q6izWExJEs#k0Hs|@U%7w`jHXw< z3V@Gp6>?7%Z!zA4H_LA{Ui@Z#lccgRLYZDDzSO`8c&S`q-d`3^UA+DI1AOP?gD!-D zDY#}4Tq11_Na<9uz+Y4o$nr*gakrSp5@dO%WSX51BsUlS&62^gjWFrLu}x~jYJnUD zU|yX()u|85oLoUQq3i7elR{mvUSuGUSyk^Ab-+%z~k zaeMJe*wCD#^Z+}Zw};s(0wFKdqtS^>fj?e;Xu3QEa_H_w>Ah*y_vjFpWY=$`8;wv} zN?{#|nfw}<7S-tN@0;D9UX4d=ti8tY{>i&L%E(aA{3xg4&gK31#C5*J-hzmu^X4>@ zx(Z`4`DOl>gRW4EUcdDDGEG~+XGcA82@cNKf35!}SGh=#ag6+PkCaD)DllxO{WZ%W zmTVEuhh|o2l98Rr)(`XBM^+zb5R=M7!PRO%9c)i3>KE%juXvjXQS?(* zUS%VYUi~9#xGZ?nwH+f9eu&TrJn30uont@P2^7kk?(#_pnW#!{F*(~$bGisRVefHL z$@q<-*0Dv^8Zg-8v72QgKnKu6;g;M9nvyj)tGoqBu^nlK|8uv%apnwv2u~$WEmoD% zK2}gEzjROU1o$SKe+3Xrqpkc6b_gZTxRDZ};?kmB9(v1P|7we0${agLg)X^M;b+Ukp&gW~ z`u&MlzkE1pU48>i*_CLnHv{Ixmp$=>iOiGmJT?I}G2J$AM}m@?o2+a3LLKJERZ4gR z@r_2U9@<38s^enGlv=M*(#OTq#^~+0j%KtM(zTn;>GR<;>-Snlk#Pq0 z47Iv|H|LszFV=GFMeK_5xu7h@D@iOu`mu8wccbqb8= zaPa+0z&xPEc4EDlRgmczm|!?a>W&f%UUe!}o$S~|@PVTwdXqrmVXVeyL)rRWC?GZb zAN|f!cEQ|jF=?1T_e-o_maP4_S-Zb1|GUmyB zGhbr2az-ilZR98Kcd|WvDAMxp)|xf;^;-wiV=zeY1t?=rq>jP}yeX9N%a zsaG&lS?4bG+~7>JAM&{9x%^w*3I2P8tG?Kw5%~jy#Qn}0H^)TLzhgw#2bB2w&%3l) zoDaHg3FwsoM)5vU&1u;1Fjuc~=!2*><*&?_$$AakZ||?DQq7Hi*r+I~ICx-97~X~@ zQLR-EXiNxgkfsY3#K>Q8>wcR-OhwZ82+Dv4!`y-tXKG@P+^I^yY;83Y6Fhg z76scs$&IBW9F-78#OK4m-et5`A@z!PoBuaEz+&0czLdYLFj5H~RzEbbk_v zLN7kkqGAWHD_sf2U)JBJr{S@PDb({bc-><&_u$MGNp6S}7SeT(8Ga+CIZZ56=A!_L zQY2`!z@bA=SqNQYDtL`ppA~;}w%QjjR5M|5nRBNEqq!(xRBgH2ea~ud@MJ;M2m(Ed zKzJ?p)Wfng{fINIS#o40y@+nzUP7&5Jv5G~rn-S%hNQdv^9 z$rAooBi#<8-N6pq=t6!;@U^qatlG|BE&t?^CKqjYbt>f;tpk%xFE&)3YdNfWzUGx- zVQz(7d?=c1(pa8#mj)WEBJaY_Q!Dv@->-|~Xv~o$@)fb2)>~<@cElI;qmy@X865_8 zt`1`hWfS-sTSm~klgZQ*d1tJrwYOtmiQ}VRk*saVxTEw~tVD2C;hh;O{owf{&ZZ2BoEoUI zzII<}S@E0s)Za)5)idDA=MU?C41|yNx5YO+Y#2TGo+9`ZH)M8;OCe@td*Jg0DD_Cy zF}VJ6?cc<>#KeHfFH(2MQ8_*K;@njSlv3PJyjrzHOoS6MOH$6>i zSWF`-z7Q0gXt5Gdc?xU{_LGojYSYhYG4TVH$jMee1;$%=sp2;loe!zrUUU4KVG;@M zH+>$$Or6OES-7-1WfxNVQz|cl{v_CRE3ebgi{AtPUufmG=`i$i$!92H`d2#X?1u~G_7C-B?Ml~B z4!%pH%6nvZXY029m>ce;OM#=JR8qw=R=e?^x;b~^jl=tesfkS-hPaQl8ER%Y&j2h5 zN--FubL?gLbELzDD^?u1{jFD*4c8KVBkuaon&tV-pK!y(W~st^fg#gynBw5Xd<2dZ zBvP507t~6M0%c?i zjkK=Re=SJUvT{)yH&sf{G1%^*NfA^Ik4lg?6x8cyC92n(6=qV#g2A|7+llk!`I9|P%z-{6QZLj1VI*p!qjDwQ)gSE9ag)026(Zuz?52LAj z7#Ea@3O37!qvAT-vJ~4|yq6stcW_r|oPJiA0^V`m zy2eFL1muaBn4;dTQ_ZGW(E+@8G+3iyv+3NY*GM=5wN4cBu&TbgCHTrcH~ZJZ5#6VB?ckpd!>)tFIuR%J9)Y3b4lJ9*8(qN6aebt9`BNXf|k zTx4q(D%{7$hHo06S>esC2awhS5P0xXhWnve!9|))U@~M-3{!+l_gbZLlp{g_Beqop zJA7Jro6lkgwq9Hrh+VZhKtXbukej$ULa}CUvV}3gJ${yA5+G^%#@AT4xp5~t2DS7# zxr4JO^b*a#>UwFMCAGA3|GEX_(nz}71h&lqgoAaLVbs}*9B18VNWamxmODb;8$zhV z!yB!a0kt46J5oGTE1PD(EDDK=^~nMyxn&B5F%-2@Tl#Z?WoeMtnmfv^Ywt%|EyiO_ ze#Y-$`)o!z4y31JXtH_)KIJpN_U(+q!p_aprnE;3Z1-4A#C#rDd||Ac;%4rbsb^6Q z0q1n#ZK)yP$`XmbZ_s(XdxIZM;vc?Doi9^kWMT>D8hb&c8h#QjTUGy2RL_x4Jzl|* zDHlzKc4+$D0>M+;=?*SldgzP>HT%Vo3JFBztigBbYxl?>azDqHTL6WpZU^b-hWxv_ z>oFP|DiMYI_UyZf>zDqnR<+}WhLSr+US*8@qs?L$`I1jH1STbg?- zm_fjo3%;NdR>e*VD&{G45qx}(MYGf}-D|9cnW&YH%hk*~WV zbFG-Vaor{%%y1~u=O}3C2v^ECE;Q0+FW^P$?kgH2O(NWfAadDG9Rm#ItUf?%_EEvN zq;GbX$}>(l9HFpZ?6Rh2CXsNHFRWhJ_Uo)fkqI>C-;M3?O_onf-?#u({Il_qmI@V; zI$UmboyMQ;Q5PGWAyVeovr`=Znz*(J)}QgUnHGA6F3eFdw;C-)gAyM#ri4xWxu*Z8 zEzusl6diEFZTZW_O6-3C<>Lj4|3gTI&baB%x%L3?^gn9Amhaa#y&*|lJXig8JK5{r zAFQmqdHb{#R+t_l==G^i3SaLVi{;Tu~`$G=<%Z$i}|sep`uB>c4} zj%`Q5g&(mG|GDQX^`H9leuNP2H$i{7OmHBgM80#XJFi<{Ov>jwlMk9n0Lk&N8JadP zQUw2-sXG>O(0o6@Z&&o5Gx>Xhu_{6X5_Z-;$K&=m(ahr~%>MP?UJ1%eQ_n{g6WE-x z#%;7Oz7cKK%%lA6@xi2CN2 zP=hgopM{~iTi6$Ck*wV7JL-NNIa{UEPDy;3)%BZPM(&VwU!I=-ri`$<^TNEQUhc7J z{n)l}!;}sef5CumXN&doaS2J!&K;KR(%yf}hJGRrN<%!fqw09^BivIDYR?y4^c!0i zXB~bIFXKhv-;Q+Rt##iAcETr)l- z%io*-mi0axe)jA+&^i||>hOGQlCG}=;RS_pC#_S32Qn?O(iPT!`b8Z9|KoFgX=i|h z5M^m&Q1(x%1Pwgzd);=iGZR-XQta0FKQ^-G^CPio+N(q(Bv{jQPSf#(09vnc^)$n{ zx*s=s?r*X}nYDYqk5D-1(PeVE+rsVs0ZNxTX`aeZssodf)&~Q6+1w<*7Tg;Gh9nea zY3!LIG|b}#sU)ypP7mnQ$u*7&H?#H3I+ZO#aHKL@j&$?81)=ZGc?&rh?3^LRKux}t zCEfh)0g8`}tf;7iEoLI~mZP&k9Dutc`}7E^eD657Tc3c_ZhX;yJ{=PEwJr@}kuOC& zuI-j&wA&-XCCXvJF4TT`c{iuE26yYS)sw!|iU&?#@YhS^!>!mf9)G7Qk>#Wp*Y2@xD_eh z;;sRTyF)0YxU@iVcXuba6?Y5n!6mpo>HWK(bIv-?`~H!&k~LW~*?acv%v}3>eXdKo zosmAN?0e4cYcB_BzdKvKC3;0;)p_7h+_Jmx_`@ zzkM6kcRmwr;13?cyFo~w8a(POU$P$cYnxT8uii$Ac(;cajqqt+6u(8(fHN5LpKOh% zUKiEpYur0gR}*v|y<(-Mu}ge^4D1sSe@_ej7-;@rNbc#eh?6fXQ1M~f$VZ&sGvv*x zSM6W-qqASJ4l#$9Py)g|nn+O;zI>x9!=kuo+~4JjcUOcAy^F@KH9u&v|64|PJpZKw zXH{#g*8==Or`4gNI2A3r_6vRDPya7s%m)G<)g5gAZjVOa8PS(2){ogKaHf|sX7}$ zO{c^IGC!MDEMLF;(9A|e<;)-?BQ`x$oG^ta##@)etYP)ZLu!Hg#VKKIlJKEqeGcG1 z{PU^_;QyW;8`%mSLNsSBUey_U)+ju6Z|?cg+e)0!gviYb=-!LD@QKy{%vT#3+{cPC zXjGesjLLgR-hC{{fA|*dRe#_pqf?-|Ks(q(4-$i*%jQTxgdBZ#{v@{4FJNp5i<`l$ z!`8RMwexS3uw{7=yrb|nDisIR!pv{}h_%zr(v4-EN8S0V%2V!jbjyd!2O*CvFM{PdBek;z(c=mwUvLR zh0&hdazZaM!%y%EyqR$fzW3rTDRxkzGbx>%_p;OQzgZAW*JOmnhLMlxLg+Wv{(S(xxRrkAlJ%2iQ=lpki3VS=i?apvaVD}GbIt+QPJl^_~N(lip>;SFx5kJQ*_I@Ff( z*{RD|E4~uYR8nxk$}&v0ugI8#)y^JLS=*nRHF*R=;71}Eq6ii_w3LN3ol{F&rL4(Z zK5a~}Kt;Yw)N3u7E?phTiSa~rG9qdNmq%RW8r80M(OmZq{9QKUn~euq(YM$~pizWh zgGk8qr9ll#z}#7nME@`jGs_ouNT{jA7BHJ)&23E)Y|&E!Ja_M%J7z}IBG+1aGkHa4 zR`GFXT%+A-2St`RztN69?#2?Y_-_ek*0PE&9@Pi!Ton&3dSm7$B^83hueoNTC+}fa zPL=!rZ&RkVbRt>oFhT5S1L`z4%J9kKd$zV~yW4G@wcc_EVdMlwsyT2LSdhkPbg}Bd zWbv)c&g?%zH-)SqmI)pF%U&+#__25DQ{E9)Zu>p@e=VbkPH)~D*a<*^@~ z1A)7WRe(9BgTK`Nm*su!vsm{_dy#LZAw=e5>OvwQe0$ z(j04|uVWJv1*lIzDSz)uQoD>RZVa`$u@mE$3nxRqyIQ5z=XLvhMq^n6_^DKBuJJn5eZJ4OoQ8`4P+aaUmuvwNuW)!~RDwfWqMgYpw6^%lKhDH&pIi4+EjG;OkEcJuA|l2oob2??)U>H0q7wVLdF zX#;7*T_pCmWgxTNU++2Xe8JAv&ydX3ujRjJL}48W((htKf&;OmVW3f6-i3-A&{F?F z(Js-Q=~RR7Ex@3NC-=BoG z>F3u6gjXlEpD>8mWk^%o(QTp&>i9_K|3I91_6!ef`Z?pqFnf6!uR6xzDCp(pyf}w? z$8ul0_b{>^oxogCwpy(e?9Ok0zGk~}AhelOcN{fI)rppP9`O|fdI`b34n^jvuu;qE zM}E)NiH}ru!~=$pW=k)iD2GdBIToNMvh-mAGK1^5Kj{W@4~tg#Pi*jN`N=e3{!{sy zy@0BOm@K!$$uSY)`pE+;2EKUiT}vxBvK~z+d~xyCYZp-=9D+G?Y{h7m60`aLa-f=y z(Owe=>iTkd8tE^h0-Axr zXTR~CS(ulOKYh%T?I<4MZguV`L(kxT;1il3VY8_28_L1+l1Vtul)KtwDL*mwhJHR7 zZ?dc`ag5G_u7p2BBrOQ4fZv74wY%hs)~uhay9&z3;Trj>W?>Qm&lR!ztE@Risl6`b zxg|qf!!|R-DU!~qsrONd2*=FW&8K}McS#(>m{`W^2)oe&PU3XYYBqg8n*AF;v7EeJ zhz1F!{T6UUt3!6SyMFooCefc^2lk=gX~x@J*9>eec{Rp>z6vKYW-0^@y!I~|2o8qsQV{G*41HN~xici4 z*pxs(ry5aXjh+VkUlHwxGWH=#aWy?Nfe)G3zv|HW2%D%+AtoWgfhuaPJFp zeq%;^g=5xR?KLDBEe`FyZAxJ?=1ks|(s_}AIpV$`>kV2VGx~jmn@Ou!!j((vI zJ`0m{0|f(Wz1}B2heU3tIgad0e`vixhe1KVfDSK)X`bJ>30pNvxtoK4BMVKW1_4{o z7@eUIgkL`l@obUZX_l#N0qRRTIbC_yi9h*Gz?jlpfVb3yJ;wd@)@7R_Bo-)}A49^X zEeqz+7Q*>lWdqr@ny~Hz6jJr)b>S7cvAO7@inu~+u^sn7>9-YC%4DVzfO@XLimqcv z==0&mE}4Hs*r(sCMJuV;QxR7qHn`pfCL-tb>#fM|PT1Sv;J>K^4i>@Fr(U8|6%6#_ zNQnozD~~SLkYE9bxOD8)Qh;jqymqlL6L0l@n|R+sZwP5z=NQXSgDLh|q?d-rS-2gN zZ4LeOO%tW+%9zMVwVQto1zOmZHd>0>#-cMv3UVg#BIqOa;$<2xm`E{(6_tC+8b?vwo^Q+#P!B*%ek}2?U>(Q~J;RAhxmZrgR;cQQO2bWh-NuIT6Eia=n z93ydaVcV83zDNR~A%Vl35JO~o0<6#Vd)tTPK6Rt%Tk@x$LmX%7aO&mzZwznaaYzHb z;yB*}a7?nyYLekYxh`qQPmJ-(l1<2WC=dz-Df~v^ybZJEA?B>SgU%4%%Gcvj4hZcN zKwEe_qWy4$R>5&V^Q$|~u0o_4DGRzXrdPTNcX}hP<>Lz(f8g@@tiloKLYvl@P?OaP zaL>W;0yV){LTOee*yJtxcl8hU_^OdLg;CFIZxm%`rDlc^$W07xwoVoyDHcRpMCBK6 zi28LbtB09wX65mb8+wFdL|^$WejaJ=w$UyM`mfq$cQGCKLEcc--7!76kx!V4X!X+( zn&UsXn%~e}FW)d8)q(+EKibyubek|R%(sEYlQ5&|Tgd^ns~HJZ{(M%x74f9|WN9km zrAS4G)JG-q3{Q4SS4xI~>0Qgs=&{P>lcF<(J=q-MRo!~GCeSwd4bI4KT4X-JpAzQb zTF7xW!lSapZw7Zdw$TzvUVH?%{M5Ax*pHtqps(&Zp-=WZNX>K2!9?Ng8n5&Gs=SKpuzB;UGg8_UaKp&LJZd78tg}* z_mEVw$yD*#SCQRmxd(?hbCkIp1}~aApTLy4wrN1k5D9Ht$E?aecmeLGA{*Ut3?W=( zxpFu&{7$z($yA*ni`=JqBpEvcfH%GBZ`>AxsPgjX(}|5Cv(`>qjd#pcYHl{qu;TJ* zqWSi^Xli}d!EK^FfY}=rF%Y@i0ei9Mz4@s8|JHK5TiC0e@6g&Hw<;U|(&ZMu1nVS_ z@F|wOAtx~m|EXr`Y8vtRd(Tu1ul*@EAV|A)*aWsruk*U}o0;hjS6S^euE1W|cuuR} z_fO@`_a<5zf)JR+!(plue_cf;%y19FDs&&|4<1<;R?Qv7t-55 zw(|wb0o4lb9<7n%e4ly8D@S&Ij$S?vBNz;$KW7Z@%|1mUq<%o+<4!J9yt}-TOH>#C zvS3#9CgMAK10I@w&F;1E^C=v4s`!lK^e3URp!2j1=SEHao;i!=F-xEpciQ>uKtsDY z3d0|J2|rE_Pj`(zxWtUYIm!YxDcF1ES{8KdejqQluXK+DC3w7LP4rd27ZlFvilLxK zdxW2a8_ib;O~uAw}Ch=t1gx7O44|qubXcUL1z`7B@4jxa~_D;D!q{ zv8D7Z^;8zwLlI*!h)$1I6Wp(H|alhY6)Y2rpB^s^Oq(UJb3`#9fy zTmMjYCrK2j(lW5lJrLnn(`ygNZ#>OLD5VrV7v;M}%^kL$$iI%9fty}(_?&X1ME&*j z#p=k#`Vg)B2VYkSlCsqNNb?VRao(HpnSfD0zH$+MDZ%=r2jc_piJSLNd~k_uWNxeE zK#^W!unE*Z3*b9!SQ}|PW0a*8^rh6VSzH!h&Kl{{(QO6Kh|O3dS<|!~LnNl|W~Uv# z5OLz3JJ1nd&LQ_js1M01*lqk3ghsN^Z_RBS=GqYs+`o_frP-1*q*?YvUAx$+O&8p= zRxL((H?48ollx_#eF1OaT?_Kw=JR1?x19pI=fH+f4iBHOPnotJpDVR9COCCr{g3TD zxRwSXp&FNU$y(*8#sqIK;IwcMidh1p)nY$7kYl)NC^%S{e&~!6>oZCuaNK^|nyu({ zs$OX&=6e{}7XbAJns`s!XO7}8D#`%DlD7?zoL{kCe!cRP!0CMFxJZY#Vz7U3^0O_r zTwFX3-g3;0o2g>JyhN#;KpQPnHp)87)E@f&7_%ozsXcgg0=!%YUR&2Yh-t43Xyv26 znI*3rTdl}e4Nr)JZAc>#Ol#lTkPJlsO{wQF zW29G!8LGEPVG&TcLt=r>`cz;i?kcLg5@AZdb>!qXe||)is7)P~)X?-hNHK6}26flA zUdVmTSCkEO0h3sfXT#@6x=Rf>!&&^D5e;i@Otd>%!!F92UJF%aUjE8KYbt(jcpJPWz&RK4BE z$NXluPN_ELo}7|3D;ix(VC>kj5&R;v64Nrz#_|DqPqWs;=IrLTHXNl$k+-_AonO>=XKV16v^x@?yE3ua0jFS+BwWRb8Vt$m9bYLK7T zO_n^7m&w3d1?W{w7@dIW3k9brSM)5MqF_xnUNWXn@%?A>wTpa(YG11ny`M`GP+koH z#UGdQ)eMv3M43s@hE3IYL&Vu-L_nyz2@X>=Y=-`)zh}qdSN$KK_{Dn7w7*GR(}Hqq zm;6Uj{8@-_dl>lS6^(GdD!hBnn`X*re;Somg%dfLsR{D_2EI3{6;Tu#(lUb_u1je zEi7{4l-cM~lmic#>Q$G#E>N(55fZw_hT{<80r4=@E0qA`a)m*2jvd$84mVO10=|E3 z7n{3E9CClF;rm9c<%DYC@_mR2)~BKh`s&GI%=E#7CGfgS{uj_~=by~}pFSXjFGp-g z7=VhOT!-ozHdoh*uTzD%G`mDYD3H&4?QXA*hdBgdJ;rE{-W}P>|D8^jk_Tf~r@k~@ z{0K0)LB#5(9;#h|b$SKieAX5c;eDkpBcxd(82O7ef{Zm17w^Q#n~9OV%g@y*+mfdj zEBhZ1lDrJbAq@Nuunl{~5;C%odua5dy7 z`#94Oxcu&DOU~T4>()zl%X>C7Lw%J7>zdETKlrD9A~Rw@xKF@vGp_aHj;EQ6NF)W$W9|}A1LB!2?2%gzzX!Pq-jby{`Hy=avL>52J_?_~n?B8kl^2S8TLVY^ zIPtuQmUOjYq11;HOO_i7ZzQ$S^j196Ha%C;-`4d7EmfwOd>4eVCR?+I~n zgJ(epBvoC-HzcefmZhspzjP+RMT9?fs|cb!pCTMrw4w*33s4l)N#ZT{?Pf}FE*pB) z9zHLy!+e#6y`)IVD&YaY-hib`}GpJ(n%7m=W(4vE%_U3I<8r-wYQUrHdb5*T>}B7I-GV zLF9Bhc*ib?l6lHD+r0A&6U{-}niWg_j%5aMT>z72af$>esjY_r`QSITKyGl$FdGU< zb-z`0|J*N001C)U%HS??Eo}3O=IBt-nuSh=eOAs?sd!oPk5xZGNrlLea;77q1Q zeNdMXPjb2*R$UwiPR#V(O+wDZm1YF{uc;j0@SZK-zjez!vH&zuZzot;a+Z(DGzWOJWT6x~6;oYJV6}~+ zmP+0;9mSwddB4Y+)zx-3vcMsA%B_br4gh`pV|$QJp4mATLJ5R1UDtxk|Kf^S}SXNbSYG)(`^}=!HIzrL04h6A-eQyDD-MB z@d;C`6=LLh)sC{U6krsd(e**K(ErPQ%kc;;=)UO_WuwAQ%Ty-a3V~aR`bW-4EGJAH znq5&diBl#v43D3>m2p42{KKD5L?!3rS5+yh0@F~05gqdb!zY-2bjH0NQN#fNbK&H4 zjFpXoTy23BHS32Qo5|KfjhI|R^huVC7<_Xx*84d9Zc_PGZc0Jkh@GVC9Ix&%QqzKR zliXEMEKGd!cx9)^7Z8D8MJ*LMun>zwiA4;$9Q^T4WBP-*?9&>PT0<|yBOi}|uf#w1U_3eq^(hq9VLoDdRXg@i%pXAQ zw?BVg$6IF&;v0UO9=}K6J7G8ZML8k7zUDKJV-NBnJ{9q{!4LW5t4~Te!m{2LaoR<% zH{MgBH-0R@s-U&ziy`7kp)3kn@$~f~fXS?Ch#o8^Ka4lNN$7jcy}rGaap?b$_(*GygC1yQu%P0&c=*AoV7gGRuN!2=!t%oS-u#yfQ_9k*qa9T=K z?)&i&@TQWi4R4@rua=mZ&~aNxrFS4^?T)0{q+r#ufjnc>JL1>k44B(eG~@noR^a3Y zuLysaCbPn9aoUaC|4b2t?sM3*zs&78=wmP8!5?B!%UbZe$=eKaVidaL@q$1lc#fWA z3l-`i3`?+ZzY(q2@{u{q$Ze2xYAVZ6z1-Uc39f!+bN4zIb{i8wpFh87^$*)p5tV`% zR2jo*1ss|`x;^o4*z6fa(}6SyG$9}#)BunNu`Fd934PPCYBL7Z!f{dK#w=awzIvtL zi4oW5evmfCc*Skr&0^XgRaV3*$VbvD>y3JahhWS&bn$`VJ{Uql06`p7|0ICIJ}}cp z64c@7kiw>~m=EtLnKkfA2T#>*ctj0xJ4v zf%6G_wC4x${hWFIdtJ+ild%E36j=GUVW!}h!Xg)WTf=9@Hh(D4f^;dfKvjyA*lnu6&--~P!> z(anld{?E>A5uTKwAmtR88iYq)W+hF+eO?BJ{3Anw+RTycg8 z2|Z~aYAm~b8zk|->nZz2qx!FxPSlMFEZgBXCqK@`dwV)-g{pX*(`v=dU!N*3HH6ZPnWp9%dxV6jIx` zpmF(b!ZD5~9O{o5wo5L}BsreZfSAp|sHgk_P^q~FS@ej=&lN#j^oGmguj{l1)F92sHuKc0@(;Byy9ur2y;PdMsEGf~CcXHE zJK4Yap1bJ^>^}jgX+=NafJu3Tr$qj{!4qyrN)6+fv9x{E9#mfA{%*kwjGh45AO z)MOKg*MJihLJr%9m23UYd)RNvy2Y zK&N$_O$Elvd*emeha{P^x{kCAq_eLgeD6<7({7h*heA`aD4sP7BHFyzMCx*8 z%g^qvJx|SqgUz=!M03(^(@FIHsO~h3uY2j{xXST+6e4;9rCwulBO?bvK3*t_liKkt zQi1-ttzT%^G;^EZiUvRp0yg>eeA>PjzIF8SPnMfiS=J^A73L>Mrb0Ms<^fZ!gSxHjxFRbM4j+U;{ifWx{J6-zYVf@nBNl(hMQK6bO2hM zOg7;X?0nRps3j|lThp)i5CHBzTy9|Tg$6p;RicR{u=gdfMhLBocxJ@jlEv5u>b>ozohL0@1i9Djo+ zVSO!_Sj2ztz92aN#E4^zJyWA(a14hn0IlQp9OP?n-E8h|?hGnr%^m7@QxWJ64Sirs z-(7QuFR$8^##|4TQ|R4~d9VvdFGD!@vPPOadi+=qD&n~I*qAWsa9!A0P;L!p5!3Ps z{pBuUIrXyDfpY#PHW_wdzne<#g-4t3gxuS~@=mK3DSX!5Fdbp+C}c$L_jM+g_>MoV zRB|>y2IAo6GGG-gd2foRCO2w|tII4vYR2cHLeDC^BN5OW-dAw2!M!tswmzrqu+Z2x z43Dxs6t&}^$l3jT@88<(fzV*Ek@F7nLIHjG8<7h;aueOj1}nLVu1KCikVIO$yK8;R zZp$i~cyfSVzQea*cj!TrnqsugM7*XhkW4taC1)y}^vDiqY>2XaZ-6MODbe^ORAQgI z6csAR!fea(R1Q=SGL{Z$+>)M+L(fXt$muMH;i+j&p^XR?@-GhQ{=?6iM{)nOc{4UJ zFwmWH%nAF*o$CI0RWE@@I>NDh^W%!cjR(VqG`gO&^nyFeO;c0(k&Xt%vA#oNv?fF5 zv0R~rvC(OsUD?+MTZWaUDyMnf#hK`6_mlEZX&OpfI;StM|2aULaej{({Xb{*4MhCk zj{e)`cfSV96#hNmZ~mV2H_2}D|4eiL*(yg<%l7ZU|7WYb`5fK9gYlmc88*`L?r+%U z8!zQgHC)25H}dAkzyH&ru;*=e|8G{2EgF| z=T`xAQbqy$tspsTC3f8s&C;%$Px)`|?F;Od{(LT-{e`6QPaEX_Z395ZQ*CN{`(1dS zX_a^w{L215BWV=!T-~T+*iH6=xY%oyI)zb*~c&|J|`u8aQ zRbm4J1J9L1!w^0DW~9T4){{sc{j3QKEqqVK!fD2YPq!T}4`N77Uy9Xn*7655u+;7i z=51AN4#Gdh1YWWGpQAq>XmxyE7dCN*CBZbon@?lg0CG zwpfdSg2M=`Fc_Pn=f!bD4!L)S@JCl%d03x1$Ugjx?eQWe^m8D zC5205|L9_iF=*a%V`DM+Ywc9PmYLp`#-2Ux!K2N`P3D~a=pidMj_5bD_mevBY2KAblS5V3DP<|D|^ z88vWU{K%Q;vF6AB{IK@2Su@Pm173T+?i_19X;BCiKn}KLudT7|!BY{&1-1@Q1(2(V z-kHaWjgf_H)MfhLL)P!z<_$vdlm*~DmYrmA5?{V%JuR5UFc}C^-QU+(46A#mwIAv- zC!lQ}LV1dKhtoS;ysA&a!d@rv9j^P<7(PE_WBZ{Nrn~E^X8avVM{;Pz81RS(o(hRM z8v*Y`RPj~!lS5`&$f#%6V(O_ZE;?7f>sLpLBa1Lju^az#H>K;oT*NeV>Uu!rQfm5>|PJma}|POz{|s0^;WbHGcq(Q2%hJ=1LmDsLc9dcON(T?z0 zmdcMu`NIx={#JGf{wYci&*Ir$W*t4f?3#5SgMDVaISDY2G1vZK!n4>(} zFVoSiYDCC0PC67ivF~~t3re#ASikn1waKtIyI5Xmur zbZLKb*){^Pkg{lc+Hp+`BWmY9wE!n8G#+ej1&vbhc!^f%VUD>dRa1w$Fo)3v*f2M8*Bmh! zge?UK!bqXrvPnAz?2fz!%zPh^FD9Vlgj6sjsRO!3J67IL`GA=@}Zd&DWTtB}F zgTw{MQMERbIIj4dg2L(iZd%tUmd%WNZP!ggD1NB;U&J*%U4v)cum-v#w~U@Ih!T(y z^^ijfGyMgXMXuB>w)%*S$~uXIKy(Vf*8@=~RmGDecGlOk5KKlQFN9T)YcR9}u4^-y zVu#$F1#J=c_E5xb;l~6IS<^f>!@@rZ!)8i?g-POm(E4Y%<>}> z>f7{FUs=Cr*!8v5g+kQ}dzDSy%{8_Y=nJg_5%I+>d<53gjG`}7*B)g8ZAZlh8I=lCM_s(#TNNde;gA}8La-^C8 zmMVqACsvywvPTku1v^XRp}Z>V?1f^PuZZ|q-gqz|6C%rM6~$0TM{;=jiNzihs75}^ z()By^hLOI}!hjJ++CoHF+DkiYLG2G_2r!bI46X5x6Z4I&LsRqz5L!Y{3p-Y$JRsl; zXt1mit}pJUd%Iao+`FqK&x5?anz`nI5?$>D<%eO%YZtkk{PfGd#OVzc`iAcd-Yx z+Eg^l?1Zcwi#={&69COShesHV_Iy+4J>l_clShiU7*}DpIr!q1gmJ-Jjwz2DzK*)y z;av}_H=-I1m0qJZ>J(KRhh@W(^M4uOHTS>ZqqK@8shIiwk!Ul;&_|E>tIrBvvd<1E zRPRpM-)8udkA7aXqzJ_<%2yS*2Jf#P&wf2)G`iAJG;k)Uad_nMUh{ePxmSRPfvSI- z7#>IJ@RIxtO;SiblZSAOAh39Q8 z&X|4h; zT;c7fj%;DE@OE~8W%%?*g@#<;<;e4KP$HapFtQ5mxPH6(4B+l}`;BP)sX^#!OaMkQ z-HO$+2X1VYgMS`C3uY|Ilaf*pKU8>`|HLw2gAB&_A?O z4v|KWfzalI-;bYTeZu0UuWqOGvL50rX*=XID{pL)?yr&L>>l`_e!mYSm8n5v-i-cx z0pCTq_n#N?_q|u5Uy&A#(Vxm}71Lax1iWq*;e+8y(WCpe`{q@(OCnZ0t`0YO@Y$Q+ zK(r{rPdw6r%a4XUGA9GK4AJzdwTx{qF9qPS;KCeeF4db(0|7ghpvXV?&JGVe7nNW{ z2z_<3PaF0kNP+Qry|$C@RPv=bQ{%SngD(}ms2BA?9J|DtrRZD>8>6N34l0+A9|DL< zKY z%(h`kW$ILci_n7or$_AAwIBKi2{iQM?*K=0g_93`!{5AFUiNbt*AjC4&nXUwb_k!o z9c&JWX|StHpjbH@v1OfQRQ@V`{)p3CG?z&#WB*XjdHxfmcIt(9uOjN+G9K_N>G;Y$ zxBbrFTkHH`m%UxG!4EO{Ni=x%hRQ;iYBgvnNXa$b6~4vjfvaz%aL?WB0{v9Hm=6Wf zh*n%_JZRUf(e%irkoxIqwYLrv6y z^^_^B%ekD4IWK!RG@b>$c?}IXijY(LkavB{df{*)1u3ajYrFq?^`@5od)M6Go8onT z_Fj{!9=I3@^41)&m%DKm!s9)$mu3WM*zH4NLaE-k()NdMDVQtPh3WXPC3WVJ+vH11 z9b!e!paJjX_ZGj$^-*&TCWTSdjOJb5-H{^7D#xtcV7S&}9?9&UmL&#vj3;x;R+p5? zwn7u*JfsV|fEtMM(F5KjgOFSD=q~0UHTShk5qIla5sXhS_wyY;E_Pe`vtr%Od_NH) zM~-YeYb$@*3)zcHiL4nzL3Bi3W5vi@?5j9d(BBPv$0d}LfjYV&`_pSti1Vh_XyEa( zGRX5$Cf@(X5+M^Ir@mcIyxcIer?Ks(&psBvho`oBr4adn635iLq~gDoAim?&jW$Z_R%PVkvdjcu_5>uz!x2`*`D2D6#s?i$PUS70$mRTglkl2D%n-1-Y{*`g>YN`X7{j*J2#E z9(wAe&%PKvu3~UT!R1)<)1!4Tk_Pz0FRnyO`J)>jGH) zgAWw^rowsm)=Z7D;K{=~94(<~XVx>7qDWF9N0A4EzR(Oun)*^s1XrQ@GV}}?8omH@ zZ?S79b@uFQ=$M~c^PNiPGA6}wBJn_&#bBFxOY}|-NL9NEBX@v;_-ov(*b@aLP##~! z7m?n^P>HpH<&A#A2@o9R&4G0a%Kb?**JI z7pX4J1HRw;ZeXfsY!Pf5+CxWIR)a>q84ebnVubjuu;H+OAYSpY3$CU$`Zihl^lG#^ zzvy|-5Sr$lt<`?&N1Jep1(oz9pNb%o5_QD>ip?93sqr-cGEW&;apQ~D^x_=pe>Q4$ zZ5NZ_0g9XGA+FH_;m>CJBj*D|%I|!sNlq{VVesT9{D!yOGC~V+dni^x-H-!Z=jeWEVY7*L}&UiW$}AfWHSu3-^yJ18rfLo5M`4Tdj9&Q}q+iodVoklW@s z+683YM0>PJ!Sf<_9Q^_iJ#==8qng(UWpucBw|z2bz4Bst2f``Rzh1&c6wZ+Pv;K2W zg$rjkD?V8XITa?hG@gg4_)2m{ksGqhncj%BUc)A@0A?bP*YVoRGk-+^(f7YI*aGZ9 zsTvmG%e;FFdsJykX&h?tTxH%1A<-OT2oRuJ;se;-^>kk4G2c1aH_`guZ`@67gx7qhqpgODY%Op<`e`)c)ZG_C3_=He0_(5e>5+v@zT~u2xMa?trdHNj+Bt%qh$dIS#Iukx& z=K5a6O!}O$gk^hwtLprcbbpmOc^Q0~hH@GcyXG0u}1sTZ&6|5-F#XS4N$S& zW#^j;UWAzk}Hc~G`0>R0TBVgxG>?-Vt3V(#2fhipf!vy1QlbJ zho&PRDXH#D##Uj_2U2rw2Nwt}&LIvX&M?PaI{IL(LfL)|?ghuVl=}mP+ViM~YMT#J zVAwzrg?R#4U^bC^=mtOXDKG5NY46KZIHZM`-nik4Jih%|GxUOJ^0FCSwH1OEf68F| zyv@7OaGC28s#jOQo#(vGO#kDYp)lL`7^@e{|8+il2g>XQT8&<&botfm$VqXQ;@3nS z9&E@ez9L+QtAV>O&#O6XS%iPg8^BQq$NaArDn7I8T6q8bWY@&NX%CW%O11m7JT`+x z7DMdj+b!CA^oB+Mn15-TgUiLrcr{Hx5DiL)g0t zC?dBV8SXXzVm-14IQE~|0InAd#>m)sZ>RSeY>ugpUi8J{oa zSM^JO=2LcZ!>iEC8bfdq8hMb3O~|lcFpF0@%63D-go6*W18Md~i3o(*YaOJ_J^X38 zu%9!EstX6*6a)|0(;FH;L@$Bj_CKU;&hFu==6FKr4d{27*LIv*NiCfhF|Kp{hv|ws z?)COxrYzOkcM*83)+^XSCd*9u$ri>6eS=cA2=!wHBoy9U9jZ}?ymGA!d1R~1ZAlF|>A4YyDD2@@ z1>grGt^$v8Y{Cp47`YC&392>{rgBsg*;UlYX|sC0eC}KgW*AhBe#&Mfg)KRZOYiWf znw*8qtIq5swd_u+f=LAS&?4I)JKJv=CgSG7w>-0g8S3mHjI*KtL(^G>#nH6w77Om~ z5Zv9}gS)#sOacUVcXxLQ79hC0ySoG!++FwN{r+7CoY6DWRabTOQ)}Jj;e=CqIQ^|u z;y8L+YUkMONNE2&{bkYN$m23>#{CMN&L}r^cDsgj_L34 z(9G?s9E||DtLeH#(n+fuT1tO^Jgx1vxLeUvrClem|Fe9`&v~Tmp2#9w=~$Us^GV z7SY)FzcbgR4PbU%3f!H2+<>+Ox<3p}F)0tdA0he(^)<7|ydIWtKli~I^ONRvEgv+LOnQzgFR?QY0TMc z7rM#Fs7^nPSnn)X9Uue$>bAftzm5=uK*bx0b- z-=PM%FQc$Nzo!*lK1HeIgSYq&bCw(WwJuXB@mjRK=Xwdq`gwR-63Ibi6e$I-?JsC; zlc?Yfvn*HXcPb=f(j=PE$FJ??;66Q7%$rOX{2uNm8f*{>ztdvw-;nz(??6m4;v-Bs z?GC$f=`D2}S2R_Qo{`1^dtgHMieWmpw%LRk2aq6_nG^vRks{f*^0 zL~X|H#YVmBbuLN@Itl2Vb92KI@R-@mQlqPdu%k+UA$~p9jUM!lb=Mh;d%Q*bD+{Hl z@4T@i7a+c=_tcMo5G8j^d98IMc&3KC;e%L0-;I}<#G4f*SO^Ahw2a^{Z}J_8ey+>4 zd03j>R@j%05PU}{@#>k3X-SZ=qm?wZJyx;bm;57_Ly}jr+~AV^A7L_mR75A z4O1p=HKx9ymEz_$cW*qwR_Fx7-|576^fTU`c;FSSTZVp+r|A6b`G+uL6>#`jk;TmW zw}_*zqX<}~vIF~Qm7L1TuRdI13|<}wr?>$D z46H;PRow4L9yw(1=UVUxGd^rcL*q5W1IKwPw-^>b(p?S`R^#=h2eHb9Ta(kvL^(T2 zIRWBqKEc{uaP?+2_?TE~n)lO4v9fWu(^=(Os`C#wTnuVz>cTdhM(XBo-Mp6mc@&y9 zOmq9bwoGN017sDOex5B`ECv9zBHO$h4)%QYu*kmA1Y(F9uB1_}kzl1u!2b6J3 zlB%Gh;*mEW@MXovjKeCUnTb^4H^b~7l6Jbs`}h1cTmPq+RWbMx~cMhh&Ck(|F0 z^_H80iY4gq@BSeGl|94GlRi19N!X#HGzFu%gJ$KtFO5fB?)(I%bX)&9KV zqYk}KLrjs%&8WK^vKCubo^H?>1Qw8UIIoZGlB2bbQf2l>24cYiLa^gIO>r7bBYaBA zEY)x*`KcJ!h?CGm+)uXry@xb%fS6GpTIvmt=sjz*RPbYqk7m7U;2anDfPbu=^tG%z|`h zgJ$vQ`F90LD%dSlo#n7WV4tF3y1=u+zV0?&P?~hj3v z*1rxsQzVu|aXfpl|AS2GU&yeTb-vx?CTxKX1KcK!YjV|J*|^@{$vk@K-D}G50jTEy z+J2OGrmd&86XAt-ZW&W$cYhTxiW^gW;{0TOzJodhEI>^e1fuR`G*Vxunz@+C?SAB6 zg%mvNgLbS+YI-PDc!ORcA6tLBF>9~(_BT=fBIx__n9+kHHq*q|p&mS7fyZN9#LvGz z$0G*If4i@54gGDqWrYC~sSCXCiMr-R6F{$wWSFPq7&2cavCid!rnJo|!n_kv7TgL> z%=A4MteO2Jw-)b3f(~VWkrKcX!D^|0@S59v9!ig+eVL%X)Eqs@Qm-d(3*BkvGSwU} zR;Z{_r2eu3vR;yHEX`L54ea_88Y6yy=z84=Lzde4jGdq5dITN6pYVJ>(b3{8k`nW@ z%;X8z(0R#{uK`O-y+gw~bMq+2#!vg^(UGlb!q*Nh_|SRPpkl|q@uM}9wAG?qPjr_IGv6){5^@-xt(G5I@X`-Q zRl~Dl^s&Hq8HpzM^9w{^pIqg!a1;;9WM-Pu7bigi^Q3jHHTv<;?I9c= zgwufF6 zTV~RY((Y5|H*p5>l&bt|hQVoaNjFI*3-MXacb2=}uG)Flx!ZGDI6+=Lg?j3pVMX7| zTIMam3@WPV8`|X4_zUh#+jV3>K;xZj{-M_y*ElL<7L`=uyd4)op-OF%7a(|}#JPk- z^pw*f&>^pTIrH}ztva$R9y~L9vaggSn@z$45cN6~I4Kn*5Q@~h*q4*bondthM)1U%@ugc2vc`zD1y2I$aXpI9jbkr2mi1s(MdE+oIt9626d4 zMMButQ_|(Q@pn@T%BV>M>GNJg7D+Q?2^>b-gymQFDGUD9lZ`D)*<0(@bF+=q=$%Z0 zR%4AO%F_Eh1x=uN4%Etm`ISA@dCrP77^6mQ#a$LhI;Vzv3ap0(oU!VPJXbX|-@^hM z&7z^oy3}p6N#2X2aU)B3HZmkmevtrL&qQJ~N2UFt%ky0#A6`-_F;A&?;(4Nom0Iw; zlyaJ=K{}1*RJvnXOi~xVNucKhR4Kbi0}1v&7O>pn^aGPf9GzM8KUnQn3U=DOgA$$= zFogq&yVVx}uGfGEhD4$q31x>^;)_4SA9eJ{nDTi7%w@&Ty zzc3QaTwiX;vy{<8^VmMC=M_;1S6V}u5^Zr)alE2m&&6}bTAh+T_{lcEElj^na%B3e z({f0gp#1TOw&lvgcos9Pb~oo=s952g(o=9Jpk7w4Tzn?CZNvvxq|Fp*2a6Ib!?Le4U84VVv~5bRAF*#rC_~Lxphy5_Q7C{8&jrzSt|8K0RS=e|h_&($ z`U8!&NE(JSVnU1+8A5q~WYO04uGUiIdcOUpUpBHN69|pd?}T!h^VLQX@z~o<1?ypH z`wEtZHb6?plCJpteGW$Hq<4BoCLq5QLWAL0UJ?7ops=#iAF&ph5liJ)>Y|Vh5^|EM zB^yYM4Cp&pd?2(1aGBEBK}lCG(j7F2fyMflcK47pB zF2FPR!lI!Q?*AL>0~1n1kqG3MWsxE4 z0X7d)*HLSb()=r-;24!%LQyS!^eFp3v=Q_tIGBYB9R17C4O^BBZNAEAGM06PI$ixm z&Q5-Yd@qyjR^G&VoIE-hnPrTQ{O1$f^tn9KwhgpO-4FPP0PQ(GlFP+ArO^23EnH)C zdL5AfFf*opdI;Dr3Bu_gR%5Dc##o4Z=Hl-8F^?9f=$i(cP{erVVyQR0D&@%*DR{zS zKZ3Jik4i0a1FQ=~5G@RU8`w*$5wQ+YBcZvIiwtJHB=YNirE08WHL1mC4L~w18DE-z zv}dXmA@hzOSx&JNOuBh!w>)fCp*9}f;iD9ID!HNM_0H7ap`FfQ#6+#(#xkDXuO};M zHGQE@8ZBsUR=Rs|x#j?)`=mP_p;MREm2A`(W#TW`VLC?B7gqR!x2UgJ&(7T5CUBMW zFw5L9ukLIfl$k}xOF;aDlXzlOEC{b&*ITlONkv7KGUU|o%aFM6Q2*q)DWd&9w(}p# zSw~s4F$SgR6cTte1zeDs8ANCG=nyIGWyo_Xc)2hvSP_BF@G#xTg#S|S)p-#l-AMY4 zHG{CUvK?mAzypOMP|4RmUsJ`=firK$nY_$`OUdT(zM#1*3rszfsI!Bac$z;LW-TAL zv()_il5&SZK@V7i9QH`(1TJmbcwx_3u_+tDSmUHU@qrvRyuk<#tV;>pOhVRXk+O_m zCl$eBWO?#9RQWh&^R9wzF(zF`B1FTnebD9+4dzX>UIttWV!UJ|5EKZi$hy@=jskXT zm>0=d{2A_cz@|Pw5nizGQ@uz=Y#LFLX(j`v)HlbT5KldPqSBvqQpRdtW> zdS+QiiAF@ylqy~lMp5M6ei}5B+y_A;DGhkq|0ZnEnkCu1csj5`l4CVH(`$va*`7%c zUE?Fb^S3PD!E30;2gPe+#3Khbgp6Nr{F-P&FFAN-_SwbPX24RA)>$bNdwKO@=OA7zyx6KeoOX|_dynFBEdRli2 z+t3vmOLz}qMd&uc0eIK@J5(AfH(mhh1$z>$DA;eYXv@xEr_yi+@N=gS5xvfWRJJ<3 z9D#E(1z!hKos`o|?x4fx_zJ05aC%53I%w!!s395>edGykHu8y9Gm+7!=sKs|4E3%; z{kZ`Vwvh>Q%&gJDswWZGTk?atfn-q1xnzm%;cZ~LOi71T$Kq|S;g|4L!5XS=wto4V z4~V*U_zjE|^PQ1s8%?%ciWwz;VL8&I)l))n#oW@K1G3C)x=wv_>cx*>wWOrQ_4~sWRBan%@E8cBWTRn3 zjiOoTnCVf)t3rOm-bb=G-iNkUBH zpd881?JajYx2gcc?;!bFmsp&uV*p)i>qWtY0s%x+W``0Y5V8)@!nPHwgl$r~p*n!B zTqL5OO8Ea1D21uaP=APm<%&eLW=f;2c(NR&&ZDN`>#2ChW=wE%{zmvkkgPm_pTff{ zr&PCh51|UoDMw9L^NI&R#iVfsV>USuT%{5_=YDibVnH zKQi*BjjC!rbz?x)WjgqMR%nH?PTr^vnzD*gg!lw?p`W?bbgMDj>aI5(Q+x*%s`Ql} z2_0(&IoV>cAX7jU!H-8+ffGU0AZXaK9$_7FPXUl?S)?N#b5^d|vQfe(RjW^1P^>6} zW{VF-{ugoA(^L;n3SnzLDUMhE`8A-F%|>cElJVeOu(dQj%)lUd}WD;f^q1N2R&1Grp^WTYR8-g$1yrc@=^w=A$4QJE(be5J~A zUX}X?%2%nwRh=4-{;kb&u=@Dxq?DlB zsoS#slS_=goMu7Cnkb;}AJ zF{k(rZ`vlmpeN20*^UN{e9ZUKmbdb4UISVr1s<}>I6*$iU$!*c0!(&l4Z4;`v{4=V zfQ;F`!V@grMI#(d1HlniV9A472_niiL0MKIU%f}=MU7|i@N_lW$;jE07uwk+PLv!l z4FdO5HWBY&TPh?KJM{SnD{DPCy~ftdlIF`<(?hSC=mVQ!BmJpZ6Sm+zutM=vU1gSS zJLn7@%Jy^c7u2)LD;?t5Y(~qFSir1N?&#) z)eys-KXk@B29wvQlLB1C6@u=Ylx43YpEJvUM+sMBe~g4Szh#e$2RH!|1d^uUw|^-B zSD6~6D^x<7QGXTgh0w^`fh#Wzz5KJ=urJ8GnFR7Gavi}TJYJzakqg^lQih=JX*m)2 zgy1_jEawHKqrB&C|G68Dx@_dE9Hox~M6L*9bJvbwL~1S4hAdxzn;hXzc5p4K}PQ zeu&*~vBhu$%*H|_MCUKQPz=idI$zEb>PfaGD9Cgws668!u;uh<%?@a;_TFIvBF)7> z`enjzT)Tib=252=`2|x3X??CxlI(M*IK-4Tzb65m$~;ZiJD$t7()UYBnv_i+8usc^ z;0?Imi?}+AIMqI0Jeag9;!y6J?$v+pD?ut(-esu*R4BY>InP)NCN+nZsS-yJlt2f9 z`~5(Z{d|-uUN&OpD$hRX1#%h7ntRf(o~H3o1f-QT)Xv@RJYs0 zT!tv6k;h?axEyA9Ngk6SJyPD8nKUdz79o&F@wYYdo+;)nm2e^k zkx1;QPMa9kq)F|mNA)GjJ5W;eV=H>%h_kqPrsU8F;E9=I4UG^^0ZBc2%MtO2kwRv$ zTkY(fT4M-M%!?)W(VT>IVpG^@Im^H%TrAHk+=@Ks06;|QS4j9RmL8vg>uNC_Wjb|o zcRD2RoWs?|ild-rp-dGEeS?8je7FY7fpFYdazij`kgW4A z^&j||K23J9O-@dJ^78b=({&ZXyaR&4`{ayXRI6-E_@ACToDO6=ZgVaNK+;WQl9G_l zrrFgfn7f1LpCe#9^x-rnD#$hj2C)ntIZ#~G1*ir|AqlyNAPGH<5cv_8EnJt1Cc))* z%=__VyAc*Tbw}BtR?gseV1)kKCuT0-(2)2MiIbOX(KzP#6guO$7n+v3K$^DgJsAfP zKbE2-Pt)odQI~vHQx?YH7yS5}vBDwJy@q@cjW2h2V{u{VOyg7STB);QQSkl6b26_? zN9);?Yk?l-bATj;#1j4ew^-|eLDR3exypXq9lNyZEB$q(pGK#ZUKV6;k`lK4_fn|K zSxWdTr|W0HpfY%Ce)p50HI$Sx>GGSUvvUsA7(rRK-i!zVBXdn4j@1pS;N8^nBEjcw z^$`3foh(z%x7?9VZ>o>^6bokWni9ATZsCWzIFqdokCwwsJBav!WhIaiGLa`t^7(%0 zr_t37x{u|T=hE{(x;k74$4JePZFf%F&*~8zTMw}9+s~WezZ-fUm%w{xI(Hge7u`v% zV&T-xS_d5Y?AEtG?M40ge1D|(KbEBjEzvS)1DFS)Fz`Xs`%*eO0|YCsJkjK+dq!Gd z@H=Bdv99F8U61}Er1)6~(nH`KPD{GYsfJXq&v7kS-UmHq{(%jcQ2L!vZdA6Yx^y>M z9^Qsce#}e+m!-Q83b-*caCL?jNl*;mDp1dVqsy|UWo{m#cnL4~o;xXVx3!_*QlU25 zN1sH|Xq)>CMQdT=C0Hm-5En6>&_Uh5w2>gSYWAMam`PE(ujar6!$WBF=&1a~>*E<% z*}Y*!9WK~;R2a2zSt6Ha`u#hqlJGkRve46xatFwvQ5WQ?7^QkE*nOUzRiRQ72nyZIA8goEOb6@ zRikU2kUy=2|_Wox0fH>WzmPWHpqiX`OGgL{~`rl$?R@!(8b@LeNP2poow$HP=|E z`(x7{{qLYNqnZal!=)-FJiUcNx(UG|dKgw!b1!}QyiS`irLBc2&SY%iaaKYF$>T1Xk@c`^>1|?OpzExpb@HT!B(u5!?J&% zknZ_+EMPpkUkvq>k4V$HvtIF8BPyG0eBzI&_5pn~ThJ5%8?Q-d<9M%zrK&?K<}ZYK z!;ZFvvSX=iYKB|7Q@{H!f=avjYqII(Cr&J^yDQp@0-6d$56jBnJJA`%ys{-OSLA zLl}{3k7braCvJxo$t^M1hWr{efZ9w(r>T7}$1s_FcEb0E77a!A5) zx}OH0K0s)Koi=;zk}sQxRzF zeF)pQ^rg*|(=y2^*!J6xy)ZnRz33&9ps9t3sOEEm75b=5^4v_k++_&RJ$&JU>UME_K>kE<}@a=Yej-*%_QZO_wccz;&$=K&0*9grOpU<&uRYDF^W%7!rNe_urM zUhb5T=XKFKc0S=Os>HJk_2mhd;p3n1=xRfY3I8;v)m7-f1M17tGfUp1{Gcn)_Vi9g zS+1%LJIxlz#Y~t6VrjpDJ~^hVOX!OP3^X*Rzu@_w`GpR(rVt0SrlgPz8768=j*L-r zMqZF5sxNGxI)}Pal%KBR&9|l|hcl)$V0K@y;pycLuf-P3{2mum_ z+O>~`GNBOe?&ryW99=osGU&I9at_B!)-nMlMgD<+5SA0_wk9)!x7>K{tSzQZm;aQ} zL`u1Zunei#VdL9>R_T(FpN}_4TGCkd$fbtt`uOl2le&Sueweo}mwumm;3f``kXtas?jqny$o(X)Kz3!zN8VG5 z_zA=lP&dnQDrpc2%lGxDD}ftp@eXgA#R)EOeoWPPLXYdm>Pva$9_K*_4$Sbo#VUPacf%8Bm1k6RL zlHK>9Kn%w9ud~Zu?ON`<`1$lSEL@{4BnVJNV9$)gVIKNt2jcPJ-mw~6hz#{yh7vd` zJJaak2G@Cc_9JntGo4=gTzbJ5{~KQ#J9jUqwOYsZ$d5qQsEv zl{Ia$lfbvtY2Q+9CFtU_nRiZ9i z;4>jZEO&k68b$c+dn0bR!SMtiTh!PKkAk6pe;?^RNNWCNN{~Uhs0VfZCX3vEaS!l0 z)q{_&|84gHdT4h$U9uB+J|Cq0ozMH=*a9J-^l4n|vG&te!mm0%71}%h$+Vn5{dEkQ zGJu9`ucLinN#~}^t&Rk5S(}&kDC@pG;4nL&F&v|dmIr$0`jhH%*|Vpn;sI9j?E^yP zNc_Bxp7keaO@K1>?fqC!nMgNQ-IKQT;~Hau9^!ZbgXWzXE(ug{nWkrIt+! z(vC|;zpxr(~`4WvHspRc{-iDG_L0eltGu+P76=7CP|&cT6Vh21|-(8hsm01IUCd2CN=b)INEZC?78?KMQT zS60e}SnLcrS}W!O&DdDX^T&5m@YIlRLDP{g_!7u(MH(h@%g#(2<-=pRA(YRJ8=W`; zP0ckT9P78u-S0@X?t}E|TPx?25A3%uAd^o6T*;D@7iGe63-oxfy+%7VZAu%D%1~BJ z>#wV>%DSCCV}P{yn0ojZOi(Qu?=`R2UDmxOHSvU8g#4q7f?UXwL zSG#TcPy|iaNx!G3e@}n48I+JZuRD6FZay->J_7}^^qj~Z6wa%>Edb-!Xoy+IMxood z{Aj-q8eKhiJNlQXJGE8WneBs=(v0yVsn4BDJ z1?B0tN?b;qtubt3#oeFyFe~i7d#y70v`UU z)2>p9a?c~*v#T6L8See7!s&YdFX`cCBe4<7{KmfjjQkV&q6_cHrq_HaBK0gI>?Ib^ z*F6M~Ea3|M6aFouWC*J1$m`3}=@#LCrBb^F3M;dJZ~qY!h2|>5M%aKLqkBNmGviX| z)kX1bID=PMxmg(1yX{hddK4`JAvF3M;0Ekpe#nxgOs>j**Jz-_)Bj-wx8323o<46H zXi*Yz5#qyh=djQA0Tk_#PEfJ!xYEUQO0#MbAo%|u`d`w4jCrbsV(iL@Zy~Ffo^l4J zqhg#$WZWwQBG~5Ol9EL(Usg_#<{k;w#>2hq&kSdr$>;3q0mN}<;(PDmMg{!?v*~4h zJqOU2X;esz>H04L!BQvqqbx>=4mK+@cH?Bel!bK9A=G`4JFlZ3K~vKL<40`2-s@F_ za7TDJHxJN^Zv9I-17WI`)$Q0l5Ta+lI?A2g6M7ht^xNAhGjghxGQ5T*nb`W$5DfO*S(#t};EyX>RS@|RAn zRS(Vk4v~8lk;nUs3msq30Yg*k*w?u!IgXD5)X3L#Zd+pIDK332uK1~gScU+B>2Jn2 zL)ULvw-b6?KX`8*WI94r%8rLiK;L89YSf)vAqAQsLfs_(4I;_9u;%_AZs za0^mHGv!|2SvPYe+pM}s@?)IRkRsE+c^ab~(-rAt7Q)~G+F21$SQ9k)?8L?6GZ;J$ zvDNSv;zw>JbM1VD5;U-c7DFGs_t{4{$;+A%_x@7qJlO+;6chYzD@5>FSWicRska%M z6I<7%XB}D;+kej^d2;1=x?E$NUkH^*NJjmh)xT~T+SuC4p4V3N*UM!F5M4%%ifHDs z4Xh)vdW~&+Fe{w5S*E6PCj%AcetLVgljYz*uT03Y^&ED5*Y3nZ53&$yHfX3v%)i$; z*N5)#NnoPh;!X;wy$-wWx3IZT?cw@8Y0nJI8&?X_!fpy0kGs97=S`5vRZ2AZO;+z? zt*D{0^1u@w5ykT36PK+}D#w)3`;^Rol`u=h@-O*s>&Ip?amf9vY8>Mezvh`q@Vhg2 zflRqtvFmFU2*=H2Zc`h4tUhUt*l6`8z2`FXoaLXTnC^+v^Y?-Dtgw-K%$zDKCp-)* z=3j?CJ(=h*EQ;CC8g5q)B!8F}N!hry_b&|3$OEE#ePYG7UkFrr(^!4i2XVU}A4l$( zA4DAJy+nIRyJ4`q-pMAm?NODC-c=aduOaqvQQ7)scJU-&w@{dpsE3>*7d*D%zRnAJ zen$_YWJ7*v(2?rQ9Nd7MmbBgXMEpF*FNPiPsHjUB`J`BULQ0~`mM-oRzD zS!?dS^zK&Fv9weN$ACE8Et#KEitj`{o0j90b8wChb$^}d8Fs*aYCNvK6@O{m=Es^S zFY523Y`OY9g2|KC9qY!%yVXnggIcf8jr`>@q z5NX-N*#mk&_~SYbEB@~tGXELNmoIZ6%LZ;a5HfKGfVsP_3T#OR>X{I zA^)FoPfT+uWwR5G0RcoUkn=&t#t0K$?>IBv$C?9I)!%xo{YNIzI7($l5FJD9KlM$8 zwPxn2OF-$;#LCA33h`4wSg&Xds^5oe98u*d^Xbkrm%q1EKEph`h~vDCV!x|$u8BZ= zv%Njt@Q0vhGF9O!otfyw461#_Wut`3p)A|r4{ z%%RSmcA~wvcacEWKEtxoa6q?Tvr@It9%J`b3 z0b#uwGIBpn%`OX7GhE%KeJsw>9$&s$(uVol{46b}IRc_j0it!1b}TDBXk%}(mz$tO z#`7{?0K38-np?C95CC%}*-r;Umo{nw5ea#|sz;?9OLt64z#bKE^d|y_2~WDoOuUne zzW4?|;Sg%I``Ed3dOEcOH8$Z-NH0p>wNLLC9GabBi~L6@!Tosg-yp)C{(Cbp*Ng5j zHqHhl1CX8_6~$@#C;Ht+@FKF^vnKy=H0l~QjuIt;WZ81N`zHL75IHuxTvE265v!~QAtB~X3z(om`kEH8 zlKWj0Kke4zRt9a{6*iUwZ`;ts5T&Qx`G}_@S)r*!JT2_4=$yFx{Z92&pQ)ZS5M$vq z(C5shBh20)0F$ajy;sD9r+mv15JuvwoufZ+d@b`$_^B5~eHBzFz12x>?@-B=ur(HU zarlEIkwOd=w;^HXl+;;jmxKBod=|=IWOso{{B#0`OTbh(lk#nO>9T(aCNDt0EBvhn zXfpMIP`AfD{I|l6X}C|^H{#2BS0J=jL;`_uXo=qwB{qpK)(f{b;%qx@{jr7n+bdvj zq3E&Pd7~_3rPdpye)-`jz43>&0$y-aWcbqagw;mAu-8LudKtysZ?_N-f9|Eypr2+; z{R|082y~%}SyTHQsE>;4UC>6l1x>p*yhwI9RoUVc|x@`5)?BdXAg z*CzW7Z%a(GxJCgRQHp(hVZO+`ZA-YFC~>(BI5+&+3KzM4%0fzkXIrqR#Lj{ze!|G( zpD(ZntNInP##QNk*$!L5qryHZZ+iR%p9lQA$HH$!2u7cb3~Lq$>S-|rcJDFQcVB7; zwH*KqzKfymFLRHO&M1R@&EBx|Onk6{>wXb?4gsP1!5{Z8zi%=hjgNnJ4|FuYgMzYd zPxnfd%#Kn?P*-vgFcL;j;KO^}jAsK#R1nQBOKU~UR!Oc`+#V#IuX~f5YYQslZaVLb z++QNaRfuuY8@312ANSJ8xLcscYXs58{k)hwn>fdJO$2-|6s?&&V6ZQL!zwk_KWdSX z38odkYFL?WHWMjXC^FT1`Grt-eYzHN@es;weWavvA#UCoK&?t^>;F_2u@G^ z!zye%7JAxga_NKncf3Xn+0g7w+0@YKh?wU|p*(qrWJb4{%QWA;mvpRXe~{||A#|=Oh~L*z zwVzQtTQ7ey>{hC-v}cUX_g9BUtapd)%~8jXJ~sYQ!fcy$f5DG_`|!THB~Ni}U!-jK zIRJ5Z$Ve404|ce%Rm4U$(+*r_{lwC1Y;v1MN$gZ@1*mziunLoAWS?##MInC($ToZ9 zKIXlkd!|qJCpPCqk9HdbPlSe%ZfA))&XXt5C$qfdZIc@up%5%lXgYqUuD64f-}ufp zbn(u7qt_MWuZx!CCxngS?aGh1c}p>L`ARNz`36%DrOx*3xE-W;W{U&41idN>ITDJ% z@{r_n^;BCUdKtA9zGa})*P&%EnA7aj9l3R%in>D^G9G$6Ck;vz((}C2azB!*m1JzB z4d`h$z39plJsG{dzFKzsR_&$7umSW@Pt-I0*n%m%i(}s11#}}Np=?J_WS>_#9S!$! z`=st-sX~E{BYK_V{@ULAq3tv?%ciAf4&t#qKz$tafle>y~!=Y37h3 zf4Kvisj2Hv`iyPg@0Yb4ZC&r{t+|py0?;6dEb*TE?I|I<$C%0(~aEle>E6LNQbg@f!VKL9MCrP9h&O<4sqL)+ky&;H1zyA< zT|;e&Dl^f1nX@8$t`P_MnhQ_y$;dmdD)x}R2WGMR8j>B>n|v2WeKDp3`2n)81&28G z2LLTdK4lA#0`>_4VQg*W4$ygR%z88k52gAS5+;He7I?kGnhD!m| zB5ilhG}TWWZOt15G54;L#~02j#MPwSezC07NjExl=(wA3omt(O8O^?s!Pzr{Ww9IC zj>*KGD67?5F&BgT$n86mJ2r(O0PonHr={-xVUo}{Lz0`l+VTlcAZRC2mb9f4q{Xmn5aZL|2GCucX z5`R$9B5il4)!pjG*FBWR6{~FjBc5j_)tAW^wz0l7xx$Za*F`4j-5sHvX<2ab4va{j zv(dU0lph>Us@#S%GoR=B(4#mKT0W)_wjOyR()qwUh^ij z!ibc>jnFF~PQP9f$1S#cgf*4T-g4{2Jx`)+$)lV2FWI>o1 zBj1pYqFV7k*FI#rU;lhQA+sU-;I{$k|8zVGr*9l@2KtgoxSvaJzMCJNJKKB?RM~b1 zXZ}FXP6DNZ6MQ^RMlFd*yC$vw`Wdx zIZ|D!B#RcYC^IZkd6o;}oZ<^AWtv}fiCZ4zB3F$aKZm@;@2~zF{WX+cB%8sV_z5#$ zWOlxxyGIT2>~9i(m~^9(h1z?&Z|ulAO{}vTcITSw-j?)sfWUw))sYK-2GW=O}1VCGR!Fp+sT0GVv5g3&&gz^3N=XJxxfgDaX@~$$}(lkM% zq-nr5-jNW4VbJ=xC z|DAo-jEu+Y!CsUHgMxkcZHeX5(h?A9ofG2CrE4T=ml~{8jOO)YuClgg6i5q-lc0ySp$`qEq zSQL2jYr|^fI9Kh zCDR0E-%y>^KnDkv(C}EJK-p>bYf3m?x-p4u!Zav*4{D8+PkhUF1W%Z+p zh#qstj;-O;7F;j7c^?C(6Fftn@*qf!rZFyEvEnJ1a00xP$+UxtF|`R7_Sl~TYDo^7 zW5sOHs*o|>llI;)Sy?e*am#BS-EvU36miXGzFSmf__D>DCK|D7u&fF463z|iV>0W! zPT9knR6W0J1C6QlmkN=%>bv*Z-ZCCLX`g0EQwZEtJ<;!F_W4D z2PB?HUq$Ub5UGQTb~CaYDg{@OrIecPTo};N`Qe+`6GwU3iU!`v1}bihOSvT(_eEP; zLosU^aHHiBA@=K;O1ACE@@3Ao5jZVFDa%lzvyrLSVwKzBz5u(xeU7y`K1G-wL>S?s zEJbnKe(|T(^qv))sM*fB6%YI9j8cl$6Zf28U4qNm7X&j)M0aI9CuVNkStP7Ot*E zF0`fS+3goBu`0PlbSDyNMTYt&;ZXpUgzm|)w=|l{eQ0w1&-46K zs_)Qb3eJvOh8N;9*J!<*7V3RxEMNC3u&YFG-QtlXvzmI<5Q6ym4bupn7f)AGWvKF| zL$^opmQb{x83}B9K5yd@9oeY@;R45u(T8g6H6?Yh-q_>A^{wXKtkAfqa=o(!TvLWa zsUF5JT&XeyE>y)#t%hBt*wDHwoRB4_R!5k_T)2S(Vb^!!IT7;_>ESK-4M@-p8ZEki zpAo)d(k&_n2hNv~zT7@rKbasg(ANBXCg)xu(!z@YT0{d~iVydU2^y-0Z~D5l!yP^A z+-nAjWuTWa$mg)4KZ{+6gT7au%6JHJJ2GG9S=pftw=w-FNoc87+podoa_(|<8XjEW zFwbK{dVWQrqP*a6@A65L)-;@Uos|+$Og31I{3;Zd!vs;dXTj@c(UHBi+J#MbS@aPO zjNa$kz4uHWw#)c?o@Y(%7`1Q4j=KyTJ2Qe%t4Ct;7J$z&H0)Nv- zqe#FAs$AppBVXb3;$PIZLVm6k2yf!-JDSopstX=)g_U7zHhA+x?fL{z#5!9ExWYv! z^akf3U-uw0uE<7W(Ecdqb6BUPXj(Fiy`xBTd>vD)$D7`yf-4OU9v}%YH*IW)uNwGq z5k1WGwT`yx(E?VvVcul)crrp1HW)2)dMuW56iKsvFAR*%XS$zhd`v1>8upEq{x7k$LXt>K+Gp5AkQ^R5@+&TXs8E@lN*iI!L~i`sg0;x7+vE)SG3ZM4;}_p8tkiTI`emyA z`)>+g>S(62XU(EY=48}qAfaUOvqJC*Xtzcyu}=l6u^(db6EaY4t29i5-bD}+npLau zf4@snM;EAvNMu=IE=#EW$aRNxEXn+3Z>dhk^GP%3U(24OxX~- zWsh~U7_e0n>!x%#vjEzRrR8SukME3y4yb1~O~xImR|q3ERxa7_+Jix(BQGq+X@~?4 zF5;XZ(fnVJ*}pzHQ>CW-yZ!&#N(HG5XcYagNz{rb%ESF%i!cnM$Uy)9tut0Q)m-&l z>WlUNW9u8ED_PfdJ007$ZQD*d?AW$#+qP}HW81c^j@5VeTKk@R#<;tF%s(@!F=pYd z`W}|W=5f8cQ5i(J<_hTB4i99C%Z_JQ5|6ycwuXCQ zeeuE)Li{+k{AAcK* zx9Q?SI9nQ|S_gMHdh(;Mh<xF}Rgw z9MPV@1%{*Y@0ZD?g+n{gByo^1X}uzQ<}3Paz*)bB^q2`~r)H@KMT-wy+RFJfSez$X zBtg2-Qq|=(tU@+IXt!I~?=;^yfU+_1Q70!EdWDWyy)VwM&Nd-~fmACHQBm0rjWI>> zEtuE?{yAcUY>>eGT~nQ__9F+VXr&uYr>N}6%^C@mC2AIQezPXXTufVB-gOqP2i?6&Bc>^BF*g|%cXx+z0Exf zl%9{gLxQr1NFYIj7><@T99%j&%<@AAnAPoL5DhV+%!}ggnRUU{uuE*bnrH9e>vmLX zSE*GPC5Db`xp~r%9-VBJ={cmJimygoh>J^oP(XoMw7z`%}wy<<*o<0=iAD1NkQIZM1%pb2Dag?7-~@w#UavioRWd44p)1+3K zV4e|(m`(WnS3T{`m9Tsk%&nQ>^&wq~)DBxBXxuCqTi8$a*&Fp6%u@oxqT#EoN?U8M zDoJtQGZW2&rUXXj>y1xhBG1i=xp2(Ilz*!4wV0V%FX=gB&$Y6gZI0#fjW_IOmrt$U z+zy2yVa~YxR{IXBK$KC9O9f^ACs~wCIPBgyYY<6hca)vG2AmoDluKrDB<)5%3+M z5_v1tUv5G=)R2WxZ8cbxT;g!j!r%0Ep(+<+?0P)XYvc97VKjmSiya#uEb)uD7J9yuF{Qw439_y+r`Ra*}}3@vh;}u@%OK!)ZYZjiwz+ZU?$_ zQD*Hgck%kt$Cv5x0qwRgH5^{#Gt1iWV;rgKd$;`g&`AJ(6Eh)#wq*z{o&ECG%*t`t z#07(s6Kr0aM3A{T*)bP28iAJey0Y|Aw&h17{t<#hf|O=22F91B6~RIzJ0PBH@X9LY z#bCnu^%RKz2J8OvvO$%U(sB3tyOMD~%OeS9rk9)Ed3|Furcq8KQV#8GbXs}2y{-Le zI0T{sPI`#@54C7ZNv~kdfvc?!kO$6gi>VZYixCz~M}#?ib>G^b>)zG!qc#&cWCI)W z`6YrH9%s;;tn!2l&M4Mqg-e^*l`Rpd-MRy&+qd6C63y5M7Weyk-YNU^s;Rpb1q3eK z0qZnVG4Q(7VpA30E=7Ofu&u@{{r-?BcOqn)3G5TE8j6;C=;Qx!VKMO2Hp%ev+62P^ z$F2$7Zm@hp!N-R38gHvPmL97fiQXOMkZXm1RasGiw}|71TRn`SoP;QW5J+PoE_5X^ z1x8AQdado(CZ|noF$E_r@(qtp(344T^(*7~3+jtqFc5u@uK(xT@Ju%J*`_^u+8(Lv zdj)9`i{}AU2Nq#*l8)AOj#|({jD+wH#M>`xOZzw4EBSjF20@{HIk5Q2QB67Gm)&Rv zG<55Rvg^nGbyJAj-U+w+lh3)u4k)%n_rwjBcW%3Vy4C}FZ<|ds%xIk^n2<-{4$d>L z`>cl#ZeO{o3!U!Lw}c6CB1APBv=7YF?i;nJL6C);&W6G{ipgWQcZbZ|hVp|Hz|)s2 ztg^C@H0ohort5JZ&+X5iOxLR&E1w11o+y#kIu5)GW*0)_CgqEh6rK*3FC?9JFR*ST z`UoBeyeE!QbZsC3=u}9bH?Et}BvO`xCwEK-1$HqVDmhLQh7$jIZyX_;!%dm|CYp># zi<|c^7xwMUtVnn~YoWb9N{ob>e7zo)%lOiR3r<9@I}>P1RcSp*c0&H(sik0PcfEAv zg&WPteDaz;KJlVL-)_)U-k%_UqAZGzzM_h)^&Ki*^o})fF`pjURW(x}yJLU7T+|h- z&0`H;#MTJ?JR{P2=f~$om@Fd*c;EYM#t?gi!%dITFS_00E;sx2%LF~gmk9mIEJ``_ z5f+nlr7p*!Pnl?@JN=QOIxX^q--i6BLZ3hR(Y~%Jr~MIRq}{$6yx5j}l;IPr;8^Rx zGFl&E=T`uD57@OA0sNXtO_H*NWma#)z)rH^khBssgRd0pz4WnxST^d-7d?KiOfegF zPJnhJOA=mE$d=O2u7?i%foI3vDO0RC)>(i29V;Q}LBCEL>R#tU+$}2xA<7nFR~5-;FzjELs#w*&Xa;`5tH-SHMGy>_#3=|O{qcpEgRU&a)v#PJLH|`K3NrOtaCIB}+0L&dgks5;D*XZR>w|>OXjIaJAQ) zp(4ifZ9_C^MeQs~&nqt7T|0RvP-V}Lma34#XrAIC@~1$fxi>s(sDs!a9Ruz5?LeFH z!5Y*QQGDjg!}6JmifQ?lQX5VnQlXk8WfG6wIiIdvYa!&fd0OdX(YI3RM=aiEH&_q% z@n(+Ar!M_Y?D|!p8`zJFbvyLgOj&M1qrU3rD|@fk*Q!&cfhmgh>d^CNGqnG+(Qf6J z@##s_?v^>9+2MD&7zy?a*x9n2jAlfJV9j4VVe70wRirC!Scpr>(AbRE;U<%%9_W*? zdAjkSs6P%oJXw)5B!r~N4N7Zz@cnq6D5zQt{LuQMQ&-}lIsJyHg=WK0uB7R_X-)dh zR#Ns;A{TbeXZNrKYW4*n;5QWmb5lIS-IOj;G~$Ntsp9V#D1&(D7{zkevUAQJ_s;EM z&8i;{A~Lb{cc!9;Z#3`jc=Q%2%Q;@vxVt@?QFW`B3T#Bd@w}>{*GoWKc~1qE#9_db zHOB&1mcUNFLF05Ykj0Q=r+_k?PI#qdqz*=j+r*)myIYllKhH}8+oAf16%D&(? zuOtllG=VxL#qX<`v)g=as$&U($D2zB!xD=h?U#o}219Ml3)T2m^eiQMU6&bvXwXQF zlOlU`yn>bE!xmk()Ib=lGE&ET8M1(E}E!Rx=0>6Y0jF>s$<8D>wF^nN&JC3UGz&LB`fpG*eFY zJ8fu&(>fu{!z>HfO|WE{A^TA5F>jIB#v)N7N%Wueq`{lT3! z^YKC=4IEqF>3Op(10)|~bVBA*-A=2g{KQLK^cG)`HW{==5|K6XG-XC9oAh=Cuoab} zDXQuSqA?18M5HVk2L+zDcFNXmptOZUptZr)nPS*K=w7;=9&5;W(SBnu*BKK|b<-R| zX;p33WV~NxNxF3t;f9AGbr^W$+J7`D!jfN+F(N8*t$&uW2t}A9SogwB8)a)1naeWh zqdX}wh%J=Z>tw*!P(tZhu5eO{HCX<=)aGKv8pG`Ys^dc*^1wn2c4IDVqQI5Y&5XQF z@2$3BA>zTdCLCdDk%B2|ZqmdiGM5t{aCguUufE%V=;@G03)w3-==K3f^m%<^(rW36 zxsz)m2wq?RL6t+i01~?J4f_`bihSmzZP*-eMJDXc@NDT|Sd!DVU}0UTdy!D7=;Ca1 zthz{yOJ}S@Bb{1)!DSoD<r!2{X)CS;0bWLj)xy~C;G76ux{!>witMhdnb%& z3EmaY#WNVu|Z8SCLyNcyk}TdjOr5uLS?f{WO~NjeBT(VD6%fI&9^C>;=| zHxl%Em=oWB2UHr-BsI`?zX*Bh{_+#CaUt2gS5(o5(*Y(jgzr6e$Ij@rUz8DtqXU9DXF|LLOPFBuF>gx6;nS_4?LA)%hNB#$QU$i3 z*t89h%2JyOY_U$<_7KpF-6SlIOqnhTRURCc-9r{-8IF_dWQ)q@#R9$kQwQMdcl#6U z!E>+j&6QWlKea3d9e-L3vV9|!1!&~68i_g;cF->kojgke_%|Wui^O*PejY($T|h8CftL#-7ekga-?=-|^BJU*7i)D|~#t75S92$uh->M4yv^urHIQ zL(kAIeT=VEDs*84=dn+#CR;Pv?)b_2JKrdK;8%zp&G1Tibp_}tsC+&tVMcsxyi%O5 zC$qI99Nw;0!8>!{6U94s)zSKlT~l{YbY1)0g;9A}^Wm0s6suTK1L5or7MXw7d8+fg zmtD{4sm5^(eo{d)I0*Z)7_>If*<1G)^Jy{PoHm9v$f)Po@W)cHQEmMV5~zEy}q9064TPkez>d|r#i14AEoMw+1oRQz+#8|?e&OjRMqwVdD+^~ zfJC0-w$FwB2bO(PpkP1%30LuM+w%sp^)rc5Dd@@zhm(`D4WPvX_5XNW(VZ)m!-v6O z5NI0UJjo(H-|cweNYGxenkkIsiy#yZf#7z_1i9k|`2K=vjg5^9U}~|MO(r{>t3a6B zqF(?7z3pY88)@OptqTDEAW4hiKEJ^r#9ezu1=Q#JGx|FqhOju;3E}vzK=57*jB|`F9bp0~w zELl_$q%}rHM3p0kS|N(~iWs=}BcTLIoluk^Qn|rQKsaf+y^5;~57km4U3%Off&*3Qvfhe^Iw9a3Si?WH zB>Yj6{6PmehDAXhX5K#dL{mOwRRSh8kk!r#Ze!fK3dX_OhosSast)2kFT$Kv^vehL z;=*A~p>t@gL_J|B%B~KX@s(jojjXbaM)BO;91CGf(t&0$7;(CKz1<@w`@Zlc2X6x@ z`}wtg0b0Nk-%8j$U4Bu7#eKRcnzzTO5KSdRVi#nDWq>2v1Y+H=V<#KRCTc(S#ngCj z&dV4jDeD=|!_Lf}#VlKkDJP}XnYcrcp;MNV;j%hox2Qimj!fF#u_7)myv<8t(+uur zsPVF6<|}Tg7xfM!T?sImFqik6aN#S`Ud(_`f18z;g@;yQEB`Tt-nVbe$Z^Kgpu{~W zkzxF75cH;U<3+h^1*~$e30&GAF+cyurZmJo0Uw^zDa8lR3Pa2;~6d5w@z1JL%igRb=Bd7xZ7RYC}VV z9U!w=|Ne071>j7w1IV4Ag$8S$09Q8_|DO-$Us<*Nf2%L(1VwYb?!yTh0mXi;@~3!i zyFh{tYmXCb3}ak?oa*3=wB7-Dewxu^Ow%mFBmpuvfZr%op1Kxbd(GEkf&@q|JjW&u zdnrq^h+5XHTemB=o-?6te(rf&FwXnbvv*S!$^X2}D+hY#o4}AW_V( zi37fLyKR6+&OlejiTp;e`BTHIaW7%ruCh(%y~ezJB|BhMxaEz z)L;~HH8)O(TZolMns@0=Ge#vgd+D3tL`SqePvs8bu~r#xl8_qswZp67 z`b9{Xu8N#C%<;8M?Nebg=}psYhMMN)rC;r5$qt(?X~2npx>3+~@CDG?Q()uWbl1a1 zgUxeQ8kjNW%eJEXcd|!?$E*^b(IId3q(R~6Mjjh~J;A=O9$#{fdGPR?@|2dXA^En~ z*=17lwyHmSTscis&b_!7I8EOh=6STae~OSi7P;s>^XHlpvYvU>4Ozp#F1ckt1WqB6 zuU8!m_upw1Y=ZN+C?3i#9YnBWkDa8Kjn*X_7pJ|J8f22C&q3(I^q3>J?6;o(WTcgO zCc6;xm5cc%7gJ$EBA!StZb^WL&b>!= zGnTxCy!y+gagEK~^WH;+F*=($nnXO+md4;$Y1w3l1Dq` zcWZXUZ1w}KG!wM|1sa{|{%pbVY!YrHZpaqrf0!Jh@7!WofXR-Z@SF5)R3tXs-pmo$36;e!)uo!x{a{az(q2a^e=N17kBFXQBRU+!A>P0><#iLiv>dl$|T$H zA1-OICAJzij)hW&`6sS3tb&?0+F=pU9EJy|g@|}>-e2lwPit==aOO?V2^;iolZZib z$#4CQLdFI8z5zv~@I{tE9=8LEtDC{QkPAwNLB7zOsZROt4>8~lDKR&*vI?rGc&JK@ zkqqA!P8wAa69dztLvH~q!9WxoGU@NJ85^9zZ`nK0{aM7d(%7pf5GW-wBSBTPRpZA& zT+k#-u{kz>8arp;wu=h06|+3=w2munDPWyN~~kc zbaRltX8?q2&4`<-of~{^%XPcG!pNa5#&Id4XZ}Y}Bh6^z5-d$tKrV<|Yyx(lV zW!i)OI%g5Cbs@-1O_&312j157h-)-J4`iQXElof^G$6gMg5*{>r~JH_1GTq{WnBOsn0Ym;eNDDjvwb`+s|TzUg@+VHN_M4TD~O z+v$;{5SOm^XgZ0K@WbJ^4vKk)$x~G$p~rE83|_FJ1zT$$RMekr7-2La;S^Y5qs|EN zN(s~U-L9G8ZjVze`~8JzeLGeCDf(m$QWvy-sjH?s<**vm?em(fMJ3ol`)PgzdCGLH zvAC_<3Sl#N;ky^c`bM07SPc`{^K-s&xM&96zf(o@q!|aSPa5*kh4S{^uIlj|K0cShHeFlOY3KsUE0aHh1pL z&0)EzB9mCy17>%QQ~UgkF%CTk^NhY{-@LwLQPHMrd-sIPv@#>Wb7%)P@hfkSTtDme z`o3*?MBrXUGm?0&4G1aS9oa3h0E59-fih7EuGcRK=jKg?m#OlD?O`DHH`snOIaB^y z*o(7c@o)PVNoyQOd$4V9A6T6~KSn^wxe@P%?ar-1U(PzIKAxMQCUbZ2O>czH&u5*w zo?Pf?_6VF(K^Jx?1VlNa3TLDSR`^wS1J{yY%&))kI5tLFh3%${k-mIYcE2RV*pBm3 zhBb*GD*SWn13u~(3P)6P5fDW*V^Ha3X?s5?mqzK+BQcv*c-(E-z@nxHMNL=j^^RIO z?jyj`nh?mkPlDq&cZZ7r6{5gV3b4fR8d2r=&U&K=*ztVYQGaMwhr*Q=|G?o6KAyjf zw4WWes&>XH|5xlc>PwEA?pG;DDvs4~7@=eR$QdigegM%X!}#~u(#j}IV(0k;(-B=) z#@##L>OR_9FuU3GGdpb7E+a0oO2dBy3V@+C*esv$ z;U;6mQ3mDP$X{02REwOhgCB#n;-{kcRnyjv=vdmOf48WwKzkdjIli~O6%E?B!Qk5t z)yVu~8VO~h*N{X~)anVO&Px_V*y=FlSXUOT=v-e0M;8DB0uVZA{P6{tx`K-|nZh+c zwa$o)W)km&#SRa5gl0>kfm2q((tKyX%j0&shiw^qz+G>i{Ek1}P#0Y~#RW2Ns zoB{g}(i-#%$f;uHi_pZU3si63eXx{{PY^w9kPK~3fO}I~uVDqFZ=c6CS25xWznu4{ z9Q3_7ADBf&zK50x7`o?Jc^W>wp~FtDDF1hE53)f8>bW$jRe>F%Ys5)Q=NdqJStB-? zmgs%7Ss9_VF}&vBfOP-vLt^gJSFJl?V83Mb(Q>lKUGEp;z>z{vC}Y%gz=+E>6qoeJ zq)azSD#|?I#{w{vjo0QV^)bpdD{HAyeA0Iq0 z7p6mBrbmX#!I-&IFyD_41@jLbVktQ;K2St10@O~)Q$JdD+@?&TcosvN{LfzWH0bc! ze?d~CVn=L|fm|41$X^fQ0cW=ze;FtzY3!2zOa=B(RE(;NuQ^k3n=A=wZ^oHIAF4X3 zw7*)lz;T_F>blK=iYzdgAzrw>hbHC7`))>L%#5{+sE9&TQrz~A)etJdjVuBmKU_`u zuRezGZbOd+>j-0+9}-N3k=1}P(h?TK(G=7tZ2`Z>M>EEIfr!IOhBqXJR(R^Xk7E#Z zP#t}-kX7${xLKJxvRLDF@8UC+{ZTy{%VMRr1X*l()8|kyy$Z~*I(QL*r5oI_&pP<~ z8VgM=X)!}TF&QezFRXPhkJwk{DG{%qawx9+i(x?I$$Co_f@vP2TzG9QSoF1vcdrFo zw?mDuX)b@THu-4-s-hm7MZzJtA}%>x%21zmn^DlU8$eQH)Ppi}Z}mqpF<(Zq)hU{| z?4M+h)HHb^km+Ry_De5rm$g8deO6HoL$K<+Y~)3Hz`d~61MliVBh&>|N}Ibscz>dj@<6@t&}aO`sXOXaCe8xO^@}TneaUFA}Jm)ta*<6zejPdK=R@sn->Tb zN!b7(l{=R_e7K-oI0#r3bO9ZWoY{h{lIpiW?Z7F z4*O_g;eae0af3v86c~$$WVWNaUrGsuGEuCU;z@wMY!R@5Y$CrSUp}2tVqjbW*Sh(8 z)OY9xrD!5EQ`q*|Krvvjr)I%t(K# zRh8BC$thJL&CMPM5mZp8`JlMNTjB}C%SWyhIAs%L5fNYWDG*io%(9c$tEs68>zNH4 z%R||oJf9!KZ}#leX#U0+iA7_90@OS}S@@3Eo9zKKS7zflx|vlRNaBLl*0g{eZkl_x zdsjZ47@uuLyNc4RES&h15sT*i49glpeSJckjx#ca*1-^nV4>56Qg|pRs5({#NFwp) zH8vs%+Nle5-V^W1F~$M$3$!>YbE5m9?Ni&HyZpNcAs+5(D6r5N<6Px~hR$_;`;6JK zU=TN%;&avdfD4%{=!ca(7J_A|ic(Skz|oQ(Vc_3P;PJ91A=-FAAHn18D8TC%g=W=} z{UV1G+4>>LVJG*bg7yW)#_9Tu&a%3LE1FRf8nT^W(1>Qt8v<;i!c-Z{Wdt!;Gl}7D z9i%lY=|WS2Z1mb#A!9~i@E#-uIK<7#;kg5dt?`ge*_#(+l_43)@zc19nVg)ur9=*P zY_6vTUG0t(-x3$$VyPg?5@VBHrHT}GG8z;o|9OL51?mGy4BrDReWT76N{Okd0ig<0 z`=qyvN^+vo(vcE7gE)SWh$m1-)@dS4kVif75+XEJS^(|=85I@vbfp#_3r9KY?)?1VY9RH2eIDOd4OS zZPgnU$EBEV?G=dbwvvQcBpsb%3L89u92`+b5_GbCY0k?R zL}MUMfRHgWTiX#p|2rv!0YE}e;4cH=`+9^9gUJM9(~4cGQr!>8>9GB~mY0x|8$ngo zB7`s!77ipxH)&K)GQCgF$e@Ct`7L&?=U_2m{j>~}30gb@E17!ie;!B6{$sdusjvHy^!tENrrtXJfjW;zi+nz& zG?Z-scK-sv8bR9)_n%h{V<8>f@5jg^(=FO~I#&Q;MVwwwYo=x8<$X=&3wDR`;`?Ld zxhO_X0G^5CY1I()&ZuF0!|@eR8Ll^;L|O2ItUOiA0TS>p_#c2!mXR&jooK-QrW`lB zHxPQzFhPfWs6v&&d(aBIKYO5AN4<3vL9e2?tY)1w^`8ereX|}?yTK4tKGSu}hD}*b z4H`&XI4*TGeb?>xZSQAop5K$qZh&~TVC+vo&>d1y!qT*145wJz$i>A)FcKh?E!E(3 zHXqR2gKY;WF*lgXU=@NjPEL4k1OPAx08j_X<{m&$;Fz16pPrrqIt|AtsMD(qrZZTj z%PM$Ees^5dAd@6P(rl2-F}L_k6Z|u80TWl}du4zO11CnT_8bo6^Z_9zjxis=pMV2E zdHR1%f!IBq&KYc$j2!gk2?qYPF+=ya157tiqKOw$E~br&{e- z=4AH_i7z_Q{QKfvpGfGQ$48Cch2#5ix35nI0H*wA^kd>oSZ%}==U>#)7#zOM0Eo;O zP-rxW`_aMp+8IESm+FV{{fNl@d1dV|pkJOQAee(AjCc;&x}P#q&6|dr8u_u#v#X&& z(Q9UA23_iuuJaNwtOF2?uONQKNAbQk$_&%h^BRc%7@pQE7$pfEgTu$yZNxboh~K>g z*j7?uRB+XUjaU=3H?#DzYfZ_*HpM=&mGJKR4O!f^$dTpAie>Q98JNnAel+bF6;8}5m3s_n9Sj*XOrC8fg&asK5W5(0$nb5j9>3?|$cY=Gbf6xvXpnD-L*F{1qvmc){go8V z5pnY|^jas(vs}n-U-2N7c0yV0i8!*rZar0O%@oKQzw0`pSPR0|$Ux6m2~?p^U(i;k z(jV#h`7kW78vD0(yQq9Dckgxza1;?&obPw-z6S{IWdeW~7q^FybpWWz$gsAVfs+T; zEsT8@v$>8V?Cl+v!hoe2)`xWvRC6)C?;ANmLqW?nAPb`OUM(zmYCfJSBJ7QlWGKhcuOQD5aq|lFgDJ%jC`@SKn;L{^A zsu{8T%+(I7gy*N7%_%?Q3BGPR_gm5_gqt9E^}~-XetKeplV{#&}3fvs)xB& z6x3BTPTfNptg&P6H%Q}&6t#xx5s{(h_=KD;wb+n0?p+XC=Hb+Nk6n&G&%1W~=zJCy zY3uwK`HMZ@vGzv>N2VW1rVGDWOD_pW*VM8X3|~YiO!dj1ya{~t?FgOxxbv~ubkn9% z_64!N${>*0nvu26YW?Ut{P zQM3Q$qcPHRSD-&|-d@FzAuN`RYnq!F_@$2ET`S)CgxzW`w~{%(X0uuQ(P^fsvQfVE zDTtab`4}S1+Y~4+DlF4?b(6CHhCF3v78dce98(##@Cuoa6z&kvQ2 zR}7x-{&-INWiSwJ@9eDlRQy6+)_^s?xZQH?7%iH|7b+v8pf2G}d1>W)rx3hFNBGHO z^T9GV6yuv4HdDjYsGQOuFt2*8-hENx21BdnniV)DGZ}D|N<}WpR0E%h8Bj7Fo>5?F zCM+Io2_6^-L;chIz)rlF%Rh?NQ-XfsC71dY{rN#aZC*`5GG0tEstU@{EaCz%{pr%| zd`UstV&b>~@w#Na1^M^*3{2HRHRr}o343GDF|tI3;5B`L9fub75Wf!+fF63+PCT;Q!c8PO3!z!6YWb*`!jr+x*OkS;E? znpz?j9IU3q&FyRYgMMEs9A()VndLeMAv7e^69KLzY=%7p=)_X=+-ps?!PE8f;;r2T z`-l+Kr+GbO>!zHKnm<|~L0`#;v;!Jlfm)T+@xd!hTlH`0B5#+!gRU*2cU5zi?>yLj zh!UimHFVhKp{^APt)FQb_V*7io`+Rlc}qBJXO_8k2Q)bu2f9!Yb*9y=Z9$=|=W?fB zIwPhRpi>w_^XkhmMcXjjyqc1|VY4VJVK>lT#+zAh)p<`XBEVI`(Z|>EeOB4cJua^2 z7j~XRZ;bExoxWEsw!w2nP9qY3T3ba}EBw;oYF%#u%$Vn3T-HUCPB@*YR%CC%D|4p7 z_kx5Pci`+^p!;u4pfapu$0FlQo`g@fK{m$ChXZX~4e2+rHL9 z-qZ}I4D8+M8}Q7gPeb%Osr$SBmoW=xK#2Vr>l8#9T5)DJVR<`*TjNjQ%gb^1PLgyf zw76lmKBuwiN(=GK{r=|oKZ&A^Quhj_>bpF?eL1E;>wD;lCcr??yRPU{roD>p&>7?N zOu^2;`<%Ahczsx-qS0UkwdRic%^(Ym0b#Gp@08i*@hz>0*|QBnA)Dp%VY?{c@wt(wqU=H3nRo!@ z9eBMT5oIZw_xhG3@BfCUzJ1O5ay#p0adIQ(xY$FYd2gogE;da6qeS|&CEhm%pR{-F zsD!AD>vl2G_j31(2uEPBfHH3I@KnUSpf$2mhvG)#X^TiXEBJh21{c5|b2I+sh z_N^>HG0ciksn$_X7zH~Gnq$74jr6^oCVRoumkVvV#sFF?ihMi)of0rb4Z#UqVb42K zw&Z`Ze}+iMG86DW&Co=I~*y#jIV51;;~JDE*GdBOorGW>xb0 zFmTQLcHsGw@i~(TXEpV$oYdU+p%ji#1g9+ak=z%K_cXt*@lFy&Kf_pKko_KUMHPMcr=UHH4PIkjOb$ zK^I~rd2^=yw62dstms^Gh!K0;rW3bI=*ce+nLcSugwZ_AKsc@zSlb!u5vIB{L#50U z_DjKVIQBkVB|WMksyfsI)qloI&`q)bVVOL@LndLUb-FBR@!oRbb7^;=d!}nd%B6!- zM`w~wBJ-#2^iunv>qjs9f5)EmBN|o+RYdEVnb%gAplv=_3%$SV z^RAq=y(Qw|ln(yl-MWO=iXg4l@A^8WQckApXQhA98zTax9b>@ue)hf`(hYT{^`u=h zr(r83T8e?csU50AUeP-pb%G&wzS@=))xPrnt5TK$fvg7RU7RdRa8C)b|9_g^+h?WC zI#iCkP2!8I&lzYzH$$-xM@7gO`g!}=>z@4===0lGIjU(K)v(`iY=(nz?W+e5SUC3X zgwuRe0Z(P$TeDq@2E-~PI+XmS92V(}f5`OM(uO4b0@qCo1WtLgU-gk@+kq=uUh-b> z=Zx+ZO)U=hpyyc$UyEhir%y%MjUaC64M}R_N|aH(B*~^NBNmdbP$i6yv+brDn3Zt| zL=x0euHJiZ)v&)lo3I;`r4<=%WTZ$%spy6)8ZO{)n$?;#`-b<61NRTFEhe4s=>Ha% z(}$TEaVEHk9Wq2tr-q+Y4Xze71j6eE;^VRr>4~QC7h}DWD%)10DwQ4+58y<1{!Gj> z=c4R~qVySd_Q&<0h={bIK3zvgN7-LY#Krj31NvaDh);QEPH71qo7(nIw{$jgg2pfm zeyBE8C)EBf-2wSZc?T53!(ybT*mLEm1qSHLE5KIYNib3Ta`Q3 zL~q?ku?Pd`o$3Y!k2tKLCgC^Z>KhvIou?gP8Bj1*%qYgIN{d}p+q{a#hi#c&P+PSj`f zWstxW8N#urxj$5;J31wIG7^jp}5b%}53M%%fQMYT4LGzfIQc zLGc*Oxz*N9UzYDm>1r?xytJD8X0UpP$r1HdKs-+8bq>x&KHn&jO%glq&VIaikBFAV z;KUb_s~KxpOVMlYnK_HWtp8#I$KyL771g~GsZU;{-}-rJrhPcD^1J_3$)>O0*83j{ z_7pcQi}LV?2KMkObINW#WI2YbD`NLc$@jvU@(^B;n#Bhc5BU-zRpxw15w-a+zn1yv zY@6$hfS&J8KeSRxz9NjOKVc<8-{s9NWI};Ao6z5$66ib2Ww?e;-jaWsYd= zE7D+UUTI0o3QHKj5SMQyPM!uQukUrXF+XR4{;aLx3>q$K z09uNO@Gh(Uet3_wB5-a8qq?~nMp_HtLDALDff6uVG4L^r+3U!NwgdRcM7ZEqwfjcK zyz5DTg+tYD&z$h5IT@i{w=RVEO+l1OUh~pn4Aev%dgGE&dP{~Ox_ZFIjAd-pbhFHg zi!$m-%6C-goChR7=Zaaw!wN_PP;)^T%;bnvUrmZ~cY|0>dcP}fN)itzv}ZpybX3~M zX}3vpgZ#vHB2pE7!pOmp3v#T-ckI5+&)V!jIIh;Qx{LM4xiMdM&%nI886aq)_SbS3>QA=e zXzDIrcEdfo-mD>xUt!Z8BLX~2&~~d&G!=xYd^BAUrfDz}=m3DgoF>C!;>LTMlA`_A zPxafM@Amw>GFqIJ7|P#h2n)>2uY0Z8YlhP1ChU&eqr*5ZN2lwFQc+qLX_zAxXhpaT z%OESX$8&gFKr8v-h*~L7k!z*|)st~kwX$RSt%7qK<*!8UV`POPDdR}fMO6Cl@}fYT zJmRQ6cvxL%GbcdeCDMW=5(We#DmFw0qZavMV&KoTuW%Ql5b9AnoxOh{I@n?X)T?*# zd=lDceM9Vv5c~1zmLgYGa7D+YYo5knINb<7Z$uG{t>}iaP}DJew7A00O$S?Y;bGai zq57XytADDzqOkP(tJu9I#_IG|yMMr}(qpSekBK&gn{m_=Q*T(tZd?$NMiaNZ>>RKA z_Y@Up-d$zVrnM{gRXs=CO#uR*2nw20a>@s0HjuClPIEx~LV4Ic4dmZS_p&}wH+@uz zV>qeNYjS}(e2joD01>z9}+WVB+Ey*?_6@6rtst4Zec<} zAvk!nHUJ;}8#wv1C_l2#7gf|akHrlPam-gFF8r2OWOyy;F4>T@YR58m$#2R)9W5}p zCc@+}N5Z8{u@jKoz_HI`m*J)NYnC9X{ZuYoV!5gQgq8EV?v)sY(6+kc2?QDQ<;TYa z#gya%d1{QHfUcS;D4IJpmU9%>4$AHB#c47`=NdwxlksZ!)ctEqMFdhI^eqW7@Zc8L zd->pVh~+A%{2B#RK@tBfn=9^>*k()jF1lD(mT>#9dKJ_uT%I2e1NC)iKPp8&vxQpj z+~#3Ao-|Lp;Ev;k+S5G*N$l6oDR>mD8&{4?-U8$ZDjh#rZwjtZ;D|$EUpdK&<~Yy& zv>%55c41}Z26|Ilt-c>>Wwk~^>iPVGivZNAQEw84)LSJI~*Twxlw>9G?F z8M}CO{8y>>xQ=TF@)Eu$!_zie7IQulwC{CnPp&yS;(2`v#kEfvBJdBYzKWlLx3_E9 zF``Ckl1uWu+7o;ZA=v&yh%C)Rj-(DOWvL)l@}3~6IK!)5;aPKO{@{( zWb6e03ZK8V3PqSp`1`tJf3XpcZ8zfDSu$?AE3l$r;Jnz}>2>|y`HsV~8YbDgq`xkO zn@hhAtILxaYkoNF>0N?YIdep;bd;AjwetQ~ zTi=;&x@n8kw#>^in2&M0BvqkGD*AY!;w3>(0M-Mv;2X9ZP%7QI$bBTlu0V*(+}XV5 z!v;HHX^7oa`y$SbI^-Uj%OMg4W)3=8s&A-5p6R$xF2y zXONI!uCB_qiYQzex`c=ddc<~^5TGPibHl*PNc)gvQ3x&!M#-^Oq!P5wp;{z1UOdmb z9GJ}HFwqL0FrENgjq}SgvQz|dEY&(9EUxCtMwmPK>Qtjv!c|y~oIXB}NRf-?VbF$1 zbU+OLh=F%CbKOAuGCICS|>rgkfXcJTZ(0+e}@B{7pt%6oXc z)IW(h7~)6VdhCb?lRcnINATQm$|Ua|EiJ6LiKP-kackH{0k;YZw48Ry*|^+R5>Zon zkgIYv!a&hMl^(W$(@20%e4$b@{^`Nr1E?IchyqD)_ZxO-BvuH@!_uOhU?C7N_lAC7 z1A`(|mRd*N_r~0cVdLi)#_Mz_E$1dk@Z_n1R$4Mf6&Iz)3V?jwzvd+Ul$}VBv09Ug z_{XaVJ?sx6M*a04%^1N#BZ$jqMRo16l8eJ+ei^6LG6^>4{|S2%gzXnA)z?5TYRT>E2^x?`DleHSqS7}wo+M~` zd|a;>W}>X$kA>EA@Ly(eT!G(Iy&SRLxCPf{m%iXOqxQFRW(3aeKz+b%5ax7a3Lj@Y zwGykkR%<-hC@1MRnc~+gy)&XOc^-5hy-2N!pUPRtx+?Od zI7lK$^|0&P=AbsCI%1vMGea;jsWZig?e%0Y*Aa_qn{0;wJ)F}{%fL4;mczQN5r6&5 z8vNXDLRLoH-M%F@@Lzim?JM#ea$ZTY!$tP^i6ALfcDWh^2@-iXjfzmyczY2PV&N1u z22!guL4LT+6undB>daTlsu+7RdrFj^$gP+t%PE#jTIabVW|30FzQkU;ZO@`tC=ge$ z1mEHE)W=S?EwH{FjGRl9Y+C+h9&!jyv0z>k#=>X&s+4-H)exfAKXXUN90#Mg;KbWZ zGJHfzN=vP95vl8F3Svbyk=YgZ_*~PkyT*@Z$LuQilr)iR4ZVEiE&`QM8K#Dm>?F*13mz85A zwOik*=LO#pp#K4H2&_h%OeVCnv_L{aLPDZv~0;!*Yr+-^f>Zc?ejZ3&oY8pUibhWO$zh<=v@bNk2dolAF9Yc#s4jB(Wf#W>=bNF6IggXcRD$H?6`I9Vg*+H57LO-EM{5lI z4^G1!*QcXto*&CRq$c%{4ul`_4%(IjVWP1f=D0S-BR(^7;z`MQY*tZ`8J1WK)eRcV z_IO}HZb9$cz+*oH)EHy`orfbTfG@rdi2;>{&fl51@K`ek2Uqkkk2R0;ixBy3_7KVS z@id$YoNl8SpV-_OL%TO3h_Plg!f69)>%#GJq*TWLDk3#c#K$wQa9O~WY81qmUjSNo z+DLB5R_~){{2u1ZoWJPx<4iYgFHko4;ht-XPzj+u)g##Y^LK0tGMW&d6+qC zAYObt2fOB|L_*`!W)=;x{x~MjMebKe?4Tl9Sa# zW>d!>xCr+=SB!lJSVfu@R`tsII8Z&i4TE>_psj_IF~=iLHTGNE%;=M4z{(|IkzGA4 z(*%=C$frP>6N3vBtXV~bmXx_*OvgWN&P2=iE%>!bK~}Dg(?%@D&=a!o(j#SPs9u8m z-izVHgBD?YFWRt6KH0D)ii!*qE<9=x{LLzCnPymN`^Nex>{LEK*NS3NRWmp$n6?Yl zGtEqX5E*g#gba)sVZo0z3jXtA1tLw0@bnTh?z$-h?F*yWeRLuA8DPNP$JPs1zky`Z zn(7FSKR*vgFAL+fZ$g+py#l2!CtBy#;fcu}v>2>-_^m#;azv_V|23aBG9f_OqbhUdvH&P#(VdpcqYy{FuK;Ws!QF8Y>~OGeGG~QS@8D?sARX z6<>b&B~Co?L|l30mDqRReUY7=4I_CUqzhd_LPDZvB$Ao%eX^;k37>xYDJD&t1XE4B zic_vD#8t|+A?YXejiLYN;jRVhWLWfsF%YrcM z6{%P>zYk{Kxe6Cf2*XG>_=4dXIJ(M!QD=H!c9{i%Y-^0-qv@?^f1m*S9AJg^rk`BW z;a;`j?Qtt`#Zx-|ODgRFRVKWA^D10)PX|0E9fJ-_#XJ8iMYg8Ekzv4dS1iF^du8HZ z_vRwf5`|6=pj2ZNkK9pDXePJ#MfZ~en@uivSTJEy4i>&pjU&glkct~cIA%pri3w_01`J8; z82CLTa&z^tni(JStYhB1dAR)Y%TZ8JfUK;nb-!(Eki=I)V#mj}?p0y?BGDL3oD`W@ zhR5SUO-&7Av6x6vDBR&3u^c7xl9p>ko@&IG9u>Q!=_ouQ1MlCEf$PqxMF=W>t&dL- z7!N{;?;tJ9g14S+#+*4Jy#Gul@_AlCawcmQ0|u5k@$8k0Fr~(U&!5l09=RT@TpPoI zC!}NA7ze(%Z!Ipmy92f~3))CMy!o08to*VK?=9AmlSi)K`Vg+TtppF7?7Q7 zK@+*)uX&^tyC_i{|3(lYOAIg04dS08a!GvUF<}Oz7 z{DlRW`a}s*3}K8PwGc*^1x02v$`8-NyyYdxvxRZ%=#{wVttc{cIX@#LBz9VCik$L# zCX-^D?D4Ufj4&9D@$r~+KQ$Gn z8u98qHF$NB2lHnY;pPGts;e=r4S47Oo#l^KVPO~<0z2o|>SgmIckOs*+B zeVhur#f;Xb2)w@rMeq<$^TtR?HDk%b2Kp zkQOx-Patg~Rh8F=0;)*7ot;7I^4rZA@?kRu9F&eX{!@x&3)ez7DVR060hdi|#i$3X zaOdL%`0q!{&=Ru3X@!J@#3qo4G+%#x$FVaKGf74zBsPzoef&0)OqD#nX0s9I&UTr_!b)p7B^3!p6x;KkVyq~}_pa|E~3EP{^~REH3v190;gOPhl1oZhuzuLIVi zO|>B>*N&0`Gg4@uMQKD_Tcc=5vEY<*Q&GLZjK5!)k8f^V3P-950|wa<^^m*VL~0`< zcqOD_?^E;f*$erY`nMH$`WsD*YxTqwyz^E$4$cbUfc+ZaEOQ|}s$$tH4fi}&2;+(l zJn(7|Db%hUik_8iK^`5OEk#3Fu^BZSi zKfgYTm0nUUvyBLa735{`Og_|hF`gC@5)wNFehWF;n7Wy`Np4Nu5}Sr>ONsw4$iTkd z11p$Dqe^7cxWUjOk&IomH7R2)t?}vHOhyG>A}UK`Mhu|Ox8Bo=lddd9+nfM`gH!P3 zta21n!6=cwxwCCJY{W_odaw^BAC-b8uZFum?vJ4tEXThVN6^$}h|d$u^|$d9bfgf5 zfS@OavA3?kK`RI1l}AH3?CN%0d4>ZoT)7%;MpE>6_Pm%b=HVW8awbkUx8sfnTgauZ zBjtz;%zm;2`Mqev3wQf4Y9{c>OXLn$s`-hpqQUx~+m0*3FNil#-t)K7Q8;1U&= zoU$0JRX0WrGhxD64dR$n+hZ{Aoq`XaFGVR&mq+&g@KY5=p0yOtgA4KERT*?F8ZMYn zg&{*$;r!`gygIor%ILtX)*x=YcrCuE4dbq{^_c#0DVi@SL7=V!XN;^xi)ulprPEXi zi5(nUJ7#lJ+i&T^NfM~dqqiUQvdwoMNRs%+qc_?(KCL8Qmptb*=YI_AB5e;7F{^{5 zPa=8kJ{@jS2YFuaD5-XNSw=+t^qCAG5fsf$QJB+6RU{Rz!>_~PBDK(~qBTHu*y8Db znB>_=f#m7_OjZL=!;HoTQUPtGKBfT!PRhjiGt4;jFRReuAh)imle!Vn#jA<%m^?AFhOnx+_Bq;_h-&U$IP_PQvl+f)>m+hFoDVFfZsWef!3$vM|` zL1!B}90oC`xSjrryqQ5&q&6~u$G)om>?M65xu>+e&JtGuw;` zQbE@)3!uSEeU@W{E~lcD*n!YnQ%i1a{U1SCBnjGfA^YZ!ha`sEF1Ds`SiKAc!!ViH zU2g1>CsWDJ&ek~+C@yhwu1W>5XcTt44Q_iTf*KJoJG)#-3MGO@AK~@u>ofPzUkAIO zi4=v@Lnp>`rv^o3`~aX8~+7z0EKIhi?=LmoHiq{@PRm0Y>x@+KE>#2XW&(3TX? zBL>QKH^(T^^3mb@7T8J@tKJ2i=@ZNjxAuTNp_4W0*_~MK4 z+H0@j%{Sk~p8*ceVa3fPa=lBJu7rfd=CLI= Date: Fri, 12 Sep 2025 11:00:36 +0530 Subject: [PATCH 25/29] feat: Added created by in the tags --- infra/main.bicep | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/infra/main.bicep b/infra/main.bicep index 8dea28c5..29dccf3d 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -103,6 +103,21 @@ param enableScalability bool = false @description('Required. Location for AI Foundry deployment. This is the location where the AI Foundry resources will be deployed.') param aiDeploymentsLocation string +@description('Optional created by user name') +param createdBy string = empty(deployer().userPrincipalName) ? '' : split(deployer().userPrincipalName, '@')[0] + +// ========== Resource Group Tag ========== // +resource resourceGroupTags 'Microsoft.Resources/tags@2021-04-01' = { + name: 'default' + properties: { + tags: { + ...tags + TemplateName: 'DKM' + CreatedBy: createdBy + } + } +} + var solutionLocation = empty(location) ? resourceGroup().location : location // @description('Optional. Key vault reference and secret settings for the module\'s secrets export.') From 0cb6a258c2a214be07c295abd7b6407bee12d8b8 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Fri, 12 Sep 2025 12:01:15 +0530 Subject: [PATCH 26/29] docs: Updated deployment guide and information message --- Deployment/resourcedeployment.ps1 | 2 +- docs/DeploymentGuide.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Deployment/resourcedeployment.ps1 b/Deployment/resourcedeployment.ps1 index 94b635b3..a0d03e42 100644 --- a/Deployment/resourcedeployment.ps1 +++ b/Deployment/resourcedeployment.ps1 @@ -826,7 +826,7 @@ try { "2. Check GPT Model's TPM rate in your resource group - $($deploymentResult.ResourceGroupName) `n`r" + "Please set each value high as much as you can set`n`r" + "`t- Open AI Resource Name - $($deploymentResult.AzOpenAiServiceName) `n`r" + - "`t- GPT4o Model - $($deploymentResult.AzGPT4oModelName) `n`r" + + "`t- GPT Model - $($deploymentResult.AzGPT4oModelName) `n`r" + "`t- GPT Embedding Model - $($deploymentResult.AzGPTEmbeddingModelName) `n`r" Write-Host $messageString -ForegroundColor Yellow Write-Host "Don't forget to control the TPM rate for your GPT and Embedding Model in Azure Open AI Studio Deployments section." -ForegroundColor Red diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index eb7246f8..acb6d46f 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -100,8 +100,8 @@ When you start the deployment, most parameters will have **default values**, but | **GPT Model Name** | Specifies the name of the GPT model to be deployed. | gpt-4.1 | | **GPT Model Version** | Version of the GPT model to be used for deployment. | 2024-08-06 | | **GPT Model Capacity** | Sets the GPT model capacity. | 100K | -| **Embedding Model** | Default: **text-embedding-ada-002**. | text-embedding-3-large | -| **Embedding Model Capacity** | Set the capacity for **embedding models** (in thousands). | 200k | +| **Embedding Model** | Sets the embedding model. | text-embedding-3-large | +| **Embedding Model Capacity** | Set the capacity for **embedding models** (in thousands). | 100k | | **Enable Telemetry** | Enables telemetry for monitoring and diagnostics. | true | | **Existing Log Analytics Workspace** | To reuse an existing Log Analytics Workspace ID instead of creating a new one. | *(none)* | From 6a0cce9f9e1bfc18a6778c3ed1c2081097eeabc1 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Fri, 12 Sep 2025 12:13:10 +0530 Subject: [PATCH 27/29] feat: Cleaning up of old code --- Deployment/bicep/azureappconfigservice.bicep | 14 -- Deployment/bicep/azurecognitiveservice.bicep | 20 -- Deployment/bicep/azurecontainerregistry.bicep | 27 --- Deployment/bicep/azurecosmosdb.bicep | 27 --- Deployment/bicep/azurekubernetesservice.bicep | 38 --- Deployment/bicep/azureopenaiservice.bicep | 19 -- .../bicep/azureopenaiservicemodel.bicep | 29 --- Deployment/bicep/azureroleassignacrpull.bicep | 27 --- Deployment/bicep/azuresearch.bicep | 18 -- Deployment/bicep/azurestorageaccount.bicep | 34 --- Deployment/main.bicep | 218 ------------------ 11 files changed, 471 deletions(-) delete mode 100644 Deployment/bicep/azureappconfigservice.bicep delete mode 100644 Deployment/bicep/azurecognitiveservice.bicep delete mode 100644 Deployment/bicep/azurecontainerregistry.bicep delete mode 100644 Deployment/bicep/azurecosmosdb.bicep delete mode 100644 Deployment/bicep/azurekubernetesservice.bicep delete mode 100644 Deployment/bicep/azureopenaiservice.bicep delete mode 100644 Deployment/bicep/azureopenaiservicemodel.bicep delete mode 100644 Deployment/bicep/azureroleassignacrpull.bicep delete mode 100644 Deployment/bicep/azuresearch.bicep delete mode 100644 Deployment/bicep/azurestorageaccount.bicep delete mode 100644 Deployment/main.bicep diff --git a/Deployment/bicep/azureappconfigservice.bicep b/Deployment/bicep/azureappconfigservice.bicep deleted file mode 100644 index 0e5f46ae..00000000 --- a/Deployment/bicep/azureappconfigservice.bicep +++ /dev/null @@ -1,14 +0,0 @@ -param appConfigName string -param location string = resourceGroup().location -param skuName string = 'Standard' - -resource appConfig 'Microsoft.AppConfiguration/configurationStores@2023-03-01' = { - name: appConfigName - location: location - sku: { - name: skuName - } -} - -output appConfigId string = appConfig.id -output appConfigEndpoint string = appConfig.properties.endpoint diff --git a/Deployment/bicep/azurecognitiveservice.bicep b/Deployment/bicep/azurecognitiveservice.bicep deleted file mode 100644 index a1bc5aa2..00000000 --- a/Deployment/bicep/azurecognitiveservice.bicep +++ /dev/null @@ -1,20 +0,0 @@ -param cognitiveServiceName string -param location string = resourceGroup().location - -resource cognitiveService 'Microsoft.CognitiveServices/accounts@2022-03-01' = { - name: cognitiveServiceName - location: location - identity: { - type: 'SystemAssigned' - } - sku: { - name: 's0' - } - kind: 'FormRecognizer' - properties: { - } -} - -output cognitiveServiceId string = cognitiveService.id -output cognitiveServiceName string = cognitiveService.name -output cognitiveServiceEndpoint string = cognitiveService.properties.endpoint diff --git a/Deployment/bicep/azurecontainerregistry.bicep b/Deployment/bicep/azurecontainerregistry.bicep deleted file mode 100644 index fa6abbba..00000000 --- a/Deployment/bicep/azurecontainerregistry.bicep +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - - -param acrName string = 'acr-' - -@description('Provide a location for the registry.') -param location string = resourceGroup().location - -@description('Provide a tier of your Azure Container Registry.') -param acrSku string = 'Basic' - -resource acrResource 'Microsoft.ContainerRegistry/registries@2021-06-01-preview' = { - name: acrName - location: location - sku: { - name: acrSku - } - properties: { - adminUserEnabled: true - } -} - -output createdAcrName string = acrName -output createdAcrId string = acrResource.id -output acrEndpoint string = acrResource.properties.loginServer - diff --git a/Deployment/bicep/azurecosmosdb.bicep b/Deployment/bicep/azurecosmosdb.bicep deleted file mode 100644 index e61380c3..00000000 --- a/Deployment/bicep/azurecosmosdb.bicep +++ /dev/null @@ -1,27 +0,0 @@ -param cosmosDbAccountName string -param location string = resourceGroup().location - -resource cosmosDbAccount 'Microsoft.DocumentDB/databaseAccounts@2024-05-15' = { - name: cosmosDbAccountName - location: location - kind: 'MongoDB' - properties: { - databaseAccountOfferType: 'Standard' - locations: [ - { - locationName: location - failoverPriority: 0 - } - ] - apiProperties: { - serverVersion: '4.2' // Specify the MongoDB server version - } - capabilities: [ - { - name: 'EnableMongo' - } - ] - } -} - -output cosmosDbAccountName string = cosmosDbAccount.name diff --git a/Deployment/bicep/azurekubernetesservice.bicep b/Deployment/bicep/azurekubernetesservice.bicep deleted file mode 100644 index b7079be5..00000000 --- a/Deployment/bicep/azurekubernetesservice.bicep +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -@description('Provide a globally unique name of your Azure kubernetes Cluster') -param aksName string = 'aks-' - -@description('Provide a location for aks.') -param location string = resourceGroup().location - -resource aks 'Microsoft.ContainerService/managedClusters@2024-05-01' = { - name: aksName - location: location - - identity: { - type: 'SystemAssigned' - } - properties: { - dnsPrefix: aksName - enableRBAC: true - kubernetesVersion: '1.30.4' - agentPoolProfiles: [ - { - name: 'agentpool' - count: 2 - vmSize: 'Standard_D4ds_v5' - mode: 'System' - osType: 'Linux' - type: 'VirtualMachineScaleSets' - } - ] - } -} - - - -output createdAksName string = aksName -output createdServicePrincipalId string = aks.identity.principalId -output createdAksId string = aks.id diff --git a/Deployment/bicep/azureopenaiservice.bicep b/Deployment/bicep/azureopenaiservice.bicep deleted file mode 100644 index 3c8a2745..00000000 --- a/Deployment/bicep/azureopenaiservice.bicep +++ /dev/null @@ -1,19 +0,0 @@ -param openAIServiceName string -param location string = resourceGroup().location - -resource openAIService 'Microsoft.CognitiveServices/accounts@2023-05-01' = { - name: openAIServiceName - location: location - kind: 'OpenAI' - sku: { - name: 'S0' - } - properties: { - // Add any specific properties if needed - } -} - -output openAIServiceId string = openAIService.id -output openAIServiceName string = openAIService.name -output openAIServiceEndpoint string = openAIService.properties.endpoint -output oopenAIServiceLocation string = openAIService.location diff --git a/Deployment/bicep/azureopenaiservicemodel.bicep b/Deployment/bicep/azureopenaiservicemodel.bicep deleted file mode 100644 index 74ac1ee6..00000000 --- a/Deployment/bicep/azureopenaiservicemodel.bicep +++ /dev/null @@ -1,29 +0,0 @@ - -param parentResourceName string -param model object -param name string -resource openAIService 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: parentResourceName -} - -resource gpt4Deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = { - parent: openAIService - name: name - sku: { - name: 'GlobalStandard' - capacity: model.capacity - } - properties: { - model: { - format: 'OpenAI' - name: model.name - version: model.version - } - } -} - -output openAIServiceId string = openAIService.id -output deployedModelName string = name -output deployedModelId string = model.name - - diff --git a/Deployment/bicep/azureroleassignacrpull.bicep b/Deployment/bicep/azureroleassignacrpull.bicep deleted file mode 100644 index b79958df..00000000 --- a/Deployment/bicep/azureroleassignacrpull.bicep +++ /dev/null @@ -1,27 +0,0 @@ -@description('Name of the AKS cluster') -param aksName string - -@description('Name of the Azure Container Registry') -param acrName string - -resource aks 'Microsoft.ContainerService/managedClusters@2021-03-01' existing = { - name: aksName - scope: resourceGroup() -} - -resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' existing = { - name: acrName - scope: resourceGroup() -} - -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { - name: guid(aks.id, 'acrpull') - scope: acr - properties: { - roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') // AcrPull role - principalId: aks.identity.principalId - } -} - -output aksName string = aks.name -output acrName string = acr.name diff --git a/Deployment/bicep/azuresearch.bicep b/Deployment/bicep/azuresearch.bicep deleted file mode 100644 index 78c15fe2..00000000 --- a/Deployment/bicep/azuresearch.bicep +++ /dev/null @@ -1,18 +0,0 @@ -param searchServiceName string -param location string - -resource searchService 'Microsoft.Search/searchServices@2023-11-01' = { - name: searchServiceName - location: location - sku: { - name: 'standard' - } - properties: { - replicaCount: 1 - partitionCount: 1 - } -} - -output searchServiceName string = searchService.name -output searchServiceId string = searchService.id - diff --git a/Deployment/bicep/azurestorageaccount.bicep b/Deployment/bicep/azurestorageaccount.bicep deleted file mode 100644 index eebc8023..00000000 --- a/Deployment/bicep/azurestorageaccount.bicep +++ /dev/null @@ -1,34 +0,0 @@ -@description('Name of the storage account') -param storageAccountName string - -@description('Location for the storage account') -param location string = resourceGroup().location - -@description('The SKU of the storage account') -param skuName string = 'Standard_LRS' - -@description('The kind of the storage account') -param kind string = 'StorageV2' - -@allowed([ - 'TLS1_2' - 'TLS1_3' -]) -@description('Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore.') -param minimumTlsVersion string = 'TLS1_2' - -resource storageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = { - name: storageAccountName - location: location - sku: { - name: skuName - } - kind: kind - properties: { - accessTier: 'Hot' - minimumTlsVersion: minimumTlsVersion - } -} - -output storageAccountName string = storageAccount.name -output storageAccountId string = storageAccount.id diff --git a/Deployment/main.bicep b/Deployment/main.bicep deleted file mode 100644 index ca9f4cac..00000000 --- a/Deployment/main.bicep +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -targetScope = 'resourceGroup' - -@minLength(3) -@maxLength(20) -@description('A unique prefix for all resources in this deployment. This should be 3-20 characters long:') -param environmentName string - -@description('The Data Center where the model is deployed.') -param modeldatacenter string - -@description('Azure data center region where resources will be deployed. This should be a valid Azure region, e.g., eastus, westus, etc.') -param location string - -var uniqueId = toLower(uniqueString(subscription().id, environmentName, location)) -var resourceprefix = padLeft(take(uniqueId, 10), 10, '0') -var resourceprefix_name = 'kmgs' - -var resourceGroupLocation = resourceGroup().location - -// Load the abbrevations file required to name the azure resources. -var abbrs = loadJsonContent('./abbreviations.json') - -@description('Optional created by user name') -param createdBy string - -resource resourceGroupTags 'Microsoft.Resources/tags@2021-04-01' = { - name: 'default' - properties: { - tags: { - TemplateName: 'DKM' - CreatedBy: createdBy - } - } -} - -// Create a storage account -module gs_storageaccount 'bicep/azurestorageaccount.bicep' = { - name: '${abbrs.storage.storageAccount}${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - storageAccountName: '${abbrs.storage.storageAccount}${resourceprefix}' - location: resourceGroupLocation - } -} - -// Create a Azure Search Service -module gs_azsearch 'bicep/azuresearch.bicep' = { - name: '${abbrs.ai.aiSearch}${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - searchServiceName: '${abbrs.ai.aiSearch}${resourceprefix}' - location: resourceGroupLocation - } -} - - -// Create Container Registry -module gs_containerregistry 'bicep/azurecontainerregistry.bicep' = { - name: '${abbrs.containers.containerRegistry}${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - acrName: '${abbrs.containers.containerRegistry}${resourceprefix_name}${resourceprefix}' - location: resourceGroupLocation - } -} - -// Create AKS Cluster -module gs_aks 'bicep/azurekubernetesservice.bicep' = { - name: '${abbrs.compute.arcEnabledKubernetesCluster}${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - aksName: '${abbrs.compute.arcEnabledKubernetesCluster}${resourceprefix_name}${resourceprefix}' - location: resourceGroupLocation - } - dependsOn: [ - gs_containerregistry - ] -} - -// Assign ACR Pull role to AKS -// module gs_roleassignacrpull 'bicep/azureroleassignacrpull.bicep' = { -// name: 'assignAcrPullRole' -// scope: gs_resourcegroup -// params: { -// aksName: gs_aks.outputs.createdAksName -// acrName: gs_containerregistry.outputs.createdAcrName -// } -// dependsOn: [ -// gs_aks -// gs_containerregistry -// ] -// } - - -// Create Azure Cognitive Service -module gs_azcognitiveservice 'bicep/azurecognitiveservice.bicep' = { - name: '${abbrs.ai.documentIntelligence}${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - cognitiveServiceName: '${abbrs.ai.documentIntelligence}${resourceprefix_name}${resourceprefix}' - location: 'eastus' - } -} - -// Create Azure Open AI Service -module gs_openaiservice 'bicep/azureopenaiservice.bicep' = { - name: '${abbrs.ai.openAIService}${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - openAIServiceName: '${abbrs.ai.openAIService}${resourceprefix_name}${resourceprefix}' - // GPT-4-32K model & GPT-4o available Data center information. - // https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#gpt-4 - location: modeldatacenter - } -} - -// Due to limited of Quota, not easy to control per each model deployment. -// Set the minimum capacity of each model -// Based on customer's Model capacity, it needs to be updated in Azure Portal. -module gs_openaiservicemodels_gpt4o 'bicep/azureopenaiservicemodel.bicep' = { - scope: resourceGroup() - name: 'gpt-4o-mini' - params: { - parentResourceName: gs_openaiservice.outputs.openAIServiceName - name:'gpt-4o-mini' - model: { - name: 'gpt-4o-mini' - version: '2024-07-18' - raiPolicyName: '' - capacity: 1 - scaleType: 'GlobalStandard' - } - - } - dependsOn: [ - gs_openaiservice - ] -} - -module gs_openaiservicemodels_text_embedding 'bicep/azureopenaiservicemodel.bicep' = { - scope: resourceGroup() - name: 'text-embedding-large' - params: { - parentResourceName: gs_openaiservice.outputs.openAIServiceName - name:'text-embedding-large' - model: { - name: 'text-embedding-3-large' - version: '1' - raiPolicyName: '' - capacity: 1 - scaleType: 'GlobalStandard' - } - } - dependsOn: [ - gs_openaiservicemodels_gpt4o - ] -} - -// Create Azure Cosmos DB Mongo -module gs_cosmosdb 'bicep/azurecosmosdb.bicep' = { - name: '${abbrs.databases.cosmosDBDatabase}${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - cosmosDbAccountName: '${abbrs.databases.cosmosDBDatabase}${resourceprefix_name}${resourceprefix}' - location: resourceGroupLocation - } -} - -// Create Azure App Configuration -module gs_appconfig 'bicep/azureappconfigservice.bicep' = { - name: 'appconfig-${resourceprefix_name}${resourceprefix}' - scope: resourceGroup() - params: { - appConfigName: 'appconfig-${resourceprefix_name}${resourceprefix}' - location: resourceGroupLocation - } -} - -// return all resource names as a output -// output gs_resourcegroup_name string = '${abbrs.managementGovernance.resourceGroup}${resourceprefix_name}${resourceprefix}' -output gs_resourcegroup_name string = resourceGroup().name -output gs_solution_prefix string = '${resourceprefix_name}${resourceprefix}' -output gs_storageaccount_name string = gs_storageaccount.outputs.storageAccountName -output gs_azsearch_name string = gs_azsearch.outputs.searchServiceName - -output gs_aks_name string = gs_aks.outputs.createdAksName -output gs_aks_serviceprincipal_id string = gs_aks.outputs.createdServicePrincipalId - -output gs_containerregistry_name string = gs_containerregistry.outputs.createdAcrName - -output gs_azcognitiveservice_name string = gs_azcognitiveservice.outputs.cognitiveServiceName -output gs_azcognitiveservice_endpoint string = gs_azcognitiveservice.outputs.cognitiveServiceEndpoint - -output gs_openaiservice_name string = gs_openaiservice.outputs.openAIServiceName -output gs_openaiservice_location string = gs_openaiservice.outputs.oopenAIServiceLocation -output gs_openaiservice_endpoint string = gs_openaiservice.outputs.openAIServiceEndpoint - -output gs_openaiservicemodels_gpt4o_model_name string = gs_openaiservicemodels_gpt4o.outputs.deployedModelName -output gs_openaiservicemodels_gpt4o_model_id string = gs_openaiservicemodels_gpt4o.outputs.deployedModelId - -output gs_openaiservicemodels_text_embedding_model_name string = gs_openaiservicemodels_text_embedding.outputs.deployedModelName -output gs_openaiservicemodels_text_embedding_model_id string = gs_openaiservicemodels_text_embedding.outputs.deployedModelId -output gs_cosmosdb_name string = gs_cosmosdb.outputs.cosmosDbAccountName - -output gs_appconfig_id string = gs_appconfig.outputs.appConfigId -output gs_appconfig_endpoint string = gs_appconfig.outputs.appConfigEndpoint - -// return acr url -output gs_containerregistry_endpoint string = gs_containerregistry.outputs.acrEndpoint - -//return resourcegroup resource ID -output gs_resourcegroup_id string = resourceGroup().id - -output createdByOutput string = createdBy - From 37ac9cbc5c8aa06616f0714af0103c330ea949b8 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Fri, 12 Sep 2025 12:15:54 +0530 Subject: [PATCH 28/29] fix: Added Metadata in azure.yaml file --- azure.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure.yaml b/azure.yaml index 73c9ac79..5a60240f 100644 --- a/azure.yaml +++ b/azure.yaml @@ -5,6 +5,6 @@ name: document-knowledge-mining-solution-accelerator requiredVersions: azd: ">= 1.15.0" -# metadata: -# template: document-knowledge-mining-solution-accelerator@1.0 -# name: document-knowledge-mining-solution-accelerator@1.0 +metadata: + template: document-knowledge-mining-solution-accelerator@1.0 + name: document-knowledge-mining-solution-accelerator@1.0 From 970a8ddfa39e568663eb91d08a454e32538fd902 Mon Sep 17 00:00:00 2001 From: Prajwal D C Date: Fri, 12 Sep 2025 12:56:46 +0530 Subject: [PATCH 29/29] docs: Updated deployment guide --- docs/DeploymentGuide.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index acb6d46f..f219131f 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -109,7 +109,7 @@ When you start the deployment, most parameters will have **default values**, but ### Deploying with AZD -Once you've opened the project in [Codespaces](#github-codespaces), [Dev Containers](#vs-code-dev-containers), or [locally](#local-environment), you can deploy it to Azure by following these steps: +Once you've opened the project [locally](#local-environment), you can deploy it to Azure by following these steps: 1. Clone the repository or download the project code via command-line: @@ -180,7 +180,9 @@ You will be prompted for the following parameters with this Screen : 1. **Email** - used for issuing certificates in Kubernetes clusters from the [Let's Encrypt](https://letsencrypt.org/) service. Email address should be valid. + 2. You will be prompted to Login, Select a account and proceed to Login. + 3. **GO !** - Post Deployment Script executes Azure Infrastructure configuration, Application code compile and publish into Kubernetes Cluster. ### Manual Deployment Steps: