diff --git a/infra/main.json b/infra/main.json index 9e1a8ac8..8ea6b044 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.37.4.10188", - "templateHash": "10857935115773987076" + "templateHash": "9657471769788917227" }, "name": "Modernize Your Code Solution Accelerator", "description": "CSA CTO Gold Standard Solution Accelerator for Modernize Your Code. \r\n" @@ -20580,7 +20580,7 @@ "_generator": { "name": "bicep", "version": "0.37.4.10188", - "templateHash": "10914722088267065272" + "templateHash": "18113126559107540948" }, "name": "AI Services and Project Module", "description": "This module creates an AI Services resource and an AI Foundry project within it. It supports private networking, OpenAI deployments, and role assignments." @@ -21774,7 +21774,8 @@ "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())]", "useExistingService": "[not(empty(parameters('existingFoundryProjectResourceId')))]", - "existingCognitiveServiceDetails": "[split(parameters('existingFoundryProjectResourceId'), '/')]" + "existingCognitiveServiceDetails": "[split(parameters('existingFoundryProjectResourceId'), '/')]", + "shouldCreatePrivateEndpoint": "[and(variables('useExistingService'), not(equals(parameters('privateNetworking'), null())))]" }, "resources": { "cMKKeyVault::cMKKey": { @@ -21867,7 +21868,7 @@ "name": "[variables('existingCognitiveServiceDetails')[8]]" }, "cognitiveServicesPrivateDnsZone": { - "condition": "[and(and(not(variables('useExistingService')), not(equals(parameters('privateNetworking'), null()))), empty(tryGet(parameters('privateNetworking'), 'cogServicesPrivateDnsZoneResourceId')))]", + "condition": "[and(and(variables('useExistingService'), not(equals(parameters('privateNetworking'), null()))), empty(tryGet(parameters('privateNetworking'), 'cogServicesPrivateDnsZoneResourceId')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[take(format('{0}-cognitiveservices-pdns-deployment', parameters('name')), 64)]", @@ -21978,7 +21979,7 @@ } }, "openAiPrivateDnsZone": { - "condition": "[and(and(not(variables('useExistingService')), not(equals(parameters('privateNetworking'), null()))), empty(tryGet(parameters('privateNetworking'), 'openAIPrivateDnsZoneResourceId')))]", + "condition": "[and(and(variables('useExistingService'), not(equals(parameters('privateNetworking'), null()))), empty(tryGet(parameters('privateNetworking'), 'openAIPrivateDnsZoneResourceId')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[take(format('{0}-openai-pdns-deployment', parameters('name')), 64)]", @@ -22089,7 +22090,7 @@ } }, "aiServicesPrivateDnsZone": { - "condition": "[and(and(not(variables('useExistingService')), not(equals(parameters('privateNetworking'), null()))), empty(tryGet(parameters('privateNetworking'), 'aiServicesPrivateDnsZoneResourceId')))]", + "condition": "[and(and(variables('useExistingService'), not(equals(parameters('privateNetworking'), null()))), empty(tryGet(parameters('privateNetworking'), 'aiServicesPrivateDnsZoneResourceId')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[take(format('{0}-ai-services-pdns-deployment', parameters('name')), 64)]", @@ -26551,6 +26552,844 @@ } } } + }, + "existingAiServicesPrivateEndpoint": { + "condition": "[variables('shouldCreatePrivateEndpoint')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('module.proj-private-endpoint.{0}', variables('existingCognitiveServiceDetails')[8]), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "isPrivate": "[if(equals(reference('cognitiveServiceExisting').publicNetworkAccess, 'Enabled'), createObject('value', false()), createObject('value', true()))]", + "aiServicesName": { + "value": "[variables('existingCognitiveServiceDetails')[8]]" + }, + "subnetResourceId": { + "value": "[coalesce(tryGet(parameters('privateNetworking'), 'subnetResourceId'), '')]" + }, + "aiServicesId": { + "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('existingCognitiveServiceDetails')[2], variables('existingCognitiveServiceDetails')[4]), 'Microsoft.CognitiveServices/accounts', variables('existingCognitiveServiceDetails')[8])]" + }, + "location": { + "value": "[parameters('location')]" + }, + "cognitiveServicesDnsZoneId": "[if(not(equals(parameters('privateNetworking'), null())), if(empty(tryGet(parameters('privateNetworking'), 'cogServicesPrivateDnsZoneResourceId')), createObject('value', coalesce(reference('cognitiveServicesPrivateDnsZone').outputs.resourceId.value, '')), createObject('value', tryGet(parameters('privateNetworking'), 'cogServicesPrivateDnsZoneResourceId'))), createObject('value', ''))]", + "openAiDnsZoneId": "[if(not(equals(parameters('privateNetworking'), null())), if(empty(tryGet(parameters('privateNetworking'), 'openAIPrivateDnsZoneResourceId')), createObject('value', coalesce(reference('openAiPrivateDnsZone').outputs.resourceId.value, '')), createObject('value', tryGet(parameters('privateNetworking'), 'openAIPrivateDnsZoneResourceId'))), createObject('value', ''))]", + "aiServicesDnsZoneId": "[if(not(equals(parameters('privateNetworking'), null())), if(empty(tryGet(parameters('privateNetworking'), 'aiServicesPrivateDnsZoneResourceId')), createObject('value', coalesce(reference('aiServicesPrivateDnsZone').outputs.resourceId.value, '')), createObject('value', tryGet(parameters('privateNetworking'), 'aiServicesPrivateDnsZoneResourceId'))), createObject('value', ''))]", + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.37.4.10188", + "templateHash": "12552733327728044777" + } + }, + "parameters": { + "aiServicesName": { + "type": "string" + }, + "aiServicesId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "subnetResourceId": { + "type": "string" + }, + "cognitiveServicesDnsZoneId": { + "type": "string" + }, + "openAiDnsZoneId": { + "type": "string" + }, + "aiServicesDnsZoneId": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "isPrivate": { + "type": "bool" + } + }, + "resources": [ + { + "condition": "[parameters('isPrivate')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('module.private-endpoint.{0}', parameters('aiServicesName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('pep-{0}', parameters('aiServicesName'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "subnetResourceId": { + "value": "[parameters('subnetResourceId')]" + }, + "customNetworkInterfaceName": { + "value": "[format('nic-{0}', parameters('aiServicesName'))]" + }, + "privateDnsZoneGroup": { + "value": { + "privateDnsZoneGroupConfigs": [ + { + "name": "ai-services-dns-zone-cognitiveservices", + "privateDnsZoneResourceId": "[parameters('cognitiveServicesDnsZoneId')]" + }, + { + "name": "ai-services-dns-zone-openai", + "privateDnsZoneResourceId": "[parameters('openAiDnsZoneId')]" + }, + { + "name": "ai-services-dns-zone-aiservices", + "privateDnsZoneResourceId": "[parameters('aiServicesDnsZoneId')]" + } + ] + } + }, + "privateLinkServiceConnections": { + "value": [ + { + "name": "[format('pep-{0}', parameters('aiServicesName'))]", + "properties": { + "groupIds": [ + "account" + ], + "privateLinkServiceId": "[parameters('aiServicesId')]" + } + } + ] + }, + "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.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": [ + "aiServicesPrivateDnsZone", + "cognitiveServiceExisting", + "cognitiveServicesPrivateDnsZone", + "openAiPrivateDnsZone" + ] } }, "outputs": { @@ -36651,7 +37490,7 @@ "_generator": { "name": "bicep", "version": "0.37.4.10188", - "templateHash": "14888974788316430208" + "templateHash": "6565162135330787654" } }, "definitions": { @@ -37005,7 +37844,7 @@ "value": false }, "disableLocalAuthentication": { - "value": "[not(equals(parameters('privateNetworking'), null()))]" + "value": true }, "diagnosticSettings": "[if(not(empty(parameters('logAnalyticsWorkspaceResourceId'))), createObject('value', createArray(createObject('workspaceResourceId', parameters('logAnalyticsWorkspaceResourceId')))), createObject('value', createArray()))]", "privateEndpoints": "[if(not(equals(parameters('privateNetworking'), null())), createObject('value', createArray(createObject('name', format('pep-{0}', parameters('name')), 'customNetworkInterfaceName', format('nic-{0}', parameters('name')), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', createArray(createObject('privateDnsZoneResourceId', if(not(equals(parameters('privateNetworking'), null())), if(empty(tryGet(parameters('privateNetworking'), 'privateDnsZoneResourceId')), coalesce(reference('privateDnsZone').outputs.resourceId.value, ''), coalesce(tryGet(parameters('privateNetworking'), 'privateDnsZoneResourceId'), '')), '')))), 'service', 'Sql', 'subnetResourceId', coalesce(tryGet(parameters('privateNetworking'), 'subnetResourceId'), '')))), createObject('value', createArray()))]", diff --git a/infra/modules/ai-foundry/aifoundry.bicep b/infra/modules/ai-foundry/aifoundry.bicep index 89dfe2d3..7b12cf1a 100644 --- a/infra/modules/ai-foundry/aifoundry.bicep +++ b/infra/modules/ai-foundry/aifoundry.bicep @@ -198,7 +198,7 @@ resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentiti var useExistingService = !empty(existingFoundryProjectResourceId) -module cognitiveServicesPrivateDnsZone '../privateDnsZone.bicep' = if (!useExistingService && privateNetworking != null && empty(privateNetworking.?cogServicesPrivateDnsZoneResourceId)) { +module cognitiveServicesPrivateDnsZone '../privateDnsZone.bicep' = if (privateNetworking != null && empty(privateNetworking.?cogServicesPrivateDnsZoneResourceId)) { name: take('${name}-cognitiveservices-pdns-deployment', 64) params: { name: 'privatelink.cognitiveservices.${toLower(environment().name) == 'azureusgovernment' ? 'azure.us' : 'azure.com'}' @@ -207,7 +207,7 @@ module cognitiveServicesPrivateDnsZone '../privateDnsZone.bicep' = if (!useExist } } -module openAiPrivateDnsZone '../privateDnsZone.bicep' = if (!useExistingService && privateNetworking != null && empty(privateNetworking.?openAIPrivateDnsZoneResourceId)) { +module openAiPrivateDnsZone '../privateDnsZone.bicep' = if (privateNetworking != null && empty(privateNetworking.?openAIPrivateDnsZoneResourceId)) { name: take('${name}-openai-pdns-deployment', 64) params: { name: 'privatelink.openai.${toLower(environment().name) == 'azureusgovernment' ? 'azure.us' : 'azure.com'}' @@ -216,7 +216,7 @@ module openAiPrivateDnsZone '../privateDnsZone.bicep' = if (!useExistingService } } -module aiServicesPrivateDnsZone '../privateDnsZone.bicep' = if (!useExistingService && privateNetworking != null && empty(privateNetworking.?aiServicesPrivateDnsZoneResourceId)) { +module aiServicesPrivateDnsZone '../privateDnsZone.bicep' = if (privateNetworking != null && empty(privateNetworking.?aiServicesPrivateDnsZoneResourceId)) { name: take('${name}-ai-services-pdns-deployment', 64) params: { name: 'privatelink.services.ai.${toLower(environment().name) == 'azureusgovernment' ? 'azure.us' : 'azure.com'}' @@ -225,22 +225,17 @@ module aiServicesPrivateDnsZone '../privateDnsZone.bicep' = if (!useExistingServ } } -var cogServicesPrivateDnsZoneResourceId = privateNetworking != null - ? (empty(privateNetworking.?cogServicesPrivateDnsZoneResourceId) - ? cognitiveServicesPrivateDnsZone.outputs.resourceId ?? '' - : privateNetworking.?cogServicesPrivateDnsZoneResourceId) - : '' -var openAIPrivateDnsZoneResourceId = privateNetworking != null - ? (empty(privateNetworking.?openAIPrivateDnsZoneResourceId) - ? openAiPrivateDnsZone.outputs.resourceId ?? '' - : privateNetworking.?openAIPrivateDnsZoneResourceId) - : '' - -var aiServicesPrivateDnsZoneResourceId = privateNetworking != null - ? (empty(privateNetworking.?aiServicesPrivateDnsZoneResourceId) - ? aiServicesPrivateDnsZone.outputs.resourceId ?? '' - : privateNetworking.?aiServicesPrivateDnsZoneResourceId) - : '' +var cogServicesPrivateDnsZoneResourceId = privateNetworking != null && empty(privateNetworking.?cogServicesPrivateDnsZoneResourceId) + ? cognitiveServicesPrivateDnsZone.outputs.resourceId ?? '' + : (privateNetworking.?cogServicesPrivateDnsZoneResourceId ?? '') + +var openAIPrivateDnsZoneResourceId = privateNetworking != null && empty(privateNetworking.?openAIPrivateDnsZoneResourceId) + ? openAiPrivateDnsZone.outputs.resourceId ?? '' + : (privateNetworking.?openAIPrivateDnsZoneResourceId ?? '') + +var aiServicesPrivateDnsZoneResourceId = privateNetworking != null && empty(privateNetworking.?aiServicesPrivateDnsZoneResourceId) + ? aiServicesPrivateDnsZone.outputs.resourceId ?? '' + : (privateNetworking.?aiServicesPrivateDnsZoneResourceId ?? '') resource cognitiveServiceNew 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = if(!useExistingService) { name: name @@ -361,6 +356,26 @@ module existing_cognitive_service_dependencies './dependencies.bicep' = if(useEx scope: resourceGroup(existingCognitiveServiceDetails[2], existingCognitiveServiceDetails[4]) } +var shouldCreatePrivateEndpoint = useExistingService && privateNetworking != null +var isProjectPrivate = cognitiveServiceExisting!.properties.publicNetworkAccess == 'Enabled' ? false : true +module existingAiServicesPrivateEndpoint './existing-aif-private-endpoint.bicep' = if (shouldCreatePrivateEndpoint){ + name: take('module.proj-private-endpoint.${cognitiveServiceExisting.name}', 64) + params: { + isPrivate: isProjectPrivate + aiServicesName: cognitiveServiceExisting.name + subnetResourceId: privateNetworking.?subnetResourceId ?? '' + aiServicesId: cognitiveServiceExisting.id + location: location + cognitiveServicesDnsZoneId: cogServicesPrivateDnsZoneResourceId + openAiDnsZoneId: openAIPrivateDnsZoneResourceId + aiServicesDnsZoneId: aiServicesPrivateDnsZoneResourceId + tags: tags + } + dependsOn:[ + cognitiveServiceExisting + ] +} + // module cognitiveService 'ai-services.bicep' = { // name: take('${name}-aiservices-deployment', 64) // #disable-next-line no-unnecessary-dependson diff --git a/infra/modules/ai-foundry/existing-aif-private-endpoint.bicep b/infra/modules/ai-foundry/existing-aif-private-endpoint.bicep new file mode 100644 index 00000000..86885570 --- /dev/null +++ b/infra/modules/ai-foundry/existing-aif-private-endpoint.bicep @@ -0,0 +1,45 @@ +param aiServicesName string +param aiServicesId string +param location string +param subnetResourceId string +param cognitiveServicesDnsZoneId string +param openAiDnsZoneId string +param aiServicesDnsZoneId string +param tags object +param isPrivate bool + +module privateEndpoint 'br/public:avm/res/network/private-endpoint:0.11.0' = if (isPrivate) { + name: take('module.private-endpoint.${aiServicesName}', 64) + params: { + name: 'pep-${aiServicesName}' + location: location + subnetResourceId: subnetResourceId + customNetworkInterfaceName: 'nic-${aiServicesName}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'ai-services-dns-zone-cognitiveservices' + privateDnsZoneResourceId: cognitiveServicesDnsZoneId + } + { + name: 'ai-services-dns-zone-openai' + privateDnsZoneResourceId: openAiDnsZoneId + } + { + name: 'ai-services-dns-zone-aiservices' + privateDnsZoneResourceId: aiServicesDnsZoneId + } + ] + } + privateLinkServiceConnections: [ + { + name: 'pep-${aiServicesName}' + properties: { + groupIds: ['account'] + privateLinkServiceId: aiServicesId + } + } + ] + tags: tags + } +} \ No newline at end of file