From 9a52728dfff194c8dfa677e3ac6e6ad5bd33425e Mon Sep 17 00:00:00 2001 From: Ajit Padhi Date: Mon, 29 Sep 2025 18:32:34 +0530 Subject: [PATCH] added dns for existing project to access --- infra/main.bicep | 43 +- infra/main.json | 775 ++++++++++++++++++++- infra/main.parameters.json | 2 +- infra/main.waf.parameters.json | 4 +- infra/scripts/process_sample_data.sh | 789 ++++++++++------------ infra/scripts/run_create_index_scripts.sh | 4 +- 6 files changed, 1189 insertions(+), 428 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index 1fb0f6a5b..86f65b30d 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -449,7 +449,7 @@ var aiRelatedDnsZoneIndices = [ // =================================================== @batchSize(5) module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.7.1' = [ - for (zone, i) in privateDnsZones: if (enablePrivateNetworking && (empty(existingFoundryProjectResourceId) || !contains(aiRelatedDnsZoneIndices, i))) { + for (zone, i) in privateDnsZones: if (enablePrivateNetworking) { name: 'dns-zone-${i}' params: { name: zone @@ -1070,6 +1070,47 @@ resource existingAiFoundryAiServicesProject 'Microsoft.CognitiveServices/account parent: existingAiFoundryAiServices } +// ========== Private Endpoint for Existing AI Services ========== // +var shouldCreatePrivateEndpoint = useExistingAiFoundryAiProject && enablePrivateNetworking +module existingAiServicesPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.11.0' = if (shouldCreatePrivateEndpoint) { + name: take('module.private-endpoint.${existingAiFoundryAiServices.name}', 64) + params: { + name: 'pep-${existingAiFoundryAiServices.name}' + location: location + subnetResourceId: network!.outputs.subnetPrivateEndpointsResourceId + customNetworkInterfaceName: 'nic-${existingAiFoundryAiServices.name}' + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + name: 'ai-services-dns-zone-cognitiveservices' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.cognitiveServices]!.outputs.resourceId + } + { + name: 'ai-services-dns-zone-openai' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.openAI]!.outputs.resourceId + } + { + name: 'ai-services-dns-zone-aiservices' + privateDnsZoneResourceId: avmPrivateDnsZones[dnsZoneIndex.aiServices]!.outputs.resourceId + } + ] + } + privateLinkServiceConnections: [ + { + name: 'pep-${existingAiFoundryAiServices.name}' + properties: { + groupIds: ['account'] + privateLinkServiceId: existingAiFoundryAiServices.id + } + } + ] + tags: tags + } + dependsOn: [ + existingAiFoundryAiServices + avmPrivateDnsZones + ] +} var aiSearchName = 'srch-${solutionSuffix}' module searchService 'br/public:avm/res/search/search-service:0.11.1' = { diff --git a/infra/main.json b/infra/main.json index 7976c4482..8dc0dd46b 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.37.4.10188", - "templateHash": "14524300692672359611" + "templateHash": "10069158074656623271" } }, "parameters": { @@ -258,9 +258,9 @@ }, "createdBy": { "type": "string", - "defaultValue": "[if(empty(deployer().userPrincipalName), '', split(deployer().userPrincipalName, '@')[0])]", + "defaultValue": "[if(contains(deployer(), 'userPrincipalName'), split(deployer().userPrincipalName, '@')[0], deployer().objectId)]", "metadata": { - "description": "Optional created by user name" + "description": "Tag, Created by user name" } } }, @@ -393,6 +393,7 @@ "sqlDbName": "[format('sqldb-{0}', variables('solutionSuffix'))]", "webServerFarmResourceName": "[format('asp-{0}', variables('solutionSuffix'))]", "webSiteResourceName": "[format('app-{0}', variables('solutionSuffix'))]", + "shouldCreatePrivateEndpoint": "[and(variables('useExistingAiFoundryAiProject'), parameters('enablePrivateNetworking'))]", "aiSearchName": "[format('srch-{0}', variables('solutionSuffix'))]" }, "resources": { @@ -410,7 +411,7 @@ "apiVersion": "2021-04-01", "name": "default", "properties": { - "tags": "[shallowMerge(createArray(parameters('tags'), createObject('TemplateName', 'Client Advisor', 'CreatedBy', parameters('createdBy'))))]" + "tags": "[shallowMerge(createArray(parameters('tags'), createObject('TemplateName', 'Client Advisor', 'Type', if(parameters('enablePrivateNetworking'), 'WAF', 'Non-WAF'), 'CreatedBy', parameters('createdBy'))))]" } }, "existingAiFoundryAiServices": { @@ -20530,7 +20531,7 @@ "mode": "serial", "batchSize": 5 }, - "condition": "[and(parameters('enablePrivateNetworking'), or(empty(parameters('existingFoundryProjectResourceId')), not(contains(variables('aiRelatedDnsZoneIndices'), copyIndex()))))]", + "condition": "[parameters('enablePrivateNetworking')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('dns-zone-{0}', copyIndex())]", @@ -32702,8 +32703,8 @@ } }, "dependsOn": [ - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]", "logAnalyticsWorkspace", "network", @@ -54794,6 +54795,768 @@ "webServerFarm" ] }, + "existingAiServicesPrivateEndpoint": { + "condition": "[variables('shouldCreatePrivateEndpoint')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[take(format('module.private-endpoint.{0}', variables('aiFoundryAiServicesResourceName')), 64)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('pep-{0}', variables('aiFoundryAiServicesResourceName'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "subnetResourceId": { + "value": "[reference('network').outputs.subnetPrivateEndpointsResourceId.value]" + }, + "customNetworkInterfaceName": { + "value": "[format('nic-{0}', variables('aiFoundryAiServicesResourceName'))]" + }, + "privateDnsZoneGroup": { + "value": { + "privateDnsZoneGroupConfigs": [ + { + "name": "ai-services-dns-zone-cognitiveservices", + "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)).outputs.resourceId.value]" + }, + { + "name": "ai-services-dns-zone-openai", + "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)).outputs.resourceId.value]" + }, + { + "name": "ai-services-dns-zone-aiservices", + "privateDnsZoneResourceId": "[reference(format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)).outputs.resourceId.value]" + } + ] + } + }, + "privateLinkServiceConnections": { + "value": [ + { + "name": "[format('pep-{0}', variables('aiFoundryAiServicesResourceName'))]", + "properties": { + "groupIds": [ + "account" + ], + "privateLinkServiceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('aiFoundryAiServicesSubscriptionId'), variables('aiFoundryAiServicesResourceGroupName')), 'Microsoft.CognitiveServices/accounts', variables('aiFoundryAiServicesResourceName'))]" + } + } + ] + }, + "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": [ + "avmPrivateDnsZones", + "existingAiFoundryAiServices", + "network" + ] + }, "searchService": { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", diff --git a/infra/main.parameters.json b/infra/main.parameters.json index 092e5a315..bc3f7622f 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -17,7 +17,7 @@ "azureOpenaiAPIVersion": { "value": "${AZURE_ENV_MODEL_VERSION}" }, - "gptDeploymentCapacity": { + "gptModelCapacity": { "value": "${AZURE_ENV_MODEL_CAPACITY}" }, "embeddingModel": { diff --git a/infra/main.waf.parameters.json b/infra/main.waf.parameters.json index fcdb73193..291679d7a 100644 --- a/infra/main.waf.parameters.json +++ b/infra/main.waf.parameters.json @@ -8,7 +8,7 @@ "cosmosLocation": { "value": "${AZURE_ENV_COSMOS_LOCATION}" }, - "deploymentType": { + "gptModelDeploymentType": { "value": "${AZURE_ENV_MODEL_DEPLOYMENT_TYPE}" }, "gptModelName": { @@ -17,7 +17,7 @@ "azureOpenaiAPIVersion": { "value": "${AZURE_ENV_MODEL_VERSION}" }, - "gptDeploymentCapacity": { + "gptModelCapacity": { "value": "${AZURE_ENV_MODEL_CAPACITY}" }, "embeddingModel": { diff --git a/infra/scripts/process_sample_data.sh b/infra/scripts/process_sample_data.sh index c0b9f8a34..0f22bc223 100644 --- a/infra/scripts/process_sample_data.sh +++ b/infra/scripts/process_sample_data.sh @@ -1,451 +1,408 @@ - #!/bin/bash - - # Variables - resourceGroupName="$1" - cosmosDbAccountName="$2" - storageAccount="$3" - fileSystem="$4" - keyvaultName="$5" - sqlServerName="$6" - SqlDatabaseName="$7" - webAppManagedIdentityClientId="$8" - webAppManagedIdentityDisplayName="$9" - aiSearchName="${10}" - aif_resource_id="${11}" - - # Global variables to track original network access states - original_storage_public_access="" - original_storage_default_action="" - original_foundry_public_access="" - aif_resource_group="" - aif_account_resource_id="" - # Add global variable for SQL Server public access - original_sql_public_access="" - created_sql_allow_all_firewall_rule="false" - original_full_range_rule_present="false" - - # Function to enable public network access temporarily - enable_public_access() { - echo "=== Temporarily enabling public network access for services ===" - - # Enable public access for Storage Account - echo "Enabling public access for Storage Account: $storageAccount" - original_storage_public_access=$(az storage account show \ - --name "$storageAccount" \ - --resource-group "$resourceGroupName" \ - --query "publicNetworkAccess" \ - -o tsv) - original_storage_default_action=$(az storage account show \ +#!/bin/bash + +# Variables +resourceGroupName="$1" +cosmosDbAccountName="$2" +storageAccount="$3" +fileSystem="$4" +keyvaultName="$5" +sqlServerName="$6" +SqlDatabaseName="$7" +webAppManagedIdentityClientId="$8" +webAppManagedIdentityDisplayName="$9" +aiSearchName="${10}" +aif_resource_id="${11}" + +# Global variables to track original network access states +original_storage_public_access="" +original_keyvault_public_access="" +original_foundry_public_access="" +aif_resource_group="" +aif_account_resource_id="" +aif_subscription_id="" +# Add global variable for SQL Server public access +original_sql_public_access="" + +# Function to enable public network access temporarily +enable_public_access() { + echo "=== Temporarily enabling public network access for services ===" + + # Enable public access for Storage Account + echo "Enabling public access for Storage Account: $storageAccount" + original_storage_public_access=$(az storage account show \ + --name "$storageAccount" \ + --resource-group "$resourceGroupName" \ + --query "publicNetworkAccess" \ + -o tsv) + + if [ "$original_storage_public_access" != "Enabled" ]; then + az storage account update \ --name "$storageAccount" \ --resource-group "$resourceGroupName" \ - --query "networkRuleSet.defaultAction" \ - -o tsv) - - if [ "$original_storage_public_access" != "Enabled" ]; then - az storage account update \ - --name "$storageAccount" \ - --resource-group "$resourceGroupName" \ - --public-network-access Enabled \ - --output none - if [ $? -eq 0 ]; then - echo "✓ Storage Account public access enabled" - else - echo "✗ Failed to enable Storage Account public access" - return 1 - fi - else - echo "✓ Storage Account public access already enabled" - fi - - # Also ensure the default network action allows access - if [ "$original_storage_default_action" != "Allow" ]; then - echo "Setting Storage Account network default action to Allow" - az storage account update \ - --name "$storageAccount" \ - --resource-group "$resourceGroupName" \ - --default-action Allow \ - --output none - if [ $? -eq 0 ]; then - echo "✓ Storage Account network default action set to Allow" - else - echo "✗ Failed to set Storage Account network default action" - return 1 - fi + --public-network-access Enabled \ + --default-action Allow \ + --output none + if [ $? -eq 0 ]; then + echo "✓ Storage Account public access enabled" else - echo "✓ Storage Account network default action already set to Allow" + echo "✗ Failed to enable Storage Account public access" + return 1 fi - - # Enable public access for AI Foundry - # Extract the account resource ID (remove /projects/... part if present) - aif_account_resource_id=$(echo "$aif_resource_id" | sed 's|/projects/.*||') - aif_resource_name=$(basename "$aif_account_resource_id") - # Extract resource group from the AI Foundry account resource ID - aif_resource_group=$(echo "$aif_account_resource_id" | sed -n 's|.*/resourceGroups/\([^/]*\)/.*|\1|p') - - original_foundry_public_access=$(az cognitiveservices account show \ - --name "$aif_resource_name" \ - --resource-group "$aif_resource_group" \ - --query "properties.publicNetworkAccess" \ - --output tsv) - if [ -z "$original_foundry_public_access" ] || [ "$original_foundry_public_access" = "null" ]; then - echo "⚠ Info: Could not retrieve AI Foundry network access status." - echo " AI Foundry network access might be managed differently." - elif [ "$original_foundry_public_access" != "Enabled" ]; then - echo "Current AI Foundry public access: $original_foundry_public_access" - echo "Enabling public access for AI Foundry resource: $aif_resource_name (Resource Group: $aif_resource_group)" - if MSYS_NO_PATHCONV=1 az resource update \ - --ids "$aif_account_resource_id" \ - --api-version 2024-10-01 \ - --set properties.publicNetworkAccess=Enabled properties.apiProperties="{}" \ - --output none; then - echo "✓ AI Foundry public access enabled" - else - echo "⚠ Warning: Failed to enable AI Foundry public access automatically." - fi + else + echo "✓ Storage Account public access already enabled" + fi + + # Enable public access for AI Foundry + aif_account_resource_id=$(echo "$aif_resource_id" | sed 's|/projects/.*||') + aif_resource_group=$(echo "$aif_account_resource_id" | sed -n 's|.*/resourceGroups/\([^/]*\)/.*|\1|p') + # Extract subscription ID from AI Foundry resource ID + aif_subscription_id=$(echo "$aif_account_resource_id" | sed -n 's|.*/subscriptions/\([^/]*\)/.*|\1|p') + + original_foundry_public_access=$(MSYS_NO_PATHCONV=1 az resource show \ + --ids "$aif_account_resource_id" \ + --subscription "$aif_subscription_id" \ + --api-version 2024-10-01 \ + --query "properties.publicNetworkAccess" \ + --output tsv) + if [ -z "$original_foundry_public_access" ] || [ "$original_foundry_public_access" = "null" ]; then + echo "⚠ Info: Could not retrieve AI Foundry network access status." + echo " AI Foundry network access might be managed differently." + elif [ "$original_foundry_public_access" != "Enabled" ]; then + echo "Enabling public access for AI Foundry Resource Group: $aif_resource_group" + if MSYS_NO_PATHCONV=1 az resource update \ + --ids "$aif_account_resource_id" \ + --api-version 2024-10-01 \ + --subscription "$aif_subscription_id" \ + --set properties.publicNetworkAccess=Enabled \ + --set properties.publicNetworkAccess=Enabled properties.apiProperties="{}" \ + --output none; then + echo "✓ AI Foundry public access enabled" else - echo "✓ AI Foundry public access already enabled" + echo "⚠ Warning: Failed to enable AI Foundry public access automatically." fi + else + echo "✓ AI Foundry public access already enabled" + fi - # Enable public access for SQL Server - echo "Enabling public access for SQL Server: $sqlServerName" - original_sql_public_access=$(az sql server show \ + # Enable public access for SQL Server + echo "Enabling public access for SQL Server: $sqlServerName" + original_sql_public_access=$(az sql server show \ + --name "$sqlServerName" \ + --resource-group "$resourceGroupName" \ + --query "publicNetworkAccess" \ + -o tsv) + if [ "$original_sql_public_access" != "Enabled" ]; then + az sql server update \ --name "$sqlServerName" \ --resource-group "$resourceGroupName" \ - --query "publicNetworkAccess" \ - -o tsv) - if [ "$original_sql_public_access" != "Enabled" ]; then - az sql server update \ - --name "$sqlServerName" \ - --resource-group "$resourceGroupName" \ - --enable-public-network true \ - --output none - if [ $? -eq 0 ]; then - echo "✓ SQL Server public access enabled" - else - echo "✗ Failed to enable SQL Server public access" - return 1 - fi + --enable-public-network true \ + --output none + if [ $? -eq 0 ]; then + echo "✓ SQL Server public access enabled" else - echo "✓ SQL Server public access already enabled" + echo "✗ Failed to enable SQL Server public access" + return 1 fi + else + echo "✓ SQL Server public access already enabled" + fi - # Add (or verify) a firewall rule allowing all IPs (TEMPORARY) - echo "Ensuring temporary wide-open firewall rule exists for data load" - sql_allow_all_rule_name="temp-allow-all-ip" - - # Detect if a full-range rule (any name) already existed before we potentially create one - pre_existing_full_range_rule=$(az sql server firewall-rule list \ - --server "$sqlServerName" \ - --resource-group "$resourceGroupName" \ - --query "[?startIpAddress=='0.0.0.0' && endIpAddress=='255.255.255.255'] | [0].name" \ - -o tsv 2>/dev/null) - if [ -n "$pre_existing_full_range_rule" ]; then - original_full_range_rule_present="true" - fi - - existing_allow_all_rule=$(az sql server firewall-rule list \ - --server "$sqlServerName" \ - --resource-group "$resourceGroupName" \ - --query "[?name=='${sql_allow_all_rule_name}'] | [0].name" \ - -o tsv 2>/dev/null) - - if [ -z "$existing_allow_all_rule" ]; then - if [ -n "$pre_existing_full_range_rule" ]; then - echo "✓ Existing rule ($pre_existing_full_range_rule) already allows full IP range." - else - echo "Creating temporary allow-all firewall rule ($sql_allow_all_rule_name)..." - if az sql server firewall-rule create \ - --resource-group "$resourceGroupName" \ - --server "$sqlServerName" \ - --name "$sql_allow_all_rule_name" \ - --start-ip-address 0.0.0.0 \ - --end-ip-address 255.255.255.255 \ - --output none; then - created_sql_allow_all_firewall_rule="true" - echo "✓ Temporary allow-all firewall rule created" - else - echo "⚠ Warning: Failed to create allow-all firewall rule" - fi - fi - else - echo "✓ Temporary allow-all firewall rule already present" - # Since it was present beforehand, mark that a full-range rule existed originally - original_full_range_rule_present="true" - fi - - # Wait a bit for changes to take effect - echo "Waiting for network access changes to propagate..." - sleep 10 - echo "=== Public network access enabled successfully ===" - return 0 - } - - # Function to restore original network access settings - restore_network_access() { - echo "=== Restoring original network access settings ===" - - # Restore Storage Account access - if [ -n "$original_storage_public_access" ] && [ "$original_storage_public_access" != "Enabled" ]; then - echo "Restoring Storage Account public access to: $original_storage_public_access" - # Handle case sensitivity - convert to proper case - case "$original_storage_public_access" in - "enabled"|"Enabled") restore_value="Enabled" ;; - "disabled"|"Disabled") restore_value="Disabled" ;; - *) restore_value="$original_storage_public_access" ;; - esac - az storage account update \ - --name "$storageAccount" \ - --resource-group "$resourceGroupName" \ - --public-network-access "$restore_value" \ - --output none - if [ $? -eq 0 ]; then - echo "✓ Storage Account access restored" - else - echo "✗ Failed to restore Storage Account access" - fi + # Add (or verify) a firewall rule allowing all IPs (TEMPORARY) + echo "Ensuring temporary wide-open firewall rule exists for data load" + sql_allow_all_rule_name="temp-allow-all-ip" + + # Detect if a full-range rule (any name) already existed before we potentially create one + pre_existing_full_range_rule=$(az sql server firewall-rule list \ + --server "$sqlServerName" \ + --resource-group "$resourceGroupName" \ + --query "[?startIpAddress=='0.0.0.0' && endIpAddress=='255.255.255.255'] | [0].name" \ + -o tsv 2>/dev/null) + + existing_allow_all_rule=$(az sql server firewall-rule list \ + --server "$sqlServerName" \ + --resource-group "$resourceGroupName" \ + --query "[?name=='${sql_allow_all_rule_name}'] | [0].name" \ + -o tsv 2>/dev/null) + + if [ -z "$existing_allow_all_rule" ]; then + if [ -n "$pre_existing_full_range_rule" ]; then + echo "✓ Existing rule ($pre_existing_full_range_rule) already allows full IP range." else - echo "Storage Account access unchanged (already at desired state)" - fi - - # Restore Storage Account network default action - if [ -n "$original_storage_default_action" ] && [ "$original_storage_default_action" != "Allow" ]; then - echo "Restoring Storage Account network default action to: $original_storage_default_action" - az storage account update \ - --name "$storageAccount" \ + echo "Creating temporary allow-all firewall rule ($sql_allow_all_rule_name)..." + if az sql server firewall-rule create \ --resource-group "$resourceGroupName" \ - --default-action "$original_storage_default_action" \ - --output none - if [ $? -eq 0 ]; then - echo "✓ Storage Account network default action restored" - else - echo "✗ Failed to restore Storage Account network default action" - fi - else - echo "Storage Account network default action unchanged (already at desired state)" - fi - - # Restore AI Foundry access - if [ -n "$original_foundry_public_access" ] && [ "$original_foundry_public_access" != "Enabled" ]; then - echo "Restoring AI Foundry public access to: $original_foundry_public_access" - # Try using the working approach to restore the original setting - if MSYS_NO_PATHCONV=1 az resource update \ - --ids "$aif_account_resource_id" \ - --api-version 2024-10-01 \ - --set properties.publicNetworkAccess="$original_foundry_public_access" \ - --set properties.apiProperties.qnaAzureSearchEndpointKey="" \ - --set properties.networkAcls.bypass="AzureServices" \ - --output none 2>/dev/null; then - echo "✓ AI Foundry access restored" + --server "$sqlServerName" \ + --name "$sql_allow_all_rule_name" \ + --start-ip-address 0.0.0.0 \ + --end-ip-address 255.255.255.255 \ + --output none; then + echo "✓ Temporary allow-all firewall rule created" else - echo "⚠ Warning: Failed to restore AI Foundry access automatically." - echo " Please manually restore network access in the Azure portal if needed." + echo "⚠ Warning: Failed to create allow-all firewall rule" fi - else - echo "AI Foundry access unchanged (already at desired state)" fi - - # Restore SQL Server public access - if [ -n "$original_sql_public_access" ] && [ "$original_sql_public_access" != "Enabled" ]; then - echo "Restoring SQL Server public access to: $original_sql_public_access" - # Handle case sensitivity - case "$original_sql_public_access" in - "enabled"|"Enabled") restore_value=true ;; - "disabled"|"Disabled") restore_value=false ;; - *) restore_value="$original_sql_public_access" ;; - esac - az sql server update \ - --name "$sqlServerName" \ - --resource-group "$resourceGroupName" \ - --enable-public-network $restore_value \ - --output none - if [ $? -eq 0 ]; then - echo "✓ SQL Server access restored" - else - echo "✗ Failed to restore SQL Server access" - fi + else + echo "✓ Temporary allow-all firewall rule already present" + fi + + # Wait a bit for changes to take effect + echo "Waiting for network access changes to propagate..." + sleep 10 + echo "=== Public network access enabled successfully ===" + return 0 +} + +# Function to restore original network access settings +restore_network_access() { + echo "=== Restoring original network access settings ===" + + # Restore Storage Account access + if [ -n "$original_storage_public_access" ] && [ "$original_storage_public_access" != "Enabled" ]; then + echo "Restoring Storage Account public access to: $original_storage_public_access" + # Handle case sensitivity - convert to proper case + case "$original_storage_public_access" in + "enabled"|"Enabled") restore_value="Enabled" ;; + "disabled"|"Disabled") restore_value="Disabled" ;; + *) restore_value="$original_storage_public_access" ;; + esac + az storage account update \ + --name "$storageAccount" \ + --resource-group "$resourceGroupName" \ + --public-network-access "$restore_value" \ + --default-action Deny \ + --output none + if [ $? -eq 0 ]; then + echo "✓ Storage Account access restored" else - echo "SQL Server access unchanged (already at desired state)" + echo "✗ Failed to restore Storage Account access" fi - - } - echo "=== Network access restoration completed ===" + else + echo "Storage Account access unchanged (already at desired state)" + fi - # Function to handle script cleanup on exit - cleanup_on_exit() { - exit_code=$? - echo "" - if [ $exit_code -ne 0 ]; then - echo "Script failed with exit code: $exit_code" + # Restore AI Foundry access + if [ -n "$original_foundry_public_access" ] && [ "$original_foundry_public_access" != "Enabled" ]; then + echo "Restoring AI Foundry public access to: $original_foundry_public_access" + # Try using the working approach to restore the original setting + if MSYS_NO_PATHCONV=1 az resource update \ + --ids "$aif_account_resource_id" \ + --api-version 2024-10-01 \ + --subscription "$aif_subscription_id" \ + --set properties.publicNetworkAccess="$original_foundry_public_access" \ + --set properties.apiProperties.qnaAzureSearchEndpointKey="" \ + --set properties.networkAcls.bypass="AzureServices" \ + --output none 2>/dev/null; then + echo "✓ AI Foundry access restored" + else + echo "⚠ Warning: Failed to restore AI Foundry access automatically." + echo " Please manually restore network access in the Azure portal if needed." fi - echo "Performing cleanup..." - restore_network_access - exit $exit_code - } - - # Set up trap to ensure cleanup happens on exit - trap cleanup_on_exit EXIT INT TERM - - # get parameters from azd env, if not provided - if [ -z "$resourceGroupName" ]; then - resourceGroupName=$(azd env get-value RESOURCE_GROUP_NAME) - fi - - - if [ -z "$cosmosDbAccountName" ]; then - cosmosDbAccountName=$(azd env get-value COSMOSDB_ACCOUNT_NAME) - fi - - if [ -z "$storageAccount" ]; then - storageAccount=$(azd env get-value STORAGE_ACCOUNT_NAME) - fi - - if [ -z "$fileSystem" ]; then - fileSystem=$(azd env get-value STORAGE_CONTAINER_NAME) - fi - - if [ -z "$keyvaultName" ]; then - keyvaultName=$(azd env get-value KEY_VAULT_NAME) - fi - - if [ -z "$sqlServerName" ]; then - sqlServerName=$(azd env get-value SQLDB_SERVER_NAME) - fi - - if [ -z "$SqlDatabaseName" ]; then - SqlDatabaseName=$(azd env get-value SQLDB_DATABASE) - fi - - if [ -z "$webAppManagedIdentityClientId" ]; then - webAppManagedIdentityClientId=$(azd env get-value MANAGEDIDENTITY_WEBAPP_CLIENTID) - fi - - if [ -z "$webAppManagedIdentityDisplayName" ]; then - webAppManagedIdentityDisplayName=$(azd env get-value MANAGEDIDENTITY_WEBAPP_NAME) - fi - - if [ -z "$aiSearchName" ]; then - aiSearchName=$(azd env get-value AI_SEARCH_SERVICE_NAME) + else + echo "AI Foundry access unchanged (already at desired state)" fi - if [ -z "$aif_resource_id" ]; then - aif_resource_id=$(azd env get-value AI_FOUNDRY_RESOURCE_ID) + # Restore SQL Server public access + if [ -n "$original_sql_public_access" ] && [ "$original_sql_public_access" != "Enabled" ]; then + echo "Restoring SQL Server public access to: $original_sql_public_access" + # Handle case sensitivity + case "$original_sql_public_access" in + "enabled"|"Enabled") restore_value=true ;; + "disabled"|"Disabled") restore_value=false ;; + *) restore_value="$original_sql_public_access" ;; + esac + az sql server update \ + --name "$sqlServerName" \ + --resource-group "$resourceGroupName" \ + --enable-public-network $restore_value \ + --output none + if [ $? -eq 0 ]; then + echo "✓ SQL Server access restored" + else + echo "✗ Failed to restore SQL Server access" + fi + else + echo "SQL Server access unchanged (already at desired state)" fi - azSubscriptionId=$(azd env get-value AZURE_SUBSCRIPTION_ID) +} +echo "=== Network access restoration completed ===" - # Check if all required arguments are provided - if [ -z "$resourceGroupName" ] || [ -z "$cosmosDbAccountName" ] || [ -z "$storageAccount" ] || [ -z "$fileSystem" ] || [ -z "$keyvaultName" ] || [ -z "$sqlServerName" ] || [ -z "$SqlDatabaseName" ] || [ -z "$webAppManagedIdentityClientId" ] || [ -z "$webAppManagedIdentityDisplayName" ] || [ -z "$aiSearchName" ] || [ -z "$aif_resource_id" ]; then - echo "Usage: $0 " - exit 1 +# Function to handle script cleanup on exit +cleanup_on_exit() { + exit_code=$? + echo "" + if [ $exit_code -ne 0 ]; then + echo "Script failed with exit code: $exit_code" fi - - # Authenticate with Azure - if az account show &> /dev/null; then - echo "Already authenticated with Azure." + echo "Performing cleanup..." + restore_network_access + exit $exit_code +} + +# Set up trap to ensure cleanup happens on exit +trap cleanup_on_exit EXIT INT TERM + +# get parameters from azd env, if not provided +if [ -z "$resourceGroupName" ]; then + resourceGroupName=$(azd env get-value RESOURCE_GROUP_NAME) +fi + + +if [ -z "$cosmosDbAccountName" ]; then + cosmosDbAccountName=$(azd env get-value COSMOSDB_ACCOUNT_NAME) +fi + +if [ -z "$storageAccount" ]; then + storageAccount=$(azd env get-value STORAGE_ACCOUNT_NAME) +fi + +if [ -z "$fileSystem" ]; then + fileSystem=$(azd env get-value STORAGE_CONTAINER_NAME) +fi + +if [ -z "$keyvaultName" ]; then + keyvaultName=$(azd env get-value KEY_VAULT_NAME) +fi + +if [ -z "$sqlServerName" ]; then + sqlServerName=$(azd env get-value SQLDB_SERVER_NAME) +fi + +if [ -z "$SqlDatabaseName" ]; then + SqlDatabaseName=$(azd env get-value SQLDB_DATABASE) +fi + +if [ -z "$webAppManagedIdentityClientId" ]; then + webAppManagedIdentityClientId=$(azd env get-value MANAGEDIDENTITY_WEBAPP_CLIENTID) +fi + +if [ -z "$webAppManagedIdentityDisplayName" ]; then + webAppManagedIdentityDisplayName=$(azd env get-value MANAGEDIDENTITY_WEBAPP_NAME) +fi + +if [ -z "$aiSearchName" ]; then + aiSearchName=$(azd env get-value AI_SEARCH_SERVICE_NAME) +fi + +if [ -z "$aif_resource_id" ]; then + aif_resource_id=$(azd env get-value AI_FOUNDRY_RESOURCE_ID) +fi + +azSubscriptionId=$(azd env get-value AZURE_SUBSCRIPTION_ID) + +# Check if all required arguments are provided +if [ -z "$resourceGroupName" ] || [ -z "$cosmosDbAccountName" ] || [ -z "$storageAccount" ] || [ -z "$fileSystem" ] || [ -z "$keyvaultName" ] || [ -z "$sqlServerName" ] || [ -z "$SqlDatabaseName" ] || [ -z "$webAppManagedIdentityClientId" ] || [ -z "$webAppManagedIdentityDisplayName" ] || [ -z "$aiSearchName" ] || [ -z "$aif_resource_id" ]; then + echo "Usage: $0 " + exit 1 +fi + +# Authenticate with Azure +if az account show &> /dev/null; then + echo "Already authenticated with Azure." +else + echo "Not authenticated with Azure. Attempting to authenticate..." + if [ -n "$managedIdentityClientId" ]; then + # Use managed identity if running in Azure + echo "Authenticating with Managed Identity..." + az login --identity --client-id ${managedIdentityClientId} else - echo "Not authenticated with Azure. Attempting to authenticate..." - if [ -n "$managedIdentityClientId" ]; then - # Use managed identity if running in Azure - echo "Authenticating with Managed Identity..." - az login --identity --client-id ${managedIdentityClientId} - else - # Use Azure CLI login if running locally - echo "Authenticating with Azure CLI..." - az login - fi + # Use Azure CLI login if running locally + echo "Authenticating with Azure CLI..." + az login fi - - #check if user has selected the correct subscription - currentSubscriptionId=$(az account show --query id -o tsv) - currentSubscriptionName=$(az account show --query name -o tsv) - if [ "$currentSubscriptionId" != "$azSubscriptionId" ]; then - echo "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )." - read -rp "Do you want to continue with this subscription?(y/n): " confirmation - if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then - echo "Fetching available subscriptions..." - availableSubscriptions=$(az account list --query "[?state=='Enabled'].[name,id]" --output tsv) - while true; do - echo "" - echo "Available Subscriptions:" - echo "========================" - echo "$availableSubscriptions" | awk '{printf "%d. %s ( %s )\n", NR, $1, $2}' - echo "========================" - echo "" - read -rp "Enter the number of the subscription (1-$(echo "$availableSubscriptions" | wc -l)) to use: " subscriptionIndex - if [[ "$subscriptionIndex" =~ ^[0-9]+$ ]] && [ "$subscriptionIndex" -ge 1 ] && [ "$subscriptionIndex" -le $(echo "$availableSubscriptions" | wc -l) ]; then - selectedSubscription=$(echo "$availableSubscriptions" | sed -n "${subscriptionIndex}p") - selectedSubscriptionName=$(echo "$selectedSubscription" | cut -f1) - selectedSubscriptionId=$(echo "$selectedSubscription" | cut -f2) - - # Set the selected subscription - if az account set --subscription "$selectedSubscriptionId"; then - echo "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )" - break - else - echo "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )." - fi +fi + +#check if user has selected the correct subscription +currentSubscriptionId=$(az account show --query id -o tsv) +currentSubscriptionName=$(az account show --query name -o tsv) +if [ "$currentSubscriptionId" != "$azSubscriptionId" ]; then + echo "Current selected subscription is $currentSubscriptionName ( $currentSubscriptionId )." + read -rp "Do you want to continue with this subscription?(y/n): " confirmation + if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then + echo "Fetching available subscriptions..." + availableSubscriptions=$(az account list --query "[?state=='Enabled'].[name,id]" --output tsv) + while true; do + echo "" + echo "Available Subscriptions:" + echo "========================" + echo "$availableSubscriptions" | awk '{printf "%d. %s ( %s )\n", NR, $1, $2}' + echo "========================" + echo "" + read -rp "Enter the number of the subscription (1-$(echo "$availableSubscriptions" | wc -l)) to use: " subscriptionIndex + if [[ "$subscriptionIndex" =~ ^[0-9]+$ ]] && [ "$subscriptionIndex" -ge 1 ] && [ "$subscriptionIndex" -le $(echo "$availableSubscriptions" | wc -l) ]; then + selectedSubscription=$(echo "$availableSubscriptions" | sed -n "${subscriptionIndex}p") + selectedSubscriptionName=$(echo "$selectedSubscription" | cut -f1) + selectedSubscriptionId=$(echo "$selectedSubscription" | cut -f2) + + # Set the selected subscription + if az account set --subscription "$selectedSubscriptionId"; then + echo "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )" + break else - echo "Invalid selection. Please try again." + echo "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )." fi - done - else - echo "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )" - az account set --subscription "$currentSubscriptionId" - fi + else + echo "Invalid selection. Please try again." + fi + done else - echo "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )" + echo "Proceeding with the current subscription: $currentSubscriptionName ( $currentSubscriptionId )" az account set --subscription "$currentSubscriptionId" fi - - - # Enable public network access for required services - enable_public_access - if [ $? -ne 0 ]; then - echo "Error: Failed to enable public network access for services." - exit 1 - fi - - - # Call add_cosmosdb_access.sh - echo "Running add_cosmosdb_access.sh" - bash infra/scripts/add_cosmosdb_access.sh "$resourceGroupName" "$cosmosDbAccountName" - if [ $? -ne 0 ]; then - echo "Error: add_cosmosdb_access.sh failed." - exit 1 - fi - echo "add_cosmosdb_access.sh completed successfully." - - # Call copy_kb_files.sh - echo "Running copy_kb_files.sh" - bash infra/scripts/copy_kb_files.sh "$storageAccount" "$fileSystem" - if [ $? -ne 0 ]; then - echo "Error: copy_kb_files.sh failed." - exit 1 - fi - echo "copy_kb_files.sh completed successfully." - - # Call run_create_index_scripts.sh - echo "Running run_create_index_scripts.sh" - bash infra/scripts/run_create_index_scripts.sh "$keyvaultName" "" "" "$resourceGroupName" "$sqlServerName" "$aiSearchName" "$aif_resource_id" - if [ $? -ne 0 ]; then - echo "Error: run_create_index_scripts.sh failed." - exit 1 - fi - echo "run_create_index_scripts.sh completed successfully." - - # Call create_sql_user_and_role.sh - echo "Running create_sql_user_and_role.sh" - bash infra/scripts/add_user_scripts/create_sql_user_and_role.sh "$sqlServerName.database.windows.net" "$SqlDatabaseName" '[ - {"clientId":"'"$webAppManagedIdentityClientId"'", "displayName":"'"$webAppManagedIdentityDisplayName"'", "role":"db_datareader"}, - {"clientId":"'"$webAppManagedIdentityClientId"'", "displayName":"'"$webAppManagedIdentityDisplayName"'", "role":"db_datawriter"} - ]' - if [ $? -ne 0 ]; then - echo "Error: create_sql_user_and_role.sh failed." - exit 1 - fi - echo "create_sql_user_and_role.sh completed successfully." - - echo "All scripts executed successfully." - echo "Network access will be restored to original settings..." - # Note: cleanup_on_exit will be called automatically via the trap \ No newline at end of file +else + echo "Proceeding with the subscription: $currentSubscriptionName ( $currentSubscriptionId )" + az account set --subscription "$currentSubscriptionId" +fi + + +# Enable public network access for required services +enable_public_access +if [ $? -ne 0 ]; then + echo "Error: Failed to enable public network access for services." + exit 1 +fi + + +# Call add_cosmosdb_access.sh +echo "Running add_cosmosdb_access.sh" +bash infra/scripts/add_cosmosdb_access.sh "$resourceGroupName" "$cosmosDbAccountName" +if [ $? -ne 0 ]; then + echo "Error: add_cosmosdb_access.sh failed." + exit 1 +fi +echo "add_cosmosdb_access.sh completed successfully." + +# Call copy_kb_files.sh +echo "Running copy_kb_files.sh" +bash infra/scripts/copy_kb_files.sh "$storageAccount" "$fileSystem" +if [ $? -ne 0 ]; then + echo "Error: copy_kb_files.sh failed." + exit 1 +fi +echo "copy_kb_files.sh completed successfully." + +# Call run_create_index_scripts.sh +echo "Running run_create_index_scripts.sh" +bash infra/scripts/run_create_index_scripts.sh "$keyvaultName" "" "" "$resourceGroupName" "$sqlServerName" "$aiSearchName" "$aif_resource_id" +if [ $? -ne 0 ]; then + echo "Error: run_create_index_scripts.sh failed." + exit 1 +fi +echo "run_create_index_scripts.sh completed successfully." + +# Call create_sql_user_and_role.sh +echo "Running create_sql_user_and_role.sh" +bash infra/scripts/add_user_scripts/create_sql_user_and_role.sh "$sqlServerName.database.windows.net" "$SqlDatabaseName" '[ + {"clientId":"'"$webAppManagedIdentityClientId"'", "displayName":"'"$webAppManagedIdentityDisplayName"'", "role":"db_datareader"}, + {"clientId":"'"$webAppManagedIdentityClientId"'", "displayName":"'"$webAppManagedIdentityDisplayName"'", "role":"db_datawriter"} +]' +if [ $? -ne 0 ]; then + echo "Error: create_sql_user_and_role.sh failed." + exit 1 +fi +echo "create_sql_user_and_role.sh completed successfully." + +echo "All scripts executed successfully." +echo "Network access will be restored to original settings..." +# Note: cleanup_on_exit will be called automatically via the trap \ No newline at end of file diff --git a/infra/scripts/run_create_index_scripts.sh b/infra/scripts/run_create_index_scripts.sh index ad4878d43..6a9c7d536 100644 --- a/infra/scripts/run_create_index_scripts.sh +++ b/infra/scripts/run_create_index_scripts.sh @@ -170,9 +170,9 @@ if [ -n "$managedIdentityClientId" ]; then fi # Determine the correct Python command -if command -v python3 &> /dev/null; then +if command -v python3 && python3 --version &> /dev/null; then PYTHON_CMD="python3" -elif command -v python &> /dev/null; then +elif command -v python && python --version &> /dev/null; then PYTHON_CMD="python" else echo "Python is not installed on this system. Or it is not added in the PATH."