diff --git a/infrastructure/infrastructure-setup-bicep/00-basic/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/00-basic/azuredeploy.json index fbb96f353..5edf30433 100644 --- a/infrastructure/infrastructure-setup-bicep/00-basic/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/00-basic/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15247532385970128893" + "version": "0.39.26.7824", + "templateHash": "4687470877814835157" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/03-custom-dns/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/03-custom-dns/azuredeploy.json index 3c7ec4096..672e78928 100644 --- a/infrastructure/infrastructure-setup-bicep/03-custom-dns/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/03-custom-dns/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "14038434470119297573" + "version": "0.39.26.7824", + "templateHash": "17555450976668837106" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/04-disable-local-auth/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/04-disable-local-auth/azuredeploy.json index 396a1b9aa..d2cee29a4 100644 --- a/infrastructure/infrastructure-setup-bicep/04-disable-local-auth/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/04-disable-local-auth/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "6518302005159872058" + "version": "0.39.26.7824", + "templateHash": "6087024663439753870" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/10-private-network-basic/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/10-private-network-basic/azuredeploy.json index 7ea345c11..840868a3e 100644 --- a/infrastructure/infrastructure-setup-bicep/10-private-network-basic/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/10-private-network-basic/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "6904353034679125432" + "version": "0.39.26.7824", + "templateHash": "258335635459710717" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/16-private-network-standard-agent-apim-setup-preview/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/16-private-network-standard-agent-apim-setup-preview/azuredeploy.json index c2a5c5198..3e10f19fa 100644 --- a/infrastructure/infrastructure-setup-bicep/16-private-network-standard-agent-apim-setup-preview/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/16-private-network-standard-agent-apim-setup-preview/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1883763996282384972" + "version": "0.39.26.7824", + "templateHash": "13929636312670904506" } }, "parameters": { @@ -297,8 +297,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "9522150535342725348" + "version": "0.39.26.7824", + "templateHash": "8505298823279202405" } }, "parameters": { @@ -411,8 +411,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1725519900292599397" + "version": "0.39.26.7824", + "templateHash": "4954184648131521061" } }, "parameters": { @@ -584,8 +584,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12834877200138369468" + "version": "0.39.26.7824", + "templateHash": "3152324712046183852" } }, "parameters": { @@ -674,8 +674,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16684585228229443328" + "version": "0.39.26.7824", + "templateHash": "17043822047386586435" } }, "parameters": { @@ -757,8 +757,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16684585228229443328" + "version": "0.39.26.7824", + "templateHash": "17043822047386586435" } }, "parameters": { @@ -929,8 +929,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "481468620289904255" + "version": "0.39.26.7824", + "templateHash": "854097619778148359" } }, "parameters": { @@ -1069,8 +1069,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "923626409245918017" + "version": "0.39.26.7824", + "templateHash": "15994578372975581196" } }, "parameters": { @@ -1248,8 +1248,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1228922674432785221" + "version": "0.39.26.7824", + "templateHash": "2754228344238136934" } }, "parameters": { @@ -1537,8 +1537,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7465170083747306470" + "version": "0.39.26.7824", + "templateHash": "16093594075579008850" } }, "parameters": { @@ -2159,8 +2159,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1007872875847158534" + "version": "0.39.26.7824", + "templateHash": "5095087340309076800" } }, "parameters": { @@ -2334,8 +2334,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12606978205101260380" + "version": "0.39.26.7824", + "templateHash": "6910483561575524105" } }, "parameters": { @@ -2389,8 +2389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15991712531324316353" + "version": "0.39.26.7824", + "templateHash": "14683840003859985069" } }, "parameters": { @@ -2405,7 +2405,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('azureStorageName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2447,8 +2447,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11201886913940098363" + "version": "0.39.26.7824", + "templateHash": "2161753938341361575" } }, "parameters": { @@ -2469,7 +2469,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2511,8 +2511,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15867124606695536257" + "version": "0.39.26.7824", + "templateHash": "7968115481508840" } }, "parameters": { @@ -2533,7 +2533,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2544,7 +2544,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2596,8 +2596,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "687161311666023487" + "version": "0.39.26.7824", + "templateHash": "17458377866351620215" } }, "parameters": { @@ -2690,8 +2690,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "3625425571119261380" + "version": "0.39.26.7824", + "templateHash": "13874725855824693255" } }, "parameters": { @@ -2721,7 +2721,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageName'))]", "name": "[guid(resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'), resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')))]", "properties": { "principalId": "[parameters('aiProjectPrincipalId')]", @@ -2769,8 +2769,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "14722474518981746838" + "version": "0.39.26.7824", + "templateHash": "17187611271934567223" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/17-private-network-standard-user-assigned-identity-agent-setup/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/17-private-network-standard-user-assigned-identity-agent-setup/azuredeploy.json index d77f1b20f..96525e295 100644 --- a/infrastructure/infrastructure-setup-bicep/17-private-network-standard-user-assigned-identity-agent-setup/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/17-private-network-standard-user-assigned-identity-agent-setup/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "5825478414824080876" + "version": "0.39.26.7824", + "templateHash": "9271946558272429054" } }, "parameters": { @@ -283,8 +283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16080922426874297734" + "version": "0.39.26.7824", + "templateHash": "12473591672685297473" } }, "parameters": { @@ -384,8 +384,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "9522150535342725348" + "version": "0.39.26.7824", + "templateHash": "8505298823279202405" } }, "parameters": { @@ -498,8 +498,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1725519900292599397" + "version": "0.39.26.7824", + "templateHash": "4954184648131521061" } }, "parameters": { @@ -671,8 +671,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12834877200138369468" + "version": "0.39.26.7824", + "templateHash": "3152324712046183852" } }, "parameters": { @@ -761,8 +761,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16684585228229443328" + "version": "0.39.26.7824", + "templateHash": "17043822047386586435" } }, "parameters": { @@ -844,8 +844,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16684585228229443328" + "version": "0.39.26.7824", + "templateHash": "17043822047386586435" } }, "parameters": { @@ -1019,8 +1019,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7102706229714471787" + "version": "0.39.26.7824", + "templateHash": "12620781326236378852" } }, "parameters": { @@ -1159,8 +1159,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16869070767065140538" + "version": "0.39.26.7824", + "templateHash": "7641310640078958122" } }, "parameters": { @@ -1311,8 +1311,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1228922674432785221" + "version": "0.39.26.7824", + "templateHash": "2754228344238136934" } }, "parameters": { @@ -1591,8 +1591,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "8505208925695592232" + "version": "0.39.26.7824", + "templateHash": "8094529554453089222" } }, "parameters": { @@ -2123,8 +2123,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "3709848476025682641" + "version": "0.39.26.7824", + "templateHash": "3622791801420135420" } }, "parameters": { @@ -2301,8 +2301,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12606978205101260380" + "version": "0.39.26.7824", + "templateHash": "6910483561575524105" } }, "parameters": { @@ -2356,8 +2356,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15991712531324316353" + "version": "0.39.26.7824", + "templateHash": "14683840003859985069" } }, "parameters": { @@ -2372,7 +2372,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('azureStorageName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2414,8 +2414,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7256419427588810161" + "version": "0.39.26.7824", + "templateHash": "25128059954858801" } }, "parameters": { @@ -2436,7 +2436,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2478,8 +2478,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15867124606695536257" + "version": "0.39.26.7824", + "templateHash": "7968115481508840" } }, "parameters": { @@ -2500,7 +2500,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2511,7 +2511,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -2563,8 +2563,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "687161311666023487" + "version": "0.39.26.7824", + "templateHash": "17458377866351620215" } }, "parameters": { @@ -2657,8 +2657,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "3625425571119261380" + "version": "0.39.26.7824", + "templateHash": "13874725855824693255" } }, "parameters": { @@ -2688,7 +2688,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageName'))]", "name": "[guid(resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'), resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')))]", "properties": { "principalId": "[parameters('aiProjectPrincipalId')]", @@ -2736,8 +2736,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "14722474518981746838" + "version": "0.39.26.7824", + "templateHash": "17187611271934567223" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/add-project.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/add-project.bicep deleted file mode 100644 index 6a6d570df..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/add-project.bicep +++ /dev/null @@ -1,197 +0,0 @@ -@description('Location for the project resources.') -param location string = 'westus' - -@description('Name of the existing AI Services account') -param existingAccountName string - -@description('Resource group containing the AI Services account') -param accountResourceGroupName string = resourceGroup().name - -@description('Subscription ID containing the AI Services account') -param accountSubscriptionId string = subscription().subscriptionId - -@description('Name for the new project') -param projectName string - -@description('Description for the new project') -param projectDescription string = 'Additional AI Foundry project with network secured deployed Agent' - -@description('Display name for the new project') -param displayName string - -@description('Name for the project capability host') -param projectCapHost string = 'caphostproj' - -// Existing shared resources (from your original deployment) -@description('Name of the existing AI Search service') -param existingAiSearchName string - -@description('Resource group containing the AI Search service') -param aiSearchResourceGroupName string - -@description('Subscription ID containing the AI Search service') -param aiSearchSubscriptionId string - -@description('Name of the existing Storage Account') -param existingStorageName string - -@description('Resource group containing the Storage Account') -param storageResourceGroupName string - -@description('Subscription ID containing the Storage Account') -param storageSubscriptionId string - -@description('Name of the existing Cosmos DB account') -param existingCosmosDBName string - -@description('Resource group containing the Cosmos DB account') -param cosmosDBResourceGroupName string - -@description('Subscription ID containing the Cosmos DB account') -param cosmosDBSubscriptionId string - -// Create a short, unique suffix for this project -param deploymentTimestamp string = utcNow('yyyyMMddHHmmss') -var uniqueSuffix = substring(uniqueString('${resourceGroup().id}-${deploymentTimestamp}'), 0, 4) -var finalProjectName = toLower('${projectName}${uniqueSuffix}') - -// Reference existing AI Services account -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { - name: existingAccountName - scope: resourceGroup(accountSubscriptionId, accountResourceGroupName) -} - -// Reference existing shared resources -resource aiSearch 'Microsoft.Search/searchServices@2023-11-01' existing = { - name: existingAiSearchName - scope: resourceGroup(aiSearchSubscriptionId, aiSearchResourceGroupName) -} - -resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' existing = { - name: existingStorageName - scope: resourceGroup(storageSubscriptionId, storageResourceGroupName) -} - -resource cosmosDB 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' existing = { - name: existingCosmosDBName - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) -} - -// Create the new project using the unique connection module -module aiProject 'modules-network-secured/ai-project-identity-unique.bicep' = { - name: 'ai-${finalProjectName}-${uniqueSuffix}-deployment' - params: { - projectName: finalProjectName - projectDescription: projectDescription - displayName: displayName - location: location - - aiSearchName: existingAiSearchName - aiSearchServiceResourceGroupName: aiSearchResourceGroupName - aiSearchServiceSubscriptionId: aiSearchSubscriptionId - - cosmosDBName: existingCosmosDBName - cosmosDBSubscriptionId: cosmosDBSubscriptionId - cosmosDBResourceGroupName: cosmosDBResourceGroupName - - azureStorageName: existingStorageName - azureStorageSubscriptionId: storageSubscriptionId - azureStorageResourceGroupName: storageResourceGroupName - - accountName: existingAccountName - - // Pass unique suffix for connection names - uniqueConnectionSuffix: '-${finalProjectName}' - } -} - -module formatProjectWorkspaceId 'modules-network-secured/format-project-workspace-id.bicep' = { - name: 'format-project-workspace-id-${uniqueSuffix}-deployment' - params: { - projectWorkspaceId: aiProject.outputs.projectWorkspaceId - } -} - -// Assign storage account role -module storageAccountRoleAssignment 'modules-network-secured/azure-storage-account-role-assignment.bicep' = { - name: 'storage-${existingStorageName}-${uniqueSuffix}-deployment' - scope: resourceGroup(storageSubscriptionId, storageResourceGroupName) - params: { - azureStorageName: existingStorageName - projectPrincipalId: aiProject.outputs.projectPrincipalId - } -} - -// Assign Cosmos DB account role -module cosmosAccountRoleAssignments 'modules-network-secured/cosmosdb-account-role-assignment.bicep' = { - name: 'cosmos-account-ra-${finalProjectName}-${uniqueSuffix}-deployment' - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) - params: { - cosmosDBName: existingCosmosDBName - projectPrincipalId: aiProject.outputs.projectPrincipalId - } -} - -// Assign AI Search role -module aiSearchRoleAssignments 'modules-network-secured/ai-search-role-assignments.bicep' = { - name: 'ai-search-ra-${finalProjectName}-${uniqueSuffix}-deployment' - scope: resourceGroup(aiSearchSubscriptionId, aiSearchResourceGroupName) - params: { - aiSearchName: existingAiSearchName - projectPrincipalId: aiProject.outputs.projectPrincipalId - } -} - -// Create capability host for the new project -module addProjectCapabilityHost 'modules-network-secured/add-project-capability-host.bicep' = { - name: 'capabilityHost-configuration-${uniqueSuffix}-deployment' - params: { - accountName: existingAccountName - projectName: aiProject.outputs.projectName - cosmosDBConnection: aiProject.outputs.cosmosDBConnection - azureStorageConnection: aiProject.outputs.azureStorageConnection - aiSearchConnection: aiProject.outputs.aiSearchConnection - projectCapHost: projectCapHost - } - dependsOn: [ - cosmosAccountRoleAssignments - storageAccountRoleAssignment - aiSearchRoleAssignments - ] -} - -// Assign storage container roles after capability host creation -module storageContainersRoleAssignment 'modules-network-secured/blob-storage-container-role-assignments-unique.bicep' = { - name: 'storage-containers-${uniqueSuffix}-deployment' - scope: resourceGroup(storageSubscriptionId, storageResourceGroupName) - params: { - aiProjectPrincipalId: aiProject.outputs.projectPrincipalId - storageName: existingStorageName - workspaceId: formatProjectWorkspaceId.outputs.projectWorkspaceIdGuid - uniqueSuffix: uniqueSuffix // Add this line - } - dependsOn: [ - addProjectCapabilityHost - ] -} - -// Assign Cosmos container roles after capability host creation -module cosmosContainerRoleAssignments 'modules-network-secured/cosmos-container-role-assignments.bicep' = { - name: 'cosmos-ra-${uniqueSuffix}-deployment' - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) - params: { - cosmosAccountName: existingCosmosDBName - projectWorkspaceId: formatProjectWorkspaceId.outputs.projectWorkspaceIdGuid - projectPrincipalId: aiProject.outputs.projectPrincipalId - } - dependsOn: [ - addProjectCapabilityHost - storageContainersRoleAssignment - ] -} - -// Outputs -output projectName string = aiProject.outputs.projectName -output projectPrincipalId string = aiProject.outputs.projectPrincipalId -output projectWorkspaceId string = aiProject.outputs.projectWorkspaceId -output capabilityHostName string = addProjectCapabilityHost.outputs.projectCapHost diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/add-project.bicepparam b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/add-project.bicepparam deleted file mode 100644 index 127907979..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/add-project.bicepparam +++ /dev/null @@ -1,29 +0,0 @@ -using './add-project.bicep' - -param location = 'westus' - -// New project details -param projectName = 'secondproject' -param projectDescription = 'Second AI Foundry project with network secured deployed Agent' -param displayName = 'Second Project' -param projectCapHost = 'caphostsecond' - -// Existing AI Services account details (from your original deployment) -// You'll need to get these from your existing deployment -param existingAccountName = '' // Replace with your actual account name -param accountResourceGroupName = '' // Your resource group -param accountSubscriptionId = '' - -// Existing shared resources (from your original deployment) -// You'll need to get these from your existing deployment outputs -param existingAiSearchName = '' // Replace with your actual search service name -param aiSearchResourceGroupName = '' // Your resource group -param aiSearchSubscriptionId = '' - -param existingStorageName = '' // Replace with your actual storage account name -param storageResourceGroupName = '' // Your resource group -param storageSubscriptionId = '' - -param existingCosmosDBName = '' // Replace with your actual Cosmos DB name -param cosmosDBResourceGroupName = '' // Your resource group -param cosmosDBSubscriptionId = '' diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/azuredeploy.json deleted file mode 100644 index 460001bba..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/azuredeploy.json +++ /dev/null @@ -1,2679 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "3024624923779287280" - } - }, - "parameters": { - "location": { - "type": "string", - "defaultValue": "eastus2", - "allowedValues": [ - "westus", - "eastus", - "eastus2", - "japaneast", - "francecentral", - "spaincentral", - "uaenorth", - "southcentralus", - "italynorth", - "germanywestcentral", - "brazilsouth", - "southafricanorth", - "australiaeast", - "swedencentral", - "canadaeast", - "westeurope", - "westus3", - "uksouth", - "southindia", - "koreacentral", - "polandcentral", - "switzerlandnorth", - "norwayeast" - ], - "metadata": { - "description": "Location for all resources." - } - }, - "aiServices": { - "type": "string", - "defaultValue": "aiservices", - "metadata": { - "description": "Name for your AI Services resource." - } - }, - "modelName": { - "type": "string", - "defaultValue": "gpt-4o", - "metadata": { - "description": "The name of the model you want to deploy" - } - }, - "modelFormat": { - "type": "string", - "defaultValue": "OpenAI", - "metadata": { - "description": "The provider of your model" - } - }, - "modelVersion": { - "type": "string", - "defaultValue": "2024-11-20", - "metadata": { - "description": "The version of your model" - } - }, - "modelSkuName": { - "type": "string", - "defaultValue": "GlobalStandard", - "metadata": { - "description": "The sku of your model deployment" - } - }, - "modelCapacity": { - "type": "int", - "defaultValue": 30, - "metadata": { - "description": "The tokens per minute (TPM) of your model deployment" - } - }, - "deploymentTimestamp": { - "type": "string", - "defaultValue": "[utcNow('yyyyMMddHHmmss')]" - }, - "firstProjectName": { - "type": "string", - "defaultValue": "project", - "metadata": { - "description": "Name for your project resource." - } - }, - "projectDescription": { - "type": "string", - "defaultValue": "A project for the AI Foundry account with network secured deployed Agent", - "metadata": { - "description": "This project will be a sub-resource of your account" - } - }, - "displayName": { - "type": "string", - "defaultValue": "network secured agent project", - "metadata": { - "description": "The display name of the project" - } - }, - "vnetName": { - "type": "string", - "defaultValue": "agent-vnet-test", - "metadata": { - "description": "Virtual Network name for the Agent to create new or existing virtual network" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet to create new or existing subnet for agents" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Private Endpoint subnet to create new or existing subnet for private endpoints" - } - }, - "existingVnetResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Existing Virtual Network name Resource ID" - } - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address space for the VNet (only used for new VNet)" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet. The default value is 192.168.0.0/24 but you can choose any size /26 or any class like 10.0.0.0 or 172.168.0.0" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet" - } - }, - "aiSearchResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The AI Search Service full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "azureStorageAccountResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The AI Storage Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "azureCosmosDBAccountResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The Cosmos DB Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "existingDnsZones": { - "type": "object", - "defaultValue": { - "privatelink.services.ai.azure.com": "", - "privatelink.openai.azure.com": "", - "privatelink.cognitiveservices.azure.com": "", - "privatelink.search.windows.net": "", - "privatelink.blob.core.windows.net": "", - "privatelink.documents.azure.com": "" - }, - "metadata": { - "description": "Object mapping DNS zone names to their resource group, or empty string to indicate creation" - } - }, - "dnsZoneNames": { - "type": "array", - "defaultValue": [ - "privatelink.services.ai.azure.com", - "privatelink.openai.azure.com", - "privatelink.cognitiveservices.azure.com", - "privatelink.search.windows.net", - "privatelink.blob.core.windows.net", - "privatelink.documents.azure.com" - ], - "metadata": { - "description": "Zone Names for Validation of existing Private Dns Zones" - } - }, - "projectCapHost": { - "type": "string", - "defaultValue": "caphostproj", - "metadata": { - "description": "The name of the project capability host to be created" - } - } - }, - "variables": { - "uniqueSuffix": "[substring(uniqueString(format('{0}-{1}', resourceGroup().id, parameters('deploymentTimestamp'))), 0, 4)]", - "accountName": "[toLower(format('{0}{1}', parameters('aiServices'), variables('uniqueSuffix')))]", - "projectName": "[toLower(format('{0}{1}', parameters('firstProjectName'), variables('uniqueSuffix')))]", - "cosmosDBName": "[toLower(format('{0}{1}cosmosdb', parameters('aiServices'), variables('uniqueSuffix')))]", - "aiSearchName": "[toLower(format('{0}{1}search', parameters('aiServices'), variables('uniqueSuffix')))]", - "azureStorageName": "[toLower(format('{0}{1}storage', parameters('aiServices'), variables('uniqueSuffix')))]", - "storagePassedIn": "[not(equals(parameters('azureStorageAccountResourceId'), ''))]", - "searchPassedIn": "[not(equals(parameters('aiSearchResourceId'), ''))]", - "cosmosPassedIn": "[not(equals(parameters('azureCosmosDBAccountResourceId'), ''))]", - "existingVnetPassedIn": "[not(equals(parameters('existingVnetResourceId'), ''))]", - "acsParts": "[split(parameters('aiSearchResourceId'), '/')]", - "aiSearchServiceSubscriptionId": "[if(variables('searchPassedIn'), variables('acsParts')[2], subscription().subscriptionId)]", - "aiSearchServiceResourceGroupName": "[if(variables('searchPassedIn'), variables('acsParts')[4], resourceGroup().name)]", - "cosmosParts": "[split(parameters('azureCosmosDBAccountResourceId'), '/')]", - "cosmosDBSubscriptionId": "[if(variables('cosmosPassedIn'), variables('cosmosParts')[2], subscription().subscriptionId)]", - "cosmosDBResourceGroupName": "[if(variables('cosmosPassedIn'), variables('cosmosParts')[4], resourceGroup().name)]", - "storageParts": "[split(parameters('azureStorageAccountResourceId'), '/')]", - "azureStorageSubscriptionId": "[if(variables('storagePassedIn'), variables('storageParts')[2], subscription().subscriptionId)]", - "azureStorageResourceGroupName": "[if(variables('storagePassedIn'), variables('storageParts')[4], resourceGroup().name)]", - "vnetParts": "[split(parameters('existingVnetResourceId'), '/')]", - "vnetSubscriptionId": "[if(variables('existingVnetPassedIn'), variables('vnetParts')[2], subscription().subscriptionId)]", - "vnetResourceGroupName": "[if(variables('existingVnetPassedIn'), variables('vnetParts')[4], resourceGroup().name)]", - "existingVnetName": "[if(variables('existingVnetPassedIn'), last(variables('vnetParts')), parameters('vnetName'))]", - "trimVnetName": "[trim(variables('existingVnetName'))]" - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "vnetName": { - "value": "[variables('trimVnetName')]" - }, - "useExistingVnet": { - "value": "[variables('existingVnetPassedIn')]" - }, - "existingVnetResourceGroupName": { - "value": "[variables('vnetResourceGroupName')]" - }, - "agentSubnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "value": "[parameters('peSubnetName')]" - }, - "vnetAddressPrefix": { - "value": "[parameters('vnetAddressPrefix')]" - }, - "agentSubnetPrefix": { - "value": "[parameters('agentSubnetPrefix')]" - }, - "peSubnetPrefix": { - "value": "[parameters('peSubnetPrefix')]" - }, - "existingVnetSubscriptionId": { - "value": "[variables('vnetSubscriptionId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "8505298823279202405" - } - }, - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Azure region for the deployment" - } - }, - "vnetName": { - "type": "string", - "metadata": { - "description": "The name of the virtual network" - } - }, - "useExistingVnet": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates if an existing VNet should be used" - } - }, - "existingVnetSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID of the existing VNet (if different from current subscription)" - } - }, - "existingVnetResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name of the existing VNet (if different from current resource group)" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Private Endpoint subnet" - } - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address space for the VNet (only used for new VNet)" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet" - } - } - }, - "resources": [ - { - "condition": "[not(parameters('useExistingVnet'))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "vnet-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "agentSubnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "value": "[parameters('peSubnetName')]" - }, - "vnetAddressPrefix": { - "value": "[parameters('vnetAddressPrefix')]" - }, - "agentSubnetPrefix": { - "value": "[parameters('agentSubnetPrefix')]" - }, - "peSubnetPrefix": { - "value": "[parameters('peSubnetPrefix')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "4954184648131521061" - } - }, - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Azure region for the deployment" - } - }, - "vnetName": { - "type": "string", - "defaultValue": "agents-vnet-test", - "metadata": { - "description": "The name of the virtual network" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Hub subnet" - } - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address space for the VNet" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet" - } - } - }, - "variables": { - "defaultVnetAddressPrefix": "192.168.0.0/16", - "vnetAddress": "[if(empty(parameters('vnetAddressPrefix')), variables('defaultVnetAddressPrefix'), parameters('vnetAddressPrefix'))]", - "agentSubnet": "[if(empty(parameters('agentSubnetPrefix')), cidrSubnet(variables('vnetAddress'), 24, 0), parameters('agentSubnetPrefix'))]", - "peSubnet": "[if(empty(parameters('peSubnetPrefix')), cidrSubnet(variables('vnetAddress'), 24, 1), parameters('peSubnetPrefix'))]" - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2024-05-01", - "name": "[parameters('vnetName')]", - "location": "[parameters('location')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('vnetAddress')]" - ] - }, - "subnets": [ - { - "name": "[parameters('agentSubnetName')]", - "properties": { - "addressPrefix": "[variables('agentSubnet')]", - "delegations": [ - { - "name": "Microsoft.app/environments", - "properties": { - "serviceName": "Microsoft.App/environments" - } - } - ] - } - }, - { - "name": "[parameters('peSubnetName')]", - "properties": { - "addressPrefix": "[variables('peSubnet')]" - } - } - ] - } - } - ], - "outputs": { - "peSubnetName": { - "type": "string", - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetName": { - "type": "string", - "value": "[parameters('agentSubnetName')]" - }, - "agentSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('agentSubnetName'))]" - }, - "peSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('peSubnetName'))]" - }, - "virtualNetworkName": { - "type": "string", - "value": "[parameters('vnetName')]" - }, - "virtualNetworkId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "virtualNetworkResourceGroup": { - "type": "string", - "value": "[resourceGroup().name]" - }, - "virtualNetworkSubscriptionId": { - "type": "string", - "value": "[subscription().subscriptionId]" - } - } - } - } - }, - { - "condition": "[parameters('useExistingVnet')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "existing-vnet-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "vnetResourceGroupName": { - "value": "[parameters('existingVnetResourceGroupName')]" - }, - "vnetSubscriptionId": { - "value": "[parameters('existingVnetSubscriptionId')]" - }, - "agentSubnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetPrefix": { - "value": "[parameters('agentSubnetPrefix')]" - }, - "peSubnetPrefix": { - "value": "[parameters('peSubnetPrefix')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "3152324712046183852" - } - }, - "parameters": { - "vnetName": { - "type": "string", - "metadata": { - "description": "The name of the existing virtual network" - } - }, - "vnetSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID of virtual network (if different from current subscription)" - } - }, - "vnetResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name of the existing VNet (if different from current resource group)" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Private Endpoint subnet" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet (only needed if creating new subnet)" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet (only needed if creating new subnet)" - } - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('agent-subnet-{0}', uniqueString(deployment().name, parameters('agentSubnetName')))]", - "resourceGroup": "[parameters('vnetResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "subnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "addressPrefix": "[if(empty(parameters('agentSubnetPrefix')), createObject('value', cidrSubnet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), '2024-05-01').addressSpace.addressPrefixes[0], 24, 0)), createObject('value', parameters('agentSubnetPrefix')))]", - "delegations": { - "value": [ - { - "name": "Microsoft.App/environments", - "properties": { - "serviceName": "Microsoft.App/environments" - } - } - ] - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17043822047386586435" - } - }, - "parameters": { - "vnetName": { - "type": "string", - "metadata": { - "description": "Name of the virtual network" - } - }, - "subnetName": { - "type": "string", - "metadata": { - "description": "Name of the subnet" - } - }, - "addressPrefix": { - "type": "string", - "metadata": { - "description": "Address prefix for the subnet" - } - }, - "delegations": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Array of subnet delegations" - } - } - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('vnetName'), parameters('subnetName'))]", - "properties": { - "addressPrefix": "[parameters('addressPrefix')]", - "delegations": "[parameters('delegations')]" - } - } - ], - "outputs": { - "subnetId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[0], split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[1])]" - }, - "subnetName": { - "type": "string", - "value": "[parameters('subnetName')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('pe-subnet-{0}', uniqueString(deployment().name, parameters('peSubnetName')))]", - "resourceGroup": "[parameters('vnetResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "subnetName": { - "value": "[parameters('peSubnetName')]" - }, - "addressPrefix": "[if(empty(parameters('peSubnetPrefix')), createObject('value', cidrSubnet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), '2024-05-01').addressSpace.addressPrefixes[0], 24, 1)), createObject('value', parameters('peSubnetPrefix')))]", - "delegations": { - "value": [] - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17043822047386586435" - } - }, - "parameters": { - "vnetName": { - "type": "string", - "metadata": { - "description": "Name of the virtual network" - } - }, - "subnetName": { - "type": "string", - "metadata": { - "description": "Name of the subnet" - } - }, - "addressPrefix": { - "type": "string", - "metadata": { - "description": "Address prefix for the subnet" - } - }, - "delegations": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Array of subnet delegations" - } - } - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('vnetName'), parameters('subnetName'))]", - "properties": { - "addressPrefix": "[parameters('addressPrefix')]", - "delegations": "[parameters('delegations')]" - } - } - ], - "outputs": { - "subnetId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[0], split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[1])]" - }, - "subnetName": { - "type": "string", - "value": "[parameters('subnetName')]" - } - } - } - } - } - ], - "outputs": { - "peSubnetName": { - "type": "string", - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetName": { - "type": "string", - "value": "[parameters('agentSubnetName')]" - }, - "agentSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('agentSubnetName'))]" - }, - "peSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('peSubnetName'))]" - }, - "virtualNetworkName": { - "type": "string", - "value": "[parameters('vnetName')]" - }, - "virtualNetworkId": { - "type": "string", - "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "virtualNetworkResourceGroup": { - "type": "string", - "value": "[parameters('vnetResourceGroupName')]" - }, - "virtualNetworkSubscriptionId": { - "type": "string", - "value": "[parameters('vnetSubscriptionId')]" - } - } - } - } - } - ], - "outputs": { - "virtualNetworkName": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkName.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkName.value)]" - }, - "virtualNetworkId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkId.value)]" - }, - "virtualNetworkSubscriptionId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkSubscriptionId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkSubscriptionId.value)]" - }, - "virtualNetworkResourceGroup": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkResourceGroup.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkResourceGroup.value)]" - }, - "agentSubnetName": { - "type": "string", - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "type": "string", - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.agentSubnetId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.agentSubnetId.value)]" - }, - "peSubnetId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.peSubnetId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.peSubnetId.value)]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": { - "value": "[variables('accountName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "modelName": { - "value": "[parameters('modelName')]" - }, - "modelFormat": { - "value": "[parameters('modelFormat')]" - }, - "modelVersion": { - "value": "[parameters('modelVersion')]" - }, - "modelSkuName": { - "value": "[parameters('modelSkuName')]" - }, - "modelCapacity": { - "value": "[parameters('modelCapacity')]" - }, - "agentSubnetId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.agentSubnetId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "854097619778148359" - } - }, - "parameters": { - "accountName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "modelName": { - "type": "string" - }, - "modelFormat": { - "type": "string" - }, - "modelVersion": { - "type": "string" - }, - "modelSkuName": { - "type": "string" - }, - "modelCapacity": { - "type": "int" - }, - "agentSubnetId": { - "type": "string" - }, - "networkInjection": { - "type": "string", - "defaultValue": "true" - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('accountName')]", - "location": "[parameters('location')]", - "sku": { - "name": "S0" - }, - "kind": "AIServices", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "allowProjectManagement": true, - "customSubDomainName": "[parameters('accountName')]", - "networkAcls": { - "defaultAction": "Deny", - "virtualNetworkRules": [], - "ipRules": [], - "bypass": "AzureServices" - }, - "publicNetworkAccess": "Disabled", - "networkInjections": "[if(equals(parameters('networkInjection'), 'true'), createArray(createObject('scenario', 'agent', 'subnetArmId', parameters('agentSubnetId'), 'useMicrosoftManagedNetwork', false())), null())]", - "disableLocalAuth": false - } - }, - { - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('modelName'))]", - "sku": { - "capacity": "[parameters('modelCapacity')]", - "name": "[parameters('modelSkuName')]" - }, - "properties": { - "model": { - "name": "[parameters('modelName')]", - "format": "[parameters('modelFormat')]", - "version": "[parameters('modelVersion')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName'))]" - ] - } - ], - "outputs": { - "accountName": { - "type": "string", - "value": "[parameters('accountName')]" - }, - "accountID": { - "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName'))]" - }, - "accountTarget": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName')), '2025-04-01-preview').endpoint]" - }, - "accountPrincipalId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName')), '2025-04-01-preview', 'full').identity.principalId]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiSearchResourceId": { - "value": "[parameters('aiSearchResourceId')]" - }, - "azureStorageAccountResourceId": { - "value": "[parameters('azureStorageAccountResourceId')]" - }, - "azureCosmosDBAccountResourceId": { - "value": "[parameters('azureCosmosDBAccountResourceId')]" - }, - "existingDnsZones": { - "value": "[parameters('existingDnsZones')]" - }, - "dnsZoneNames": { - "value": "[parameters('dnsZoneNames')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "7641310640078958122" - } - }, - "parameters": { - "aiSearchResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the AI Search Service." - } - }, - "azureStorageAccountResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Azure Storage Account." - } - }, - "azureCosmosDBAccountResourceId": { - "type": "string", - "metadata": { - "description": "ResourceId of Cosmos DB Account" - } - }, - "existingDnsZones": { - "type": "object", - "metadata": { - "description": "Object mapping DNS zone names to their resource group, or empty string to indicate creation" - } - }, - "dnsZoneNames": { - "type": "array", - "metadata": { - "description": "List of private DNS zone names to validate" - } - } - }, - "variables": { - "storagePassedIn": "[not(equals(parameters('azureStorageAccountResourceId'), ''))]", - "searchPassedIn": "[not(equals(parameters('aiSearchResourceId'), ''))]", - "cosmosPassedIn": "[not(equals(parameters('azureCosmosDBAccountResourceId'), ''))]", - "storageParts": "[split(parameters('azureStorageAccountResourceId'), '/')]", - "azureStorageSubscriptionId": "[if(and(variables('storagePassedIn'), greater(length(variables('storageParts')), 2)), variables('storageParts')[2], subscription().subscriptionId)]", - "azureStorageResourceGroupName": "[if(and(variables('storagePassedIn'), greater(length(variables('storageParts')), 4)), variables('storageParts')[4], resourceGroup().name)]", - "acsParts": "[split(parameters('aiSearchResourceId'), '/')]", - "aiSearchServiceSubscriptionId": "[if(and(variables('searchPassedIn'), greater(length(variables('acsParts')), 2)), variables('acsParts')[2], subscription().subscriptionId)]", - "aiSearchServiceResourceGroupName": "[if(and(variables('searchPassedIn'), greater(length(variables('acsParts')), 4)), variables('acsParts')[4], resourceGroup().name)]", - "cosmosParts": "[split(parameters('azureCosmosDBAccountResourceId'), '/')]", - "cosmosDBSubscriptionId": "[if(and(variables('cosmosPassedIn'), greater(length(variables('cosmosParts')), 2)), variables('cosmosParts')[2], subscription().subscriptionId)]", - "cosmosDBResourceGroupName": "[if(and(variables('cosmosPassedIn'), greater(length(variables('cosmosParts')), 4)), variables('cosmosParts')[4], resourceGroup().name)]", - "dnsZoneTypes": [ - "Microsoft.Network/privateDnsZones" - ] - }, - "resources": [], - "outputs": { - "aiSearchExists": { - "type": "bool", - "value": "[and(variables('searchPassedIn'), equals(last(split(parameters('aiSearchResourceId'), '/')), variables('acsParts')[8]))]" - }, - "cosmosDBExists": { - "type": "bool", - "value": "[and(variables('cosmosPassedIn'), equals(last(split(parameters('azureCosmosDBAccountResourceId'), '/')), variables('cosmosParts')[8]))]" - }, - "azureStorageExists": { - "type": "bool", - "value": "[and(variables('storagePassedIn'), equals(last(split(parameters('azureStorageAccountResourceId'), '/')), variables('storageParts')[8]))]" - }, - "aiSearchServiceSubscriptionId": { - "type": "string", - "value": "[variables('aiSearchServiceSubscriptionId')]" - }, - "aiSearchServiceResourceGroupName": { - "type": "string", - "value": "[variables('aiSearchServiceResourceGroupName')]" - }, - "cosmosDBSubscriptionId": { - "type": "string", - "value": "[variables('cosmosDBSubscriptionId')]" - }, - "cosmosDBResourceGroupName": { - "type": "string", - "value": "[variables('cosmosDBResourceGroupName')]" - }, - "azureStorageSubscriptionId": { - "type": "string", - "value": "[variables('azureStorageSubscriptionId')]" - }, - "azureStorageResourceGroupName": { - "type": "string", - "value": "[variables('azureStorageResourceGroupName')]" - }, - "dnsZoneExists": { - "type": "array", - "copy": { - "count": "[length(parameters('dnsZoneNames'))]", - "input": { - "name": "[parameters('dnsZoneNames')[copyIndex()]]", - "exists": "[not(empty(parameters('existingDnsZones')[parameters('dnsZoneNames')[copyIndex()]]))]" - } - } - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('dependencies-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "azureStorageName": { - "value": "[variables('azureStorageName')]" - }, - "aiSearchName": { - "value": "[variables('aiSearchName')]" - }, - "cosmosDBName": { - "value": "[variables('cosmosDBName')]" - }, - "aiSearchResourceId": { - "value": "[parameters('aiSearchResourceId')]" - }, - "aiSearchExists": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchExists.value]" - }, - "azureStorageAccountResourceId": { - "value": "[parameters('azureStorageAccountResourceId')]" - }, - "azureStorageExists": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageExists.value]" - }, - "cosmosDBResourceId": { - "value": "[parameters('azureCosmosDBAccountResourceId')]" - }, - "cosmosDBExists": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBExists.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "2754228344238136934" - } - }, - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Azure region of the deployment" - } - }, - "aiSearchName": { - "type": "string", - "metadata": { - "description": "The name of the AI Search resource" - } - }, - "azureStorageName": { - "type": "string", - "metadata": { - "description": "Name of the storage account" - } - }, - "cosmosDBName": { - "type": "string", - "metadata": { - "description": "Name of the new Cosmos DB account" - } - }, - "aiSearchResourceId": { - "type": "string", - "metadata": { - "description": "The AI Search Service full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "azureStorageAccountResourceId": { - "type": "string", - "metadata": { - "description": "The AI Storage Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "cosmosDBResourceId": { - "type": "string", - "metadata": { - "description": "The Cosmos DB Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "aiSearchExists": { - "type": "bool" - }, - "azureStorageExists": { - "type": "bool" - }, - "cosmosDBExists": { - "type": "bool" - }, - "noZRSRegions": { - "type": "array", - "defaultValue": [ - "southindia", - "westus" - ] - }, - "sku": { - "type": "object", - "defaultValue": "[if(contains(parameters('noZRSRegions'), parameters('location')), createObject('name', 'Standard_GRS'), createObject('name', 'Standard_ZRS'))]" - } - }, - "variables": { - "cosmosParts": "[split(parameters('cosmosDBResourceId'), '/')]", - "canaryRegions": [ - "eastus2euap", - "centraluseuap" - ], - "cosmosDbRegion": "[if(contains(variables('canaryRegions'), parameters('location')), 'westus', parameters('location'))]", - "acsParts": "[split(parameters('aiSearchResourceId'), '/')]", - "azureStorageParts": "[split(parameters('azureStorageAccountResourceId'), '/')]" - }, - "resources": [ - { - "condition": "[not(parameters('cosmosDBExists'))]", - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2024-11-15", - "name": "[parameters('cosmosDBName')]", - "location": "[variables('cosmosDbRegion')]", - "kind": "GlobalDocumentDB", - "properties": { - "consistencyPolicy": { - "defaultConsistencyLevel": "Session" - }, - "disableLocalAuth": true, - "enableAutomaticFailover": false, - "enableMultipleWriteLocations": false, - "publicNetworkAccess": "Disabled", - "enableFreeTier": false, - "locations": [ - { - "locationName": "[parameters('location')]", - "failoverPriority": 0, - "isZoneRedundant": false - } - ], - "databaseAccountOfferType": "Standard" - } - }, - { - "condition": "[not(parameters('aiSearchExists'))]", - "type": "Microsoft.Search/searchServices", - "apiVersion": "2024-06-01-preview", - "name": "[parameters('aiSearchName')]", - "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "disableLocalAuth": false, - "authOptions": { - "aadOrApiKey": { - "aadAuthFailureMode": "http401WithBearerChallenge" - } - }, - "encryptionWithCmk": { - "enforcement": "Unspecified" - }, - "hostingMode": "default", - "partitionCount": 1, - "publicNetworkAccess": "disabled", - "replicaCount": 1, - "semanticSearch": "disabled", - "networkRuleSet": { - "bypass": "None", - "ipRules": [] - } - }, - "sku": { - "name": "standard" - } - }, - { - "condition": "[not(parameters('azureStorageExists'))]", - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-05-01", - "name": "[parameters('azureStorageName')]", - "location": "[parameters('location')]", - "kind": "StorageV2", - "sku": "[parameters('sku')]", - "properties": { - "minimumTlsVersion": "TLS1_2", - "allowBlobPublicAccess": false, - "publicNetworkAccess": "Disabled", - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "Deny", - "virtualNetworkRules": [] - }, - "allowSharedKeyAccess": false - } - } - ], - "outputs": { - "aiSearchName": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), variables('acsParts')[8], parameters('aiSearchName'))]" - }, - "aiSearchID": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('acsParts')[2], variables('acsParts')[4]), 'Microsoft.Search/searchServices', variables('acsParts')[8]), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]" - }, - "aiSearchServiceResourceGroupName": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), variables('acsParts')[4], resourceGroup().name)]" - }, - "aiSearchServiceSubscriptionId": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), variables('acsParts')[2], subscription().subscriptionId)]" - }, - "azureStorageName": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), variables('azureStorageParts')[8], parameters('azureStorageName'))]" - }, - "azureStorageId": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('azureStorageParts')[2], variables('azureStorageParts')[4]), 'Microsoft.Storage/storageAccounts', variables('azureStorageParts')[8]), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]" - }, - "azureStorageResourceGroupName": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), variables('azureStorageParts')[4], resourceGroup().name)]" - }, - "azureStorageSubscriptionId": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), variables('azureStorageParts')[2], subscription().subscriptionId)]" - }, - "cosmosDBName": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), variables('cosmosParts')[8], parameters('cosmosDBName'))]" - }, - "cosmosDBId": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('cosmosParts')[2], variables('cosmosParts')[4]), 'Microsoft.DocumentDB/databaseAccounts', variables('cosmosParts')[8]), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]" - }, - "cosmosDBResourceGroupName": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), variables('cosmosParts')[4], resourceGroup().name)]" - }, - "cosmosDBSubscriptionId": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), variables('cosmosParts')[2], subscription().subscriptionId)]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-private-endpoint', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiAccountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))), '2025-04-01').outputs.accountName.value]" - }, - "aiSearchName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchName.value]" - }, - "storageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "cosmosDBName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "vnetName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.virtualNetworkName.value]" - }, - "peSubnetName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.peSubnetName.value]" - }, - "suffix": { - "value": "[variables('uniqueSuffix')]" - }, - "vnetResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.virtualNetworkResourceGroup.value]" - }, - "vnetSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.virtualNetworkSubscriptionId.value]" - }, - "cosmosDBSubscriptionId": { - "value": "[variables('cosmosDBSubscriptionId')]" - }, - "cosmosDBResourceGroupName": { - "value": "[variables('cosmosDBResourceGroupName')]" - }, - "aiSearchSubscriptionId": { - "value": "[variables('aiSearchServiceSubscriptionId')]" - }, - "aiSearchResourceGroupName": { - "value": "[variables('aiSearchServiceResourceGroupName')]" - }, - "storageAccountResourceGroupName": { - "value": "[variables('azureStorageResourceGroupName')]" - }, - "storageAccountSubscriptionId": { - "value": "[variables('azureStorageSubscriptionId')]" - }, - "existingDnsZones": { - "value": "[parameters('existingDnsZones')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "8094529554453089222" - } - }, - "parameters": { - "aiAccountName": { - "type": "string", - "metadata": { - "description": "Name of the AI Foundry account" - } - }, - "aiSearchName": { - "type": "string", - "metadata": { - "description": "Name of the AI Search service" - } - }, - "storageName": { - "type": "string", - "metadata": { - "description": "Name of the storage account" - } - }, - "cosmosDBName": { - "type": "string", - "metadata": { - "description": "Name of the Cosmos DB account" - } - }, - "vnetName": { - "type": "string", - "metadata": { - "description": "Name of the Vnet" - } - }, - "peSubnetName": { - "type": "string", - "metadata": { - "description": "Name of the Customer subnet" - } - }, - "suffix": { - "type": "string", - "metadata": { - "description": "Suffix for unique resource names" - } - }, - "vnetResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name for existing Virtual Network (if different from current resource group)" - } - }, - "vnetSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for Virtual Network" - } - }, - "storageAccountResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name for Storage Account" - } - }, - "storageAccountSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for Storage account" - } - }, - "aiSearchSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for AI Search service" - } - }, - "aiSearchResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name for AI Search service" - } - }, - "cosmosDBSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for Cosmos DB account" - } - }, - "cosmosDBResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource group name for Cosmos DB account" - } - }, - "existingDnsZones": { - "type": "object", - "defaultValue": { - "privatelink.services.ai.azure.com": "", - "privatelink.openai.azure.com": "", - "privatelink.cognitiveservices.azure.com": "", - "privatelink.search.windows.net": "", - "[format('privatelink.blob.{0}', environment().suffixes.storage)]": "", - "privatelink.documents.azure.com": "" - }, - "metadata": { - "description": "Map of DNS zone FQDNs to resource group names. If provided, reference existing DNS zones in this resource group instead of creating them." - } - } - }, - "variables": { - "aiServicesDnsZoneName": "privatelink.services.ai.azure.com", - "openAiDnsZoneName": "privatelink.openai.azure.com", - "cognitiveServicesDnsZoneName": "privatelink.cognitiveservices.azure.com", - "aiSearchDnsZoneName": "privatelink.search.windows.net", - "storageDnsZoneName": "[format('privatelink.blob.{0}', environment().suffixes.storage)]", - "cosmosDBDnsZoneName": "privatelink.documents.azure.com", - "aiServicesDnsZoneRG": "[parameters('existingDnsZones')[variables('aiServicesDnsZoneName')]]", - "openAiDnsZoneRG": "[parameters('existingDnsZones')[variables('openAiDnsZoneName')]]", - "cognitiveServicesDnsZoneRG": "[parameters('existingDnsZones')[variables('cognitiveServicesDnsZoneName')]]", - "aiSearchDnsZoneRG": "[parameters('existingDnsZones')[variables('aiSearchDnsZoneName')]]", - "storageDnsZoneRG": "[parameters('existingDnsZones')[variables('storageDnsZoneName')]]", - "cosmosDBDnsZoneRG": "[parameters('existingDnsZones')[variables('cosmosDBDnsZoneName')]]", - "aiServicesDnsZoneId": "[if(empty(variables('aiServicesDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('aiServicesDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName')))]", - "openAiDnsZoneId": "[if(empty(variables('openAiDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('openAiDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName')))]", - "cognitiveServicesDnsZoneId": "[if(empty(variables('cognitiveServicesDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('cognitiveServicesDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName')))]", - "aiSearchDnsZoneId": "[if(empty(variables('aiSearchDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('aiSearchDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName')))]", - "storageDnsZoneId": "[if(empty(variables('storageDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('storageDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('storageDnsZoneName')))]", - "cosmosDBDnsZoneId": "[if(empty(variables('cosmosDBDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('cosmosDBDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName')))]" - }, - "resources": [ - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('aiAccountName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('aiAccountName'))]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('aiAccountName'))]", - "groupIds": [ - "account" - ] - } - } - ] - } - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('aiSearchName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('aiSearchName'))]", - "properties": { - "privateLinkServiceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('aiSearchSubscriptionId'), parameters('aiSearchResourceGroupName')), 'Microsoft.Search/searchServices', parameters('aiSearchName'))]", - "groupIds": [ - "searchService" - ] - } - } - ] - } - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('storageName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('storageName'))]", - "properties": { - "privateLinkServiceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('storageAccountSubscriptionId'), parameters('storageAccountResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('storageName'))]", - "groupIds": [ - "blob" - ] - } - } - ] - } - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('cosmosDBName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('cosmosDBName'))]", - "properties": { - "privateLinkServiceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", - "groupIds": [ - "Sql" - ] - } - } - ] - } - }, - { - "condition": "[empty(variables('aiServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('aiServicesDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('openAiDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('openAiDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('cognitiveServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('cognitiveServicesDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('aiSearchDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('aiSearchDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('storageDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('storageDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('cosmosDBDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('cosmosDBDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('aiServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('aiServicesDnsZoneName'), format('aiServices-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('openAiDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('openAiDnsZoneName'), format('aiServicesOpenAI-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('cognitiveServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('cognitiveServicesDnsZoneName'), format('aiServicesCognitiveServices-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('aiSearchDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('aiSearchDnsZoneName'), format('aiSearch-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('storageDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('storageDnsZoneName'), format('storage-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('cosmosDBDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('cosmosDBDnsZoneName'), format('cosmosDB-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName'))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('aiAccountName')), format('{0}-dns-group', parameters('aiAccountName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-aiserv-config', parameters('aiAccountName'))]", - "properties": { - "privateDnsZoneId": "[variables('aiServicesDnsZoneId')]" - } - }, - { - "name": "[format('{0}-dns-openai-config', parameters('aiAccountName'))]", - "properties": { - "privateDnsZoneId": "[variables('openAiDnsZoneId')]" - } - }, - { - "name": "[format('{0}-dns-cogserv-config', parameters('aiAccountName'))]", - "properties": { - "privateDnsZoneId": "[variables('cognitiveServicesDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('aiAccountName')))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('aiServicesDnsZoneName'), format('aiServices-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('cognitiveServicesDnsZoneName'), format('aiServicesCognitiveServices-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('openAiDnsZoneName'), format('aiServicesOpenAI-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName'))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('aiSearchName')), format('{0}-dns-group', parameters('aiSearchName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-config', parameters('aiSearchName'))]", - "properties": { - "privateDnsZoneId": "[variables('aiSearchDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('aiSearchDnsZoneName'), format('aiSearch-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('aiSearchName')))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('storageName')), format('{0}-dns-group', parameters('storageName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-config', parameters('storageName'))]", - "properties": { - "privateDnsZoneId": "[variables('storageDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('storageDnsZoneName'), format('storage-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('storageName')))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('cosmosDBName')), format('{0}-dns-group', parameters('cosmosDBName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-config', parameters('cosmosDBName'))]", - "properties": { - "privateDnsZoneId": "[variables('cosmosDBDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('cosmosDBDnsZoneName'), format('cosmosDB-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('cosmosDBName')))]" - ] - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "projectName": { - "value": "[variables('projectName')]" - }, - "projectDescription": { - "value": "[parameters('projectDescription')]" - }, - "displayName": { - "value": "[parameters('displayName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "aiSearchName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchName.value]" - }, - "aiSearchServiceResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchServiceResourceGroupName.value]" - }, - "aiSearchServiceSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchServiceSubscriptionId.value]" - }, - "cosmosDBName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "cosmosDBSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBSubscriptionId.value]" - }, - "cosmosDBResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBResourceGroupName.value]" - }, - "azureStorageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "azureStorageSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageSubscriptionId.value]" - }, - "azureStorageResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageResourceGroupName.value]" - }, - "accountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))), '2025-04-01').outputs.accountName.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "5095087340309076800" - } - }, - "parameters": { - "accountName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "projectName": { - "type": "string" - }, - "projectDescription": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "aiSearchName": { - "type": "string" - }, - "aiSearchServiceResourceGroupName": { - "type": "string" - }, - "aiSearchServiceSubscriptionId": { - "type": "string" - }, - "cosmosDBName": { - "type": "string" - }, - "cosmosDBSubscriptionId": { - "type": "string" - }, - "cosmosDBResourceGroupName": { - "type": "string" - }, - "azureStorageName": { - "type": "string" - }, - "azureStorageSubscriptionId": { - "type": "string" - }, - "azureStorageResourceGroupName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('cosmosDBName'))]", - "properties": { - "category": "CosmosDB", - "target": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')), '2024-12-01-preview').documentEndpoint]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", - "location": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')), '2024-12-01-preview', 'full').location]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - ] - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('azureStorageName'))]", - "properties": { - "category": "AzureStorageAccount", - "target": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('azureStorageSubscriptionId'), parameters('azureStorageResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('azureStorageName')), '2023-05-01').primaryEndpoints.blob]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('azureStorageSubscriptionId'), parameters('azureStorageResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('azureStorageName'))]", - "location": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('azureStorageSubscriptionId'), parameters('azureStorageResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('azureStorageName')), '2023-05-01', 'full').location]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - ] - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('aiSearchName'))]", - "properties": { - "category": "CognitiveSearch", - "target": "[format('https://{0}.search.windows.net', parameters('aiSearchName'))]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('aiSearchServiceSubscriptionId'), parameters('aiSearchServiceResourceGroupName')), 'Microsoft.Search/searchServices', parameters('aiSearchName'))]", - "location": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('aiSearchServiceSubscriptionId'), parameters('aiSearchServiceResourceGroupName')), 'Microsoft.Search/searchServices', parameters('aiSearchName')), '2024-06-01-preview', 'full').location]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - ] - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('projectName'))]", - "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "description": "[parameters('projectDescription')]", - "displayName": "[parameters('displayName')]" - } - } - ], - "outputs": { - "projectName": { - "type": "string", - "value": "[parameters('projectName')]" - }, - "projectId": { - "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - }, - "projectPrincipalId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName')), '2025-04-01-preview', 'full').identity.principalId]" - }, - "projectWorkspaceId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName')), '2025-04-01-preview').internalId]" - }, - "cosmosDBConnection": { - "type": "string", - "value": "[parameters('cosmosDBName')]" - }, - "azureStorageConnection": { - "type": "string", - "value": "[parameters('azureStorageName')]" - }, - "aiSearchConnection": { - "type": "string", - "value": "[parameters('aiSearchName')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "projectWorkspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectWorkspaceId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "6910483561575524105" - } - }, - "parameters": { - "projectWorkspaceId": { - "type": "string" - } - }, - "variables": { - "part1": "[substring(parameters('projectWorkspaceId'), 0, 8)]", - "part2": "[substring(parameters('projectWorkspaceId'), 8, 4)]", - "part3": "[substring(parameters('projectWorkspaceId'), 12, 4)]", - "part4": "[substring(parameters('projectWorkspaceId'), 16, 4)]", - "part5": "[substring(parameters('projectWorkspaceId'), 20, 12)]", - "formattedGuid": "[format('{0}-{1}-{2}-{3}-{4}', variables('part1'), variables('part2'), variables('part3'), variables('part4'), variables('part5'))]" - }, - "resources": [], - "outputs": { - "projectWorkspaceIdGuid": { - "type": "string", - "value": "[variables('formattedGuid')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('storage-{0}-{1}-deployment', variables('azureStorageName'), variables('uniqueSuffix'))]", - "subscriptionId": "[variables('azureStorageSubscriptionId')]", - "resourceGroup": "[variables('azureStorageResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "azureStorageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "14683840003859985069" - } - }, - "parameters": { - "azureStorageName": { - "type": "string" - }, - "projectPrincipalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('azureStorageName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "principalType": "ServicePrincipal" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('cosmos-account-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('cosmosDBSubscriptionId')]", - "resourceGroup": "[variables('cosmosDBResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "cosmosDBName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "25128059954858801" - } - }, - "parameters": { - "cosmosDBName": { - "type": "string", - "metadata": { - "description": "Name of the Cosmos DB resource" - } - }, - "projectPrincipalId": { - "type": "string", - "metadata": { - "description": "Principal ID of the AI project" - } - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", - "principalType": "ServicePrincipal" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('ai-search-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('aiSearchServiceSubscriptionId')]", - "resourceGroup": "[variables('aiSearchServiceResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiSearchName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchName.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "7968115481508840" - } - }, - "parameters": { - "aiSearchName": { - "type": "string", - "metadata": { - "description": "Name of the AI Search resource" - } - }, - "projectPrincipalId": { - "type": "string", - "metadata": { - "description": "Principal ID of the AI project" - } - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", - "principalType": "ServicePrincipal" - } - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", - "principalType": "ServicePrincipal" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('capabilityHost-configuration-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))), '2025-04-01').outputs.accountName.value]" - }, - "projectName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectName.value]" - }, - "cosmosDBConnection": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBConnection.value]" - }, - "azureStorageConnection": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageConnection.value]" - }, - "aiSearchConnection": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchConnection.value]" - }, - "projectCapHost": { - "value": "[parameters('projectCapHost')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17458377866351620215" - } - }, - "parameters": { - "cosmosDBConnection": { - "type": "string" - }, - "azureStorageConnection": { - "type": "string" - }, - "aiSearchConnection": { - "type": "string" - }, - "projectName": { - "type": "string" - }, - "accountName": { - "type": "string" - }, - "projectCapHost": { - "type": "string" - } - }, - "variables": { - "threadConnections": [ - "[format('{0}', parameters('cosmosDBConnection'))]" - ], - "storageConnections": [ - "[format('{0}', parameters('azureStorageConnection'))]" - ], - "vectorStoreConnections": [ - "[format('{0}', parameters('aiSearchConnection'))]" - ] - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts/projects/capabilityHosts", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('projectCapHost'))]", - "properties": { - "capabilityHostKind": "Agents", - "vectorStoreConnections": "[variables('vectorStoreConnections')]", - "storageConnections": "[variables('storageConnections')]", - "threadStorageConnections": "[variables('threadConnections')]" - } - } - ], - "outputs": { - "projectCapHost": { - "type": "string", - "value": "[parameters('projectCapHost')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('aiSearchServiceSubscriptionId'), variables('aiSearchServiceResourceGroupName')), 'Microsoft.Resources/deployments', format('ai-search-ra-{0}-deployment', variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('cosmosDBSubscriptionId'), variables('cosmosDBResourceGroupName')), 'Microsoft.Resources/deployments', format('cosmos-account-ra-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('azureStorageSubscriptionId'), variables('azureStorageResourceGroupName')), 'Microsoft.Resources/deployments', format('storage-{0}-{1}-deployment', variables('azureStorageName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('storage-containers-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('azureStorageSubscriptionId')]", - "resourceGroup": "[variables('azureStorageResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiProjectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - }, - "storageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "workspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.projectWorkspaceIdGuid.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "13874725855824693255" - } - }, - "parameters": { - "storageName": { - "type": "string", - "metadata": { - "description": "Name of the storage account" - } - }, - "aiProjectPrincipalId": { - "type": "string", - "metadata": { - "description": "Principal ID of the AI Project" - } - }, - "workspaceId": { - "type": "string", - "metadata": { - "description": "Workspace Id of the AI Project" - } - } - }, - "variables": { - "conditionStr": "[format('((!(ActionMatches{{''Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/read''}}) AND !(ActionMatches{{''Microsoft.Storage/storageAccounts/blobServices/containers/blobs/filter/action''}}) AND !(ActionMatches{{''Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/write''}}) ) OR (@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringStartsWithIgnoreCase ''{0}'' AND @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringLikeIgnoreCase ''*-azureml-agent''))', parameters('workspaceId'))]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageName'))]", - "name": "[guid(resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'), resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')))]", - "properties": { - "principalId": "[parameters('aiProjectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", - "principalType": "ServicePrincipal", - "conditionVersion": "2.0", - "condition": "[variables('conditionStr')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('capabilityHost-configuration-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('cosmos-containers-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('cosmosDBSubscriptionId')]", - "resourceGroup": "[variables('cosmosDBResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "cosmosAccountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "projectWorkspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.projectWorkspaceIdGuid.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17187611271934567223" - } - }, - "parameters": { - "cosmosAccountName": { - "type": "string", - "metadata": { - "description": "Name of the AI Search resource" - } - }, - "projectPrincipalId": { - "type": "string", - "metadata": { - "description": "Project name" - } - }, - "projectWorkspaceId": { - "type": "string" - } - }, - "variables": { - "roleDefinitionId": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('cosmosAccountName'), '00000000-0000-0000-0000-000000000002')]", - "accountScope": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/{2}/dbs/enterprise_memory', subscription().subscriptionId, resourceGroup().name, parameters('cosmosAccountName'))]" - }, - "resources": [ - { - "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", - "apiVersion": "2022-05-15", - "name": "[format('{0}/{1}', parameters('cosmosAccountName'), guid(parameters('projectWorkspaceId'), parameters('cosmosAccountName'), variables('roleDefinitionId'), parameters('projectPrincipalId')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[variables('roleDefinitionId')]", - "scope": "[variables('accountScope')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('capabilityHost-configuration-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('azureStorageSubscriptionId'), variables('azureStorageResourceGroupName')), 'Microsoft.Resources/deployments', format('storage-containers-ra-{0}-deployment', variables('uniqueSuffix')))]" - ] - } - ] -} \ No newline at end of file diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/azuredeploy.parameters.json b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/azuredeploy.parameters.json deleted file mode 100644 index f3621743e..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/azuredeploy.parameters.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "location": { - "value": "eastus2" - }, - "aiServices": { - "value": "" - }, - "modelName": { - "value": "" - }, - "modelFormat": { - "value": "" - }, - "modelVersion": { - "value": "" - }, - "modelSkuName": { - "value": "" - }, - "modelCapacity": { - "value": 0 - }, - "deploymentTimestamp": { - "value": "" - }, - "firstProjectName": { - "value": "" - }, - "projectDescription": { - "value": "" - }, - "displayName": { - "value": "" - }, - "vnetName": { - "value": "" - }, - "agentSubnetName": { - "value": "" - }, - "peSubnetName": { - "value": "" - }, - "existingVnetResourceId": { - "value": "" - }, - "vnetAddressPrefix": { - "value": "" - }, - "agentSubnetPrefix": { - "value": "" - }, - "peSubnetPrefix": { - "value": "" - }, - "aiSearchResourceId": { - "value": "" - }, - "azureStorageAccountResourceId": { - "value": "" - }, - "azureCosmosDBAccountResourceId": { - "value": "" - }, - "projectCapHost": { - "value": "" - }, - "existingDnsZones": { - "value": { - "privatelink.services.ai.azure.com": "", - "privatelink.openai.azure.com": "", - "privatelink.cognitiveservices.azure.com": "", - "privatelink.search.windows.net": "", - "privatelink.blob.core.windows.net": "", - "privatelink.documents.azure.com": "" - } - } - } -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/createCapHost.sh b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/createCapHost.sh deleted file mode 100644 index 00de21300..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/createCapHost.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash - -# Script to create the account capability host - -# Prompt for required information -read -p "Enter Subscription ID: " subscription_id -read -p "Enter Resource Group name: " resource_group -read -p "Enter Foundry Account or Project name: " account_name -read -p "Enter CapabilityHost name: " caphost_name -read -p "Enter Customer full ARM subnet ResourceId: " subnet_resource_id - -# Get Azure access token -echo "Getting Azure access token..." -access_token=$(az account get-access-token --query accessToken -o tsv) - -if [ -z "$access_token" ]; then - echo "Error: Failed to get access token. Please make sure you're logged in with 'az login'" - exit 1 -fi - -# Construct the API URL -api_url="https://management.azure.com/subscriptions/${subscription_id}/resourceGroups/${resource_group}/providers/Microsoft.CognitiveServices/accounts/${account_name}/capabilityHosts/${caphost_name}?api-version=2025-04-01-preview" - -echo "Creating capability host: ${caphost_name}" -echo "API URL: ${api_url}" - -# Send PUT request and capture headers -echo "Sending PUT request..." -response_headers=$(mktemp) - -read -r -d '' BODY < Linked to Agent Subnet for secure runtime execution', ha='center', va='center', - fontsize=9, color=COLORS['text_dark']) - ax.text(7, 2.6, '(From Layer 1: VNet Agent Subnet)', ha='center', va='center', - fontsize=9, color=COLORS['primary'], style='italic') - - plt.tight_layout() - plt.savefig('layer3_ai_services.png', dpi=150, bbox_inches='tight', - facecolor='white', edgecolor='none') - plt.close() - print("Generated layer3_ai_services.png") - - -# ============================================================================= -# LAYER 4: Project + Connections -# ============================================================================= -def generate_layer4(): - fig, ax = setup_figure('Layer 4: Project & Service Connections', figsize=(14, 10)) - - # Parent account container (dashed) - parent = FancyBboxPatch( - (0.5, 1), 13, 6.3, - boxstyle="round,pad=0.02,rounding_size=0.15", - facecolor=COLORS['bg_light'], - edgecolor=COLORS['border_light'], - linewidth=2, - linestyle='--' - ) - ax.add_patch(parent) - ax.text(7, 7.1, 'AI Services Account (Parent)', ha='center', va='center', - fontsize=10, color=COLORS['primary'], style='italic') - - # Project box - draw_box(ax, 1, 1.5, 12, 5.3, COLORS['primary'], COLORS['primary_dark'], 3) - ax.text(7, 6.5, 'FOUNDRY PROJECT', ha='center', va='center', - fontsize=14, fontweight='bold', color=COLORS['text_light']) - ax.text(7, 6.1, 'Microsoft.CognitiveServices/accounts/projects', ha='center', va='center', - fontsize=9, color=COLORS['bg_light'], family='monospace') - - # Identity box - draw_box(ax, 1.5, 5, 3, 0.9, COLORS['white'], COLORS['border_light'], 1.5) - ax.text(3, 5.6, 'Identity (MSI)', ha='center', va='center', - fontsize=9, fontweight='bold', color=COLORS['primary_dark']) - ax.text(3, 5.25, 'System-Assigned', ha='center', va='center', - fontsize=8, color=COLORS['text_dark']) - - # Connections header - ax.text(7, 4.6, '-- Service Connections (AAD Auth) --', ha='center', va='center', - fontsize=11, fontweight='bold', color=COLORS['text_light']) - - # Connection boxes - conn_y = 3.0 - conn_h = 1.2 - - # Cosmos Connection - draw_box(ax, 1.5, conn_y, 3.5, conn_h, COLORS['bg_medium'], COLORS['text_light'], 1.5) - ax.text(3.25, conn_y + 0.85, 'CosmosDB Connection', ha='center', va='center', - fontsize=9, fontweight='bold', color=COLORS['primary_dark']) - ax.text(3.25, conn_y + 0.4, 'category: CosmosDB', ha='center', va='center', - fontsize=8, color=COLORS['text_dark'], family='monospace') - - # Storage Connection - draw_box(ax, 5.25, conn_y, 3.5, conn_h, COLORS['bg_medium'], COLORS['text_light'], 1.5) - ax.text(7, conn_y + 0.85, 'Storage Connection', ha='center', va='center', - fontsize=9, fontweight='bold', color=COLORS['primary_dark']) - ax.text(7, conn_y + 0.4, 'category: AzureStorage', ha='center', va='center', - fontsize=8, color=COLORS['text_dark'], family='monospace') - - # AI Search Connection - draw_box(ax, 9, conn_y, 3.5, conn_h, COLORS['bg_medium'], COLORS['text_light'], 1.5) - ax.text(10.75, conn_y + 0.85, 'AI Search Connection', ha='center', va='center', - fontsize=9, fontweight='bold', color=COLORS['primary_dark']) - ax.text(10.75, conn_y + 0.4, 'category: CognitiveSearch', ha='center', va='center', - fontsize=8, color=COLORS['text_dark'], family='monospace') - - # Key insight box - draw_box(ax, 1.5, 1.7, 11, 0.9, COLORS['white'], COLORS['highlight'], 2) - ax.text(7, 2.15, 'KEY INSIGHT: Connections store target endpoints + auth method.', ha='center', va='center', - fontsize=9, fontweight='bold', color=COLORS['primary_dark']) - - plt.tight_layout() - plt.savefig('layer4_project_connections.png', dpi=150, bbox_inches='tight', - facecolor='white', edgecolor='none') - plt.close() - print("Generated layer4_project_connections.png") - - -# ============================================================================= -# LAYER 5: Capability Host -# ============================================================================= -def generate_layer5(): - fig, ax = setup_figure('Layer 5: Capability Host - The Activator', figsize=(14, 10)) - - # Main capability host box - draw_box(ax, 0.5, 2.8, 13, 4.5, COLORS['primary'], COLORS['primary_dark'], 3) - ax.text(7, 7, 'PROJECT CAPABILITY HOST', ha='center', va='center', - fontsize=16, fontweight='bold', color=COLORS['text_light']) - ax.text(7, 6.55, 'Microsoft.CognitiveServices/accounts/projects/capabilityHosts', ha='center', va='center', - fontsize=9, color=COLORS['bg_light'], family='monospace') - - # capabilityHostKind box - draw_box(ax, 1, 5.5, 3.5, 1, COLORS['white'], COLORS['border_light'], 2) - ax.text(2.75, 6.15, 'capabilityHostKind', ha='center', va='center', - fontsize=9, fontweight='bold', color=COLORS['primary_dark']) - ax.text(2.75, 5.75, '"Agents"', ha='center', va='center', - fontsize=11, fontweight='bold', color=COLORS['primary'], family='monospace') - - # Connection bindings header - ax.text(7, 5.1, '-- Connection Bindings --', ha='center', va='center', - fontsize=11, fontweight='bold', color=COLORS['text_light']) - - bind_y = 3.9 - bind_h = 0.9 - - # Vector Store Connections - draw_box(ax, 1, bind_y, 3.8, bind_h, COLORS['bg_medium'], COLORS['text_light'], 1.5) - ax.text(2.9, bind_y + 0.6, 'vectorStoreConnections', ha='center', va='center', - fontsize=8, fontweight='bold', color=COLORS['primary_dark'], family='monospace') - ax.text(2.9, bind_y + 0.25, '-> AI Search', ha='center', va='center', - fontsize=9, color=COLORS['text_dark']) - - # Storage Connections - draw_box(ax, 5.1, bind_y, 3.8, bind_h, COLORS['bg_medium'], COLORS['text_light'], 1.5) - ax.text(7, bind_y + 0.6, 'storageConnections', ha='center', va='center', - fontsize=8, fontweight='bold', color=COLORS['primary_dark'], family='monospace') - ax.text(7, bind_y + 0.25, '-> Azure Storage', ha='center', va='center', - fontsize=9, color=COLORS['text_dark']) - - # Thread Storage Connections - draw_box(ax, 9.2, bind_y, 3.8, bind_h, COLORS['bg_medium'], COLORS['text_light'], 1.5) - ax.text(11.1, bind_y + 0.6, 'threadStorageConnections', ha='center', va='center', - fontsize=8, fontweight='bold', color=COLORS['primary_dark'], family='monospace') - ax.text(11.1, bind_y + 0.25, '-> Cosmos DB', ha='center', va='center', - fontsize=9, color=COLORS['text_dark']) - - # Runtime info box - draw_box(ax, 1, 3, 12, 0.7, COLORS['bg_light'], COLORS['text_light'], 1.5) - ax.text(7, 3.35, 'RUNTIME: Creates Container App environment in Agent Subnet | Provisions infrastructure', - ha='center', va='center', fontsize=9, fontweight='bold', color=COLORS['primary_dark']) - - # Header injection explanation box - draw_box(ax, 0.5, 0.5, 13, 2.1, COLORS['bg_light'], COLORS['primary'], 2) - ax.text(7, 2.3, 'HOW CAPABILITY HOST ENABLES ADDITIONAL HEADERS', ha='center', va='center', - fontsize=11, fontweight='bold', color=COLORS['primary_dark']) - - flow_text = [ - '1. Agent makes API call -> 2. Capability Host intercepts -> 3. Looks up connection config', - '4. Injects headers: Authorization (Bearer token from MSI), x-ms-documentdb-partitionkey, etc.', - '5. Routes through Private Endpoint -> 6. Resource receives authenticated request' - ] - for i, line in enumerate(flow_text): - ax.text(7, 1.85 - i*0.4, line, ha='center', va='center', - fontsize=9, color=COLORS['text_dark'], family='monospace') - - plt.tight_layout() - plt.savefig('layer5_capability_host.png', dpi=150, bbox_inches='tight', - facecolor='white', edgecolor='none') - plt.close() - print("Generated layer5_capability_host.png") - - -# ============================================================================= -# DEPLOYMENT FLOW - All Phases (Two Column Layout) -# ============================================================================= -def generate_deployment_flow(): - fig, ax = plt.subplots(1, 1, figsize=(32, 18)) - ax.set_xlim(0, 28) - ax.set_ylim(0, 13) - ax.set_aspect('equal') - ax.axis('off') - fig.patch.set_facecolor(COLORS['white']) - ax.set_facecolor(COLORS['white']) - - # Left column phases (1-4) - left_phases = [ - { - 'num': '1', - 'title': 'Network Infrastructure', - 'y': 10.0, - 'items': ['VNet + Agent Subnet + PE Subnet + MCP Subnet'] - }, - { - 'num': '2', - 'title': 'AI Services Account + Model', - 'y': 7.2, - 'items': ['AI Services (Kind: AIServices)', 'Model Deployment (GPT-4o)'] - }, - { - 'num': '3', - 'title': 'BYO Data Resources', - 'y': 4.4, - 'items': ['Cosmos DB (threads)', 'Storage (files)', 'AI Search (vector store)'] - }, - { - 'num': '4', - 'title': 'Private Network Security', - 'y': 1.6, - 'items': ['Private Endpoints for all services', 'Private DNS Zones'] - } - ] - - # Right column phases (5-8) - right_phases = [ - { - 'num': '5', - 'title': 'Project + Connections', - 'y': 10.0, - 'items': ['Foundry Project', 'CosmosDB / Storage / AI Search', 'connections (AAD Auth)'] - }, - { - 'num': '6', - 'title': 'RBAC (Pre-Capability Host)', - 'y': 7.2, - 'items': ['Storage Blob Data Contributor', 'Cosmos DB Operator', 'Search Index Data Contributor'] - }, - { - 'num': '7', - 'title': 'Capability Host', - 'y': 4.4, - 'items': ['vectorStoreConnections', 'storageConnections', 'threadStorageConnections'] - }, - { - 'num': '8', - 'title': 'RBAC (Post-Capability Host)', - 'y': 1.6, - 'items': ['Storage Blob Data Owner', 'Cosmos Built-In Data Contributor', '(on containers created by caphost)'] - } - ] - - box_width = 12.5 - box_height = 2.4 - left_x = 0.8 - right_x = 14.5 - - def draw_phase(phase, box_x, y): - # Main phase box - box = FancyBboxPatch( - (box_x, y), box_width, box_height, - boxstyle="round,pad=0.02,rounding_size=0.15", - facecolor=COLORS['bg_medium'], - edgecolor=COLORS['border'], - linewidth=4 - ) - ax.add_patch(box) - - # Phase number circle - circle = FancyBboxPatch( - (box_x + 0.3, y + box_height/2 - 0.5), 1.0, 1.0, - boxstyle="round,pad=0.02,rounding_size=0.5", - facecolor=COLORS['primary'], - edgecolor=COLORS['primary_dark'], - linewidth=3 - ) - ax.add_patch(circle) - ax.text(box_x + 0.8, y + box_height/2, phase['num'], ha='center', va='center', - fontsize=36, fontweight='bold', color=COLORS['text_light']) - - # Phase title - ax.text(box_x + 1.6, y + box_height - 0.45, f"Phase {phase['num']}", ha='left', va='center', - fontsize=26, color=COLORS['accent'], fontweight='bold') - ax.text(box_x + 4.0, y + box_height - 0.45, f"({phase['title']})", ha='left', va='center', - fontsize=26, fontweight='bold', color=COLORS['primary_dark']) - - # Items - each on its own line - for j, item in enumerate(phase['items']): - ax.text(box_x + 1.6, y + box_height - 1.0 - j*0.5, f"• {item}", ha='left', va='center', - fontsize=22, color=COLORS['text_dark']) - - # Draw left column - for i, phase in enumerate(left_phases): - draw_phase(phase, left_x, phase['y']) - # Arrow to next phase (except last in column) - if i < len(left_phases) - 1: - arrow_y = phase['y'] - 0.2 - ax.annotate('', xy=(left_x + box_width/2, arrow_y - 0.35), - xytext=(left_x + box_width/2, arrow_y + 0.05), - arrowprops=dict(arrowstyle='->', color=COLORS['primary'], lw=4)) - - # Draw right column - for i, phase in enumerate(right_phases): - draw_phase(phase, right_x, phase['y']) - # Arrow to next phase (except last in column) - if i < len(right_phases) - 1: - arrow_y = phase['y'] - 0.2 - ax.annotate('', xy=(right_x + box_width/2, arrow_y - 0.35), - xytext=(right_x + box_width/2, arrow_y + 0.05), - arrowprops=dict(arrowstyle='->', color=COLORS['primary'], lw=4)) - - plt.tight_layout() - plt.savefig('deployment_flow.png', dpi=400, bbox_inches='tight', - facecolor='white', edgecolor='none') - plt.close() - print("Generated deployment_flow.png") - - -# ============================================================================= -# Main -# ============================================================================= -if __name__ == '__main__': - print("Generating architecture diagrams...") - print("-" * 40) - generate_layer1() - generate_layer2() - generate_layer3() - generate_layer4() - generate_layer5() - generate_deployment_flow() - print("-" * 40) - print("All diagrams generated successfully!") diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer1_network_foundation.png b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer1_network_foundation.png deleted file mode 100644 index 3006a0ae3..000000000 Binary files a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer1_network_foundation.png and /dev/null differ diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer2_data_resources.png b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer2_data_resources.png deleted file mode 100644 index 2f8a80cac..000000000 Binary files a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer2_data_resources.png and /dev/null differ diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer3_ai_services.png b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer3_ai_services.png deleted file mode 100644 index 498c159b2..000000000 Binary files a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer3_ai_services.png and /dev/null differ diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer4_project_connections.png b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer4_project_connections.png deleted file mode 100644 index 82e57f9a1..000000000 Binary files a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer4_project_connections.png and /dev/null differ diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer5_capability_host.png b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer5_capability_host.png deleted file mode 100644 index b8c040870..000000000 Binary files a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/diagrams/layer5_capability_host.png and /dev/null differ diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/get-existing-resources.ps1 b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/get-existing-resources.ps1 deleted file mode 100644 index 76cca81a9..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/get-existing-resources.ps1 +++ /dev/null @@ -1,62 +0,0 @@ -# PowerShell script to help you get the names of your existing resources -# Run this after your initial deployment to get the resource names for the add-project parameters - -param( - [Parameter(Mandatory=$true)] - [string]$ResourceGroupName, - - [Parameter(Mandatory=$false)] - [string]$SubscriptionId -) - -if ($SubscriptionId) { - az account set --subscription $SubscriptionId -} - -Write-Host "Getting existing AI Foundry resources from Resource Group: $ResourceGroupName" -ForegroundColor Green - -# Get AI Services account -Write-Host "`n=== AI Services Account ===" -ForegroundColor Yellow -$aiAccount = az cognitiveservices account list --resource-group $ResourceGroupName --query "[?kind=='AIServices'].[name]" -o tsv -if ($aiAccount) { - Write-Host "AI Services Account Name: $aiAccount" -} else { - Write-Host "No AI Services account found" -ForegroundColor Red -} - -# Get Storage Account -Write-Host "`n=== Storage Account ===" -ForegroundColor Yellow -$storageAccount = az storage account list --resource-group $ResourceGroupName --query "[].name" -o tsv -if ($storageAccount) { - Write-Host "Storage Account Name: $storageAccount" -} else { - Write-Host "No Storage account found" -ForegroundColor Red -} - -# Get AI Search Service -Write-Host "`n=== AI Search Service ===" -ForegroundColor Yellow -$searchService = az search service list --resource-group $ResourceGroupName --query "[].name" -o tsv -if ($searchService) { - Write-Host "AI Search Service Name: $searchService" -} else { - Write-Host "No AI Search service found" -ForegroundColor Red -} - -# Get Cosmos DB Account -Write-Host "`n=== Cosmos DB Account ===" -ForegroundColor Yellow -$cosmosAccount = az cosmosdb list --resource-group $ResourceGroupName --query "[].name" -o tsv -if ($cosmosAccount) { - Write-Host "Cosmos DB Account Name: $cosmosAccount" -} else { - Write-Host "No Cosmos DB account found" -ForegroundColor Red -} - -Write-Host "`n=== Summary for add-project.bicepparam ===" -ForegroundColor Green -Write-Host "param existingAccountName = '$aiAccount'" -Write-Host "param existingAiSearchName = '$searchService'" -Write-Host "param existingStorageName = '$storageAccount'" -Write-Host "param existingCosmosDBName = '$cosmosAccount'" -Write-Host "param accountResourceGroupName = '$ResourceGroupName'" -Write-Host "param aiSearchResourceGroupName = '$ResourceGroupName'" -Write-Host "param storageResourceGroupName = '$ResourceGroupName'" -Write-Host "param cosmosDBResourceGroupName = '$ResourceGroupName'" diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.bicep deleted file mode 100644 index 11d4c2a06..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.bicep +++ /dev/null @@ -1,438 +0,0 @@ -/* -Hybrid Private Resources Setup for Azure AI Foundry Agents ------------------------------------------------------------ -This template creates an Azure AI Foundry account with public network access DISABLED, -while keeping backend resources (AI Search, Cosmos DB, Storage) on private endpoints. - -Key differences from template 15 (fully private): -- AI Services: publicNetworkAccess = Disabled (default) -- Backend resources: Still private (AI Search, Cosmos DB, Storage) -- Data Proxy: networkInjections configured to route to private VNet - -This enables: -✓ Agents can use AI Search tool (routed via Data Proxy to private endpoint) -✓ Agents can use MCP servers running on the VNet - -Architecture: - Private VNet → AI Services (private) → Data Proxy → Private VNet → Backend Resources -*/ -@description('Location for all resources.') -@allowed([ - 'westus' - 'westus2' - 'eastus' - 'eastus2' - 'japaneast' - 'francecentral' - 'spaincentral' - 'uaenorth' - 'southcentralus' - 'italynorth' - 'germanywestcentral' - 'brazilsouth' - 'southafricanorth' - 'australiaeast' - 'swedencentral' - 'canadaeast' - 'westeurope' - 'westus3' - 'uksouth' - 'southindia' - - //only class B and C - 'koreacentral' - 'polandcentral' - 'switzerlandnorth' - 'norwayeast' -]) -param location string = 'eastus2' - -@description('Name for your AI Services resource.') -param aiServices string = 'aiservices' - -// Model deployment parameters -@description('The name of the model you want to deploy') -param modelName string = 'gpt-4o-mini' -@description('The provider of your model') -param modelFormat string = 'OpenAI' -@description('The version of your model') -param modelVersion string = '2024-07-18' -@description('The sku of your model deployment') -param modelSkuName string = 'GlobalStandard' -@description('The tokens per minute (TPM) of your model deployment') -param modelCapacity int = 30 - -// Create a short, unique suffix, that will be unique to each resource group -param deploymentTimestamp string = utcNow('yyyyMMddHHmmss') -var uniqueSuffix = substring(uniqueString('${resourceGroup().id}-${deploymentTimestamp}'), 0, 4) -var accountName = toLower('${aiServices}${uniqueSuffix}') - -@description('Name for your project resource.') -param firstProjectName string = 'project' - -@description('This project will be a sub-resource of your account') -param projectDescription string = 'A project for the AI Foundry account with network secured deployed Agent' - -@description('The display name of the project') -param displayName string = 'network secured agent project' - -// Existing Virtual Network parameters -@description('Virtual Network name for the Agent to create new or existing virtual network') -param vnetName string = 'agent-vnet-test' - -@description('The name of Agents Subnet to create new or existing subnet for agents') -param agentSubnetName string = 'agent-subnet' - -@description('The name of Private Endpoint subnet to create new or existing subnet for private endpoints') -param peSubnetName string = 'pe-subnet' - -@description('The name of MCP subnet for user-deployed Container Apps (e.g., MCP servers)') -param mcpSubnetName string = 'mcp-subnet' - -//Existing standard Agent required resources -@description('Existing Virtual Network name Resource ID') -param existingVnetResourceId string = '' - -@description('Address space for the VNet (only used for new VNet)') -param vnetAddressPrefix string = '' - -@description('Address prefix for the agent subnet. The default value is 192.168.0.0/24 but you can choose any size /26 or any class like 10.0.0.0 or 172.168.0.0') -param agentSubnetPrefix string = '' - -@description('Address prefix for the private endpoint subnet') -param peSubnetPrefix string = '' - -@description('Address prefix for the MCP subnet. The default value is 192.168.2.0/24.') -param mcpSubnetPrefix string = '' - -@description('The AI Search Service full ARM Resource ID. This is an optional field, and if not provided, the resource will be created.') -param aiSearchResourceId string = '' -@description('The AI Storage Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created.') -param azureStorageAccountResourceId string = '' -@description('The Cosmos DB Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created.') -param azureCosmosDBAccountResourceId string = '' - -@description('The Microsoft Fabric Workspace full ARM Resource ID. This is an optional field for Fabric private link connectivity.') -param fabricWorkspaceResourceId string = '' - -//New Param for resource group of Private DNS zones -//@description('Optional: Resource group containing existing private DNS zones. If specified, DNS zones will not be created.') -//param existingDnsZonesResourceGroup string = '' - -@description('Object mapping DNS zone names to their resource group, or empty string to indicate creation') -param existingDnsZones object = { - 'privatelink.services.ai.azure.com': '' - 'privatelink.openai.azure.com': '' - 'privatelink.cognitiveservices.azure.com': '' - 'privatelink.search.windows.net': '' - 'privatelink.blob.core.windows.net': '' - 'privatelink.documents.azure.com': '' - 'privatelink.analysis.windows.net': '' -} - -@description('Zone Names for Validation of existing Private Dns Zones') -param dnsZoneNames array = [ - 'privatelink.services.ai.azure.com' - 'privatelink.openai.azure.com' - 'privatelink.cognitiveservices.azure.com' - 'privatelink.search.windows.net' - 'privatelink.blob.core.windows.net' - 'privatelink.documents.azure.com' - 'privatelink.analysis.windows.net' -] - -var projectName = toLower('${firstProjectName}${uniqueSuffix}') -var cosmosDBName = toLower('${aiServices}${uniqueSuffix}cosmosdb') -var aiSearchName = toLower('${aiServices}${uniqueSuffix}search') -var azureStorageName = toLower('${aiServices}${uniqueSuffix}storage') - -// Check if existing resources have been passed in -var storagePassedIn = azureStorageAccountResourceId != '' -var searchPassedIn = aiSearchResourceId != '' -var cosmosPassedIn = azureCosmosDBAccountResourceId != '' -var existingVnetPassedIn = existingVnetResourceId != '' - -var acsParts = split(aiSearchResourceId, '/') -var aiSearchServiceSubscriptionId = searchPassedIn ? acsParts[2] : subscription().subscriptionId -var aiSearchServiceResourceGroupName = searchPassedIn ? acsParts[4] : resourceGroup().name - -var cosmosParts = split(azureCosmosDBAccountResourceId, '/') -var cosmosDBSubscriptionId = cosmosPassedIn ? cosmosParts[2] : subscription().subscriptionId -var cosmosDBResourceGroupName = cosmosPassedIn ? cosmosParts[4] : resourceGroup().name - -var storageParts = split(azureStorageAccountResourceId, '/') -var azureStorageSubscriptionId = storagePassedIn ? storageParts[2] : subscription().subscriptionId -var azureStorageResourceGroupName = storagePassedIn ? storageParts[4] : resourceGroup().name - -var vnetParts = split(existingVnetResourceId, '/') -var vnetSubscriptionId = existingVnetPassedIn ? vnetParts[2] : subscription().subscriptionId -var vnetResourceGroupName = existingVnetPassedIn ? vnetParts[4] : resourceGroup().name -var existingVnetName = existingVnetPassedIn ? last(vnetParts) : vnetName -var trimVnetName = trim(existingVnetName) - -@description('The name of the project capability host to be created') -param projectCapHost string = 'caphostproj' - -// Create Virtual Network and Subnets -module vnet 'modules-network-secured/network-agent-vnet.bicep' = { - name: 'vnet-${trimVnetName}-${uniqueSuffix}-deployment' - params: { - location: location - vnetName: trimVnetName - useExistingVnet: existingVnetPassedIn - existingVnetResourceGroupName: vnetResourceGroupName - agentSubnetName: agentSubnetName - peSubnetName: peSubnetName - mcpSubnetName: mcpSubnetName - vnetAddressPrefix: vnetAddressPrefix - agentSubnetPrefix: agentSubnetPrefix - peSubnetPrefix: peSubnetPrefix - mcpSubnetPrefix: mcpSubnetPrefix - existingVnetSubscriptionId: vnetSubscriptionId - } -} - -/* - Create the AI Services account and gpt-4o model deployment -*/ -module aiAccount 'modules-network-secured/ai-account-identity.bicep' = { - name: '${accountName}-${uniqueSuffix}-deployment' - params: { - // workspace organization - accountName: accountName - location: location - modelName: modelName - modelFormat: modelFormat - modelVersion: modelVersion - modelSkuName: modelSkuName - modelCapacity: modelCapacity - agentSubnetId: vnet.outputs.agentSubnetId - } -} -/* - Validate existing resources - This module will check if the AI Search Service, Storage Account, and Cosmos DB Account already exist. - If they do, it will set the corresponding output to true. If they do not exist, it will set the output to false. -*/ -module validateExistingResources 'modules-network-secured/validate-existing-resources.bicep' = { - name: 'validate-existing-resources-${uniqueSuffix}-deployment' - params: { - aiSearchResourceId: aiSearchResourceId - azureStorageAccountResourceId: azureStorageAccountResourceId - azureCosmosDBAccountResourceId: azureCosmosDBAccountResourceId - existingDnsZones: existingDnsZones - dnsZoneNames: dnsZoneNames - } -} - -// This module will create new agent dependent resources -// A Cosmos DB account, an AI Search Service, and a Storage Account are created if they do not already exist -module aiDependencies 'modules-network-secured/standard-dependent-resources.bicep' = { - name: 'dependencies-${uniqueSuffix}-deployment' - params: { - location: location - azureStorageName: azureStorageName - aiSearchName: aiSearchName - cosmosDBName: cosmosDBName - - // AI Search Service parameters - aiSearchResourceId: aiSearchResourceId - aiSearchExists: validateExistingResources.outputs.aiSearchExists - - // Storage Account - azureStorageAccountResourceId: azureStorageAccountResourceId - azureStorageExists: validateExistingResources.outputs.azureStorageExists - - // Cosmos DB Account - cosmosDBResourceId: azureCosmosDBAccountResourceId - cosmosDBExists: validateExistingResources.outputs.cosmosDBExists - } -} - -resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' existing = { - name: aiDependencies.outputs.azureStorageName - scope: resourceGroup(azureStorageSubscriptionId, azureStorageResourceGroupName) -} - -resource aiSearch 'Microsoft.Search/searchServices@2023-11-01' existing = { - name: aiDependencies.outputs.aiSearchName - scope: resourceGroup( - aiDependencies.outputs.aiSearchServiceSubscriptionId, - aiDependencies.outputs.aiSearchServiceResourceGroupName - ) -} - -resource cosmosDB 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' existing = { - name: aiDependencies.outputs.cosmosDBName - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) -} - -// Private Endpoint and DNS Configuration -// This module sets up private network access for all Azure services: -// 1. Creates private endpoints in the specified subnet -// 2. Sets up private DNS zones for each service -// 3. Links private DNS zones to the VNet for name resolution -// 4. Configures network policies to restrict access to private endpoints only -module privateEndpointAndDNS 'modules-network-secured/private-endpoint-and-dns.bicep' = { - name: '${uniqueSuffix}-private-endpoint' - params: { - aiAccountName: aiAccount.outputs.accountName // AI Services to secure - aiSearchName: aiDependencies.outputs.aiSearchName // AI Search to secure - storageName: aiDependencies.outputs.azureStorageName // Storage to secure - cosmosDBName: aiDependencies.outputs.cosmosDBName - fabricWorkspaceResourceId: fabricWorkspaceResourceId // Microsoft Fabric workspace (optional) - vnetName: vnet.outputs.virtualNetworkName // VNet containing subnets - peSubnetName: vnet.outputs.peSubnetName // Subnet for private endpoints - suffix: uniqueSuffix // Unique identifier - vnetResourceGroupName: vnet.outputs.virtualNetworkResourceGroup - vnetSubscriptionId: vnet.outputs.virtualNetworkSubscriptionId // Subscription ID for the VNet - cosmosDBSubscriptionId: cosmosDBSubscriptionId // Subscription ID for Cosmos DB - cosmosDBResourceGroupName: cosmosDBResourceGroupName // Resource Group for Cosmos DB - aiSearchSubscriptionId: aiSearchServiceSubscriptionId // Subscription ID for AI Search Service - aiSearchResourceGroupName: aiSearchServiceResourceGroupName // Resource Group for AI Search Service - storageAccountResourceGroupName: azureStorageResourceGroupName // Resource Group for Storage Account - storageAccountSubscriptionId: azureStorageSubscriptionId // Subscription ID for Storage Account - existingDnsZones: existingDnsZones - } - dependsOn: [ - aiSearch // Ensure AI Search exists - storage // Ensure Storage exists - cosmosDB // Ensure Cosmos DB exists - ] -} - -/* - Creates a new project (sub-resource of the AI Services account) -*/ -module aiProject 'modules-network-secured/ai-project-identity.bicep' = { - name: '${projectName}-${uniqueSuffix}-deployment' - params: { - // workspace organization - projectName: projectName - projectDescription: projectDescription - displayName: displayName - location: location - - aiSearchName: aiDependencies.outputs.aiSearchName - aiSearchServiceResourceGroupName: aiDependencies.outputs.aiSearchServiceResourceGroupName - aiSearchServiceSubscriptionId: aiDependencies.outputs.aiSearchServiceSubscriptionId - - cosmosDBName: aiDependencies.outputs.cosmosDBName - cosmosDBSubscriptionId: aiDependencies.outputs.cosmosDBSubscriptionId - cosmosDBResourceGroupName: aiDependencies.outputs.cosmosDBResourceGroupName - - azureStorageName: aiDependencies.outputs.azureStorageName - azureStorageSubscriptionId: aiDependencies.outputs.azureStorageSubscriptionId - azureStorageResourceGroupName: aiDependencies.outputs.azureStorageResourceGroupName - // dependent resources - accountName: aiAccount.outputs.accountName - } - dependsOn: [ - privateEndpointAndDNS - cosmosDB - aiSearch - storage - ] -} - -module formatProjectWorkspaceId 'modules-network-secured/format-project-workspace-id.bicep' = { - name: 'format-project-workspace-id-${uniqueSuffix}-deployment' - params: { - projectWorkspaceId: aiProject.outputs.projectWorkspaceId - } -} - -/* - Assigns the project SMI the storage blob data contributor role on the storage account -*/ -module storageAccountRoleAssignment 'modules-network-secured/azure-storage-account-role-assignment.bicep' = { - name: 'storage-${azureStorageName}-${uniqueSuffix}-deployment' - scope: resourceGroup(azureStorageSubscriptionId, azureStorageResourceGroupName) - params: { - azureStorageName: aiDependencies.outputs.azureStorageName - projectPrincipalId: aiProject.outputs.projectPrincipalId - } - dependsOn: [ - storage - privateEndpointAndDNS - ] -} - -// The Comos DB Operator role must be assigned before the caphost is created -module cosmosAccountRoleAssignments 'modules-network-secured/cosmosdb-account-role-assignment.bicep' = { - name: 'cosmos-account-ra-${uniqueSuffix}-deployment' - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) - params: { - cosmosDBName: aiDependencies.outputs.cosmosDBName - projectPrincipalId: aiProject.outputs.projectPrincipalId - } - dependsOn: [ - cosmosDB - privateEndpointAndDNS - ] -} - -// This role can be assigned before or after the caphost is created -module aiSearchRoleAssignments 'modules-network-secured/ai-search-role-assignments.bicep' = { - name: 'ai-search-ra-${uniqueSuffix}-deployment' - scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) - params: { - aiSearchName: aiDependencies.outputs.aiSearchName - projectPrincipalId: aiProject.outputs.projectPrincipalId - } - dependsOn: [ - aiSearch - privateEndpointAndDNS - ] -} - -// This module creates the capability host for the project and account -module addProjectCapabilityHost 'modules-network-secured/add-project-capability-host.bicep' = { - name: 'capabilityHost-configuration-${uniqueSuffix}-deployment' - params: { - accountName: aiAccount.outputs.accountName - projectName: aiProject.outputs.projectName - cosmosDBConnection: aiProject.outputs.cosmosDBConnection - azureStorageConnection: aiProject.outputs.azureStorageConnection - aiSearchConnection: aiProject.outputs.aiSearchConnection - projectCapHost: projectCapHost - } - dependsOn: [ - aiSearch // Ensure AI Search exists - storage // Ensure Storage exists - cosmosDB - privateEndpointAndDNS - cosmosAccountRoleAssignments - storageAccountRoleAssignment - aiSearchRoleAssignments - ] -} - -// The Storage Blob Data Owner role must be assigned after the caphost is created -module storageContainersRoleAssignment 'modules-network-secured/blob-storage-container-role-assignments.bicep' = { - name: 'storage-containers-ra-${uniqueSuffix}-deployment' - scope: resourceGroup(azureStorageSubscriptionId, azureStorageResourceGroupName) - params: { - aiProjectPrincipalId: aiProject.outputs.projectPrincipalId - storageName: aiDependencies.outputs.azureStorageName - workspaceId: formatProjectWorkspaceId.outputs.projectWorkspaceIdGuid - } - dependsOn: [ - addProjectCapabilityHost - ] -} - -// The Cosmos Built-In Data Contributor role must be assigned after the caphost is created -module cosmosContainerRoleAssignments 'modules-network-secured/cosmos-container-role-assignments.bicep' = { - name: 'cosmos-containers-ra-${uniqueSuffix}-deployment' - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) - params: { - cosmosAccountName: aiDependencies.outputs.cosmosDBName - projectWorkspaceId: formatProjectWorkspaceId.outputs.projectWorkspaceIdGuid - projectPrincipalId: aiProject.outputs.projectPrincipalId - } - dependsOn: [ - addProjectCapabilityHost - storageContainersRoleAssignment - ] -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.bicepparam b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.bicepparam deleted file mode 100644 index a5125398b..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.bicepparam +++ /dev/null @@ -1,66 +0,0 @@ -using './main.bicep' - -param location = 'norwayeast' -param aiServices = 'djetchev' -param modelName = 'gpt-4o-mini' -param modelFormat = 'OpenAI' -param modelVersion = '2024-07-18' -param modelSkuName = 'GlobalStandard' -param modelCapacity = 1 -param firstProjectName = 'project' -param projectDescription = 'A project for the AI Foundry account with network secured deployed Agent' -param displayName = 'project' -param peSubnetName = 'pe-subnet' - -// Resource IDs for existing resources -// If you provide these, the deployment will use the existing resources instead of creating new ones -param existingVnetResourceId = '' -param vnetName = 'agent-vnet-test' -param agentSubnetName = 'agent-subnet' -param aiSearchResourceId = '' -param azureStorageAccountResourceId = '' -param azureCosmosDBAccountResourceId = '' -// Pass the DNS zone map here -// Leave empty to create new DNS zone, add the resource group of existing DNS zone to use it -param existingDnsZones = { - 'privatelink.services.ai.azure.com': '' - 'privatelink.openai.azure.com': '' - 'privatelink.cognitiveservices.azure.com': '' - 'privatelink.search.windows.net': '' - 'privatelink.blob.core.windows.net': '' - 'privatelink.documents.azure.com': '' -} - -//DNSZones names for validating if they exist -param dnsZoneNames = [ - 'privatelink.services.ai.azure.com' - 'privatelink.openai.azure.com' - 'privatelink.cognitiveservices.azure.com' - 'privatelink.search.windows.net' - 'privatelink.blob.core.windows.net' - 'privatelink.documents.azure.com' -] - -// Network configuration (behavior depends on `existingVnetResourceId`) -// -// - NEW VNet (existingVnetResourceId is empty): -// The values below are used to CREATE the VNet and the two subnets. -// Provide explicit, non-overlapping CIDR ranges when creating a new VNet. -// -// - EXISTING VNet (existingVnetResourceId is provided): -// The module will reference the existing VNet. Subnet handling depends on the -// values you provide: -// * If `agentSubnetPrefix` or `peSubnetPrefix` are empty, the module may -// auto-derive subnet CIDRs from the existing VNet's address space -// (using cidrSubnet). This can produce /24 (or configured) subnets -// starting at index 0, 1, etc. -// * If you provide explicit subnet prefixes, the module will attempt to -// create or update subnets with those prefixes in the existing VNet. -// -// Important operational notes and risks (when existingVnetResourceId is provided): -// - Avoid CIDR overlaps with any existing subnets in the target VNet. Overlap -// leads to `NetcfgSubnetRangesOverlap` and failed deployments. -// - For highest safety when using an existing VNet, supply the existing `agentSubnetPrefix` and `peSubnetPrefix`. -param vnetAddressPrefix = '' -param agentSubnetPrefix = '' -param peSubnetPrefix = '' diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.json b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.json deleted file mode 100644 index d7dd22475..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/main.json +++ /dev/null @@ -1,2772 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "789904159633670276" - } - }, - "parameters": { - "location": { - "type": "string", - "defaultValue": "eastus2", - "allowedValues": [ - "westus", - "eastus", - "eastus2", - "japaneast", - "francecentral", - "spaincentral", - "uaenorth", - "southcentralus", - "italynorth", - "germanywestcentral", - "brazilsouth", - "southafricanorth", - "australiaeast", - "swedencentral", - "canadaeast", - "westeurope", - "westus3", - "uksouth", - "southindia", - "koreacentral", - "polandcentral", - "switzerlandnorth", - "norwayeast" - ], - "metadata": { - "description": "Location for all resources." - } - }, - "aiServices": { - "type": "string", - "defaultValue": "aiservices", - "metadata": { - "description": "Name for your AI Services resource." - } - }, - "modelName": { - "type": "string", - "defaultValue": "gpt-4o", - "metadata": { - "description": "The name of the model you want to deploy" - } - }, - "modelFormat": { - "type": "string", - "defaultValue": "OpenAI", - "metadata": { - "description": "The provider of your model" - } - }, - "modelVersion": { - "type": "string", - "defaultValue": "2024-11-20", - "metadata": { - "description": "The version of your model" - } - }, - "modelSkuName": { - "type": "string", - "defaultValue": "GlobalStandard", - "metadata": { - "description": "The sku of your model deployment" - } - }, - "modelCapacity": { - "type": "int", - "defaultValue": 30, - "metadata": { - "description": "The tokens per minute (TPM) of your model deployment" - } - }, - "deploymentTimestamp": { - "type": "string", - "defaultValue": "[utcNow('yyyyMMddHHmmss')]" - }, - "firstProjectName": { - "type": "string", - "defaultValue": "project", - "metadata": { - "description": "Name for your project resource." - } - }, - "projectDescription": { - "type": "string", - "defaultValue": "A project for the AI Foundry account with network secured deployed Agent", - "metadata": { - "description": "This project will be a sub-resource of your account" - } - }, - "displayName": { - "type": "string", - "defaultValue": "network secured agent project", - "metadata": { - "description": "The display name of the project" - } - }, - "vnetName": { - "type": "string", - "defaultValue": "agent-vnet-test", - "metadata": { - "description": "Virtual Network name for the Agent to create new or existing virtual network" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet to create new or existing subnet for agents" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Private Endpoint subnet to create new or existing subnet for private endpoints" - } - }, - "existingVnetResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Existing Virtual Network name Resource ID" - } - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address space for the VNet (only used for new VNet)" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet. The default value is 192.168.0.0/24 but you can choose any size /26 or any class like 10.0.0.0 or 172.168.0.0" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet" - } - }, - "aiSearchResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The AI Search Service full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "azureStorageAccountResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The AI Storage Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "azureCosmosDBAccountResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The Cosmos DB Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "fabricWorkspaceResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The Microsoft Fabric Workspace full ARM Resource ID. This is an optional field for Fabric private link connectivity." - } - }, - "existingDnsZones": { - "type": "object", - "defaultValue": { - "privatelink.services.ai.azure.com": "", - "privatelink.openai.azure.com": "", - "privatelink.cognitiveservices.azure.com": "", - "privatelink.search.windows.net": "", - "privatelink.blob.core.windows.net": "", - "privatelink.documents.azure.com": "", - "privatelink.analysis.windows.net": "" - }, - "metadata": { - "description": "Object mapping DNS zone names to their resource group, or empty string to indicate creation" - } - }, - "dnsZoneNames": { - "type": "array", - "defaultValue": [ - "privatelink.services.ai.azure.com", - "privatelink.openai.azure.com", - "privatelink.cognitiveservices.azure.com", - "privatelink.search.windows.net", - "privatelink.blob.core.windows.net", - "privatelink.documents.azure.com", - "privatelink.analysis.windows.net" - ], - "metadata": { - "description": "Zone Names for Validation of existing Private Dns Zones" - } - }, - "projectCapHost": { - "type": "string", - "defaultValue": "caphostproj", - "metadata": { - "description": "The name of the project capability host to be created" - } - } - }, - "variables": { - "uniqueSuffix": "[substring(uniqueString(format('{0}-{1}', resourceGroup().id, parameters('deploymentTimestamp'))), 0, 4)]", - "accountName": "[toLower(format('{0}{1}', parameters('aiServices'), variables('uniqueSuffix')))]", - "projectName": "[toLower(format('{0}{1}', parameters('firstProjectName'), variables('uniqueSuffix')))]", - "cosmosDBName": "[toLower(format('{0}{1}cosmosdb', parameters('aiServices'), variables('uniqueSuffix')))]", - "aiSearchName": "[toLower(format('{0}{1}search', parameters('aiServices'), variables('uniqueSuffix')))]", - "azureStorageName": "[toLower(format('{0}{1}storage', parameters('aiServices'), variables('uniqueSuffix')))]", - "storagePassedIn": "[not(equals(parameters('azureStorageAccountResourceId'), ''))]", - "searchPassedIn": "[not(equals(parameters('aiSearchResourceId'), ''))]", - "cosmosPassedIn": "[not(equals(parameters('azureCosmosDBAccountResourceId'), ''))]", - "existingVnetPassedIn": "[not(equals(parameters('existingVnetResourceId'), ''))]", - "acsParts": "[split(parameters('aiSearchResourceId'), '/')]", - "aiSearchServiceSubscriptionId": "[if(variables('searchPassedIn'), variables('acsParts')[2], subscription().subscriptionId)]", - "aiSearchServiceResourceGroupName": "[if(variables('searchPassedIn'), variables('acsParts')[4], resourceGroup().name)]", - "cosmosParts": "[split(parameters('azureCosmosDBAccountResourceId'), '/')]", - "cosmosDBSubscriptionId": "[if(variables('cosmosPassedIn'), variables('cosmosParts')[2], subscription().subscriptionId)]", - "cosmosDBResourceGroupName": "[if(variables('cosmosPassedIn'), variables('cosmosParts')[4], resourceGroup().name)]", - "storageParts": "[split(parameters('azureStorageAccountResourceId'), '/')]", - "azureStorageSubscriptionId": "[if(variables('storagePassedIn'), variables('storageParts')[2], subscription().subscriptionId)]", - "azureStorageResourceGroupName": "[if(variables('storagePassedIn'), variables('storageParts')[4], resourceGroup().name)]", - "vnetParts": "[split(parameters('existingVnetResourceId'), '/')]", - "vnetSubscriptionId": "[if(variables('existingVnetPassedIn'), variables('vnetParts')[2], subscription().subscriptionId)]", - "vnetResourceGroupName": "[if(variables('existingVnetPassedIn'), variables('vnetParts')[4], resourceGroup().name)]", - "existingVnetName": "[if(variables('existingVnetPassedIn'), last(variables('vnetParts')), parameters('vnetName'))]", - "trimVnetName": "[trim(variables('existingVnetName'))]" - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "vnetName": { - "value": "[variables('trimVnetName')]" - }, - "useExistingVnet": { - "value": "[variables('existingVnetPassedIn')]" - }, - "existingVnetResourceGroupName": { - "value": "[variables('vnetResourceGroupName')]" - }, - "agentSubnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "value": "[parameters('peSubnetName')]" - }, - "vnetAddressPrefix": { - "value": "[parameters('vnetAddressPrefix')]" - }, - "agentSubnetPrefix": { - "value": "[parameters('agentSubnetPrefix')]" - }, - "peSubnetPrefix": { - "value": "[parameters('peSubnetPrefix')]" - }, - "existingVnetSubscriptionId": { - "value": "[variables('vnetSubscriptionId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "8505298823279202405" - } - }, - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Azure region for the deployment" - } - }, - "vnetName": { - "type": "string", - "metadata": { - "description": "The name of the virtual network" - } - }, - "useExistingVnet": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Indicates if an existing VNet should be used" - } - }, - "existingVnetSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID of the existing VNet (if different from current subscription)" - } - }, - "existingVnetResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name of the existing VNet (if different from current resource group)" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Private Endpoint subnet" - } - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address space for the VNet (only used for new VNet)" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet" - } - } - }, - "resources": [ - { - "condition": "[not(parameters('useExistingVnet'))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "vnet-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "agentSubnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "value": "[parameters('peSubnetName')]" - }, - "vnetAddressPrefix": { - "value": "[parameters('vnetAddressPrefix')]" - }, - "agentSubnetPrefix": { - "value": "[parameters('agentSubnetPrefix')]" - }, - "peSubnetPrefix": { - "value": "[parameters('peSubnetPrefix')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "4954184648131521061" - } - }, - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Azure region for the deployment" - } - }, - "vnetName": { - "type": "string", - "defaultValue": "agents-vnet-test", - "metadata": { - "description": "The name of the virtual network" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Hub subnet" - } - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address space for the VNet" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet" - } - } - }, - "variables": { - "defaultVnetAddressPrefix": "192.168.0.0/16", - "vnetAddress": "[if(empty(parameters('vnetAddressPrefix')), variables('defaultVnetAddressPrefix'), parameters('vnetAddressPrefix'))]", - "agentSubnet": "[if(empty(parameters('agentSubnetPrefix')), cidrSubnet(variables('vnetAddress'), 24, 0), parameters('agentSubnetPrefix'))]", - "peSubnet": "[if(empty(parameters('peSubnetPrefix')), cidrSubnet(variables('vnetAddress'), 24, 1), parameters('peSubnetPrefix'))]" - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2024-05-01", - "name": "[parameters('vnetName')]", - "location": "[parameters('location')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[variables('vnetAddress')]" - ] - }, - "subnets": [ - { - "name": "[parameters('agentSubnetName')]", - "properties": { - "addressPrefix": "[variables('agentSubnet')]", - "delegations": [ - { - "name": "Microsoft.app/environments", - "properties": { - "serviceName": "Microsoft.App/environments" - } - } - ] - } - }, - { - "name": "[parameters('peSubnetName')]", - "properties": { - "addressPrefix": "[variables('peSubnet')]" - } - } - ] - } - } - ], - "outputs": { - "peSubnetName": { - "type": "string", - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetName": { - "type": "string", - "value": "[parameters('agentSubnetName')]" - }, - "agentSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('agentSubnetName'))]" - }, - "peSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('peSubnetName'))]" - }, - "virtualNetworkName": { - "type": "string", - "value": "[parameters('vnetName')]" - }, - "virtualNetworkId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "virtualNetworkResourceGroup": { - "type": "string", - "value": "[resourceGroup().name]" - }, - "virtualNetworkSubscriptionId": { - "type": "string", - "value": "[subscription().subscriptionId]" - } - } - } - } - }, - { - "condition": "[parameters('useExistingVnet')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "existing-vnet-deployment", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "vnetResourceGroupName": { - "value": "[parameters('existingVnetResourceGroupName')]" - }, - "vnetSubscriptionId": { - "value": "[parameters('existingVnetSubscriptionId')]" - }, - "agentSubnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetPrefix": { - "value": "[parameters('agentSubnetPrefix')]" - }, - "peSubnetPrefix": { - "value": "[parameters('peSubnetPrefix')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "3152324712046183852" - } - }, - "parameters": { - "vnetName": { - "type": "string", - "metadata": { - "description": "The name of the existing virtual network" - } - }, - "vnetSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID of virtual network (if different from current subscription)" - } - }, - "vnetResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name of the existing VNet (if different from current resource group)" - } - }, - "agentSubnetName": { - "type": "string", - "defaultValue": "agent-subnet", - "metadata": { - "description": "The name of Agents Subnet" - } - }, - "peSubnetName": { - "type": "string", - "defaultValue": "pe-subnet", - "metadata": { - "description": "The name of Private Endpoint subnet" - } - }, - "agentSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the agent subnet (only needed if creating new subnet)" - } - }, - "peSubnetPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Address prefix for the private endpoint subnet (only needed if creating new subnet)" - } - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('agent-subnet-{0}', uniqueString(deployment().name, parameters('agentSubnetName')))]", - "resourceGroup": "[parameters('vnetResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "subnetName": { - "value": "[parameters('agentSubnetName')]" - }, - "addressPrefix": "[if(empty(parameters('agentSubnetPrefix')), createObject('value', cidrSubnet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), '2024-05-01').addressSpace.addressPrefixes[0], 24, 0)), createObject('value', parameters('agentSubnetPrefix')))]", - "delegations": { - "value": [ - { - "name": "Microsoft.App/environments", - "properties": { - "serviceName": "Microsoft.App/environments" - } - } - ] - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17043822047386586435" - } - }, - "parameters": { - "vnetName": { - "type": "string", - "metadata": { - "description": "Name of the virtual network" - } - }, - "subnetName": { - "type": "string", - "metadata": { - "description": "Name of the subnet" - } - }, - "addressPrefix": { - "type": "string", - "metadata": { - "description": "Address prefix for the subnet" - } - }, - "delegations": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Array of subnet delegations" - } - } - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('vnetName'), parameters('subnetName'))]", - "properties": { - "addressPrefix": "[parameters('addressPrefix')]", - "delegations": "[parameters('delegations')]" - } - } - ], - "outputs": { - "subnetId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[0], split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[1])]" - }, - "subnetName": { - "type": "string", - "value": "[parameters('subnetName')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('pe-subnet-{0}', uniqueString(deployment().name, parameters('peSubnetName')))]", - "resourceGroup": "[parameters('vnetResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "vnetName": { - "value": "[parameters('vnetName')]" - }, - "subnetName": { - "value": "[parameters('peSubnetName')]" - }, - "addressPrefix": "[if(empty(parameters('peSubnetPrefix')), createObject('value', cidrSubnet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), '2024-05-01').addressSpace.addressPrefixes[0], 24, 1)), createObject('value', parameters('peSubnetPrefix')))]", - "delegations": { - "value": [] - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17043822047386586435" - } - }, - "parameters": { - "vnetName": { - "type": "string", - "metadata": { - "description": "Name of the virtual network" - } - }, - "subnetName": { - "type": "string", - "metadata": { - "description": "Name of the subnet" - } - }, - "addressPrefix": { - "type": "string", - "metadata": { - "description": "Address prefix for the subnet" - } - }, - "delegations": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "Array of subnet delegations" - } - } - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', parameters('vnetName'), parameters('subnetName'))]", - "properties": { - "addressPrefix": "[parameters('addressPrefix')]", - "delegations": "[parameters('delegations')]" - } - } - ], - "outputs": { - "subnetId": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[0], split(format('{0}/{1}', parameters('vnetName'), parameters('subnetName')), '/')[1])]" - }, - "subnetName": { - "type": "string", - "value": "[parameters('subnetName')]" - } - } - } - } - } - ], - "outputs": { - "peSubnetName": { - "type": "string", - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetName": { - "type": "string", - "value": "[parameters('agentSubnetName')]" - }, - "agentSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('agentSubnetName'))]" - }, - "peSubnetId": { - "type": "string", - "value": "[format('{0}/subnets/{1}', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName')), parameters('peSubnetName'))]" - }, - "virtualNetworkName": { - "type": "string", - "value": "[parameters('vnetName')]" - }, - "virtualNetworkId": { - "type": "string", - "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "virtualNetworkResourceGroup": { - "type": "string", - "value": "[parameters('vnetResourceGroupName')]" - }, - "virtualNetworkSubscriptionId": { - "type": "string", - "value": "[parameters('vnetSubscriptionId')]" - } - } - } - } - } - ], - "outputs": { - "virtualNetworkName": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkName.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkName.value)]" - }, - "virtualNetworkId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkId.value)]" - }, - "virtualNetworkSubscriptionId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkSubscriptionId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkSubscriptionId.value)]" - }, - "virtualNetworkResourceGroup": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.virtualNetworkResourceGroup.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.virtualNetworkResourceGroup.value)]" - }, - "agentSubnetName": { - "type": "string", - "value": "[parameters('agentSubnetName')]" - }, - "peSubnetName": { - "type": "string", - "value": "[parameters('peSubnetName')]" - }, - "agentSubnetId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.agentSubnetId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.agentSubnetId.value)]" - }, - "peSubnetId": { - "type": "string", - "value": "[if(parameters('useExistingVnet'), reference(resourceId('Microsoft.Resources/deployments', 'existing-vnet-deployment'), '2025-04-01').outputs.peSubnetId.value, reference(resourceId('Microsoft.Resources/deployments', 'vnet-deployment'), '2025-04-01').outputs.peSubnetId.value)]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": { - "value": "[variables('accountName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "modelName": { - "value": "[parameters('modelName')]" - }, - "modelFormat": { - "value": "[parameters('modelFormat')]" - }, - "modelVersion": { - "value": "[parameters('modelVersion')]" - }, - "modelSkuName": { - "value": "[parameters('modelSkuName')]" - }, - "modelCapacity": { - "value": "[parameters('modelCapacity')]" - }, - "agentSubnetId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.agentSubnetId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "854097619778148359" - } - }, - "parameters": { - "accountName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "modelName": { - "type": "string" - }, - "modelFormat": { - "type": "string" - }, - "modelVersion": { - "type": "string" - }, - "modelSkuName": { - "type": "string" - }, - "modelCapacity": { - "type": "int" - }, - "agentSubnetId": { - "type": "string" - }, - "networkInjection": { - "type": "string", - "defaultValue": "true" - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('accountName')]", - "location": "[parameters('location')]", - "sku": { - "name": "S0" - }, - "kind": "AIServices", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "allowProjectManagement": true, - "customSubDomainName": "[parameters('accountName')]", - "networkAcls": { - "defaultAction": "Deny", - "virtualNetworkRules": [], - "ipRules": [], - "bypass": "AzureServices" - }, - "publicNetworkAccess": "Disabled", - "networkInjections": "[if(equals(parameters('networkInjection'), 'true'), createArray(createObject('scenario', 'agent', 'subnetArmId', parameters('agentSubnetId'), 'useMicrosoftManagedNetwork', false())), null())]", - "disableLocalAuth": false - } - }, - { - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('modelName'))]", - "sku": { - "capacity": "[parameters('modelCapacity')]", - "name": "[parameters('modelSkuName')]" - }, - "properties": { - "model": { - "name": "[parameters('modelName')]", - "format": "[parameters('modelFormat')]", - "version": "[parameters('modelVersion')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName'))]" - ] - } - ], - "outputs": { - "accountName": { - "type": "string", - "value": "[parameters('accountName')]" - }, - "accountID": { - "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName'))]" - }, - "accountTarget": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName')), '2025-04-01-preview').endpoint]" - }, - "accountPrincipalId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('accountName')), '2025-04-01-preview', 'full').identity.principalId]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiSearchResourceId": { - "value": "[parameters('aiSearchResourceId')]" - }, - "azureStorageAccountResourceId": { - "value": "[parameters('azureStorageAccountResourceId')]" - }, - "azureCosmosDBAccountResourceId": { - "value": "[parameters('azureCosmosDBAccountResourceId')]" - }, - "existingDnsZones": { - "value": "[parameters('existingDnsZones')]" - }, - "dnsZoneNames": { - "value": "[parameters('dnsZoneNames')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "7641310640078958122" - } - }, - "parameters": { - "aiSearchResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the AI Search Service." - } - }, - "azureStorageAccountResourceId": { - "type": "string", - "metadata": { - "description": "Resource ID of the Azure Storage Account." - } - }, - "azureCosmosDBAccountResourceId": { - "type": "string", - "metadata": { - "description": "ResourceId of Cosmos DB Account" - } - }, - "existingDnsZones": { - "type": "object", - "metadata": { - "description": "Object mapping DNS zone names to their resource group, or empty string to indicate creation" - } - }, - "dnsZoneNames": { - "type": "array", - "metadata": { - "description": "List of private DNS zone names to validate" - } - } - }, - "variables": { - "storagePassedIn": "[not(equals(parameters('azureStorageAccountResourceId'), ''))]", - "searchPassedIn": "[not(equals(parameters('aiSearchResourceId'), ''))]", - "cosmosPassedIn": "[not(equals(parameters('azureCosmosDBAccountResourceId'), ''))]", - "storageParts": "[split(parameters('azureStorageAccountResourceId'), '/')]", - "azureStorageSubscriptionId": "[if(and(variables('storagePassedIn'), greater(length(variables('storageParts')), 2)), variables('storageParts')[2], subscription().subscriptionId)]", - "azureStorageResourceGroupName": "[if(and(variables('storagePassedIn'), greater(length(variables('storageParts')), 4)), variables('storageParts')[4], resourceGroup().name)]", - "acsParts": "[split(parameters('aiSearchResourceId'), '/')]", - "aiSearchServiceSubscriptionId": "[if(and(variables('searchPassedIn'), greater(length(variables('acsParts')), 2)), variables('acsParts')[2], subscription().subscriptionId)]", - "aiSearchServiceResourceGroupName": "[if(and(variables('searchPassedIn'), greater(length(variables('acsParts')), 4)), variables('acsParts')[4], resourceGroup().name)]", - "cosmosParts": "[split(parameters('azureCosmosDBAccountResourceId'), '/')]", - "cosmosDBSubscriptionId": "[if(and(variables('cosmosPassedIn'), greater(length(variables('cosmosParts')), 2)), variables('cosmosParts')[2], subscription().subscriptionId)]", - "cosmosDBResourceGroupName": "[if(and(variables('cosmosPassedIn'), greater(length(variables('cosmosParts')), 4)), variables('cosmosParts')[4], resourceGroup().name)]", - "dnsZoneTypes": [ - "Microsoft.Network/privateDnsZones" - ] - }, - "resources": [], - "outputs": { - "aiSearchExists": { - "type": "bool", - "value": "[and(variables('searchPassedIn'), equals(last(split(parameters('aiSearchResourceId'), '/')), variables('acsParts')[8]))]" - }, - "cosmosDBExists": { - "type": "bool", - "value": "[and(variables('cosmosPassedIn'), equals(last(split(parameters('azureCosmosDBAccountResourceId'), '/')), variables('cosmosParts')[8]))]" - }, - "azureStorageExists": { - "type": "bool", - "value": "[and(variables('storagePassedIn'), equals(last(split(parameters('azureStorageAccountResourceId'), '/')), variables('storageParts')[8]))]" - }, - "aiSearchServiceSubscriptionId": { - "type": "string", - "value": "[variables('aiSearchServiceSubscriptionId')]" - }, - "aiSearchServiceResourceGroupName": { - "type": "string", - "value": "[variables('aiSearchServiceResourceGroupName')]" - }, - "cosmosDBSubscriptionId": { - "type": "string", - "value": "[variables('cosmosDBSubscriptionId')]" - }, - "cosmosDBResourceGroupName": { - "type": "string", - "value": "[variables('cosmosDBResourceGroupName')]" - }, - "azureStorageSubscriptionId": { - "type": "string", - "value": "[variables('azureStorageSubscriptionId')]" - }, - "azureStorageResourceGroupName": { - "type": "string", - "value": "[variables('azureStorageResourceGroupName')]" - }, - "dnsZoneExists": { - "type": "array", - "copy": { - "count": "[length(parameters('dnsZoneNames'))]", - "input": { - "name": "[parameters('dnsZoneNames')[copyIndex()]]", - "exists": "[not(empty(parameters('existingDnsZones')[parameters('dnsZoneNames')[copyIndex()]]))]" - } - } - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('dependencies-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "azureStorageName": { - "value": "[variables('azureStorageName')]" - }, - "aiSearchName": { - "value": "[variables('aiSearchName')]" - }, - "cosmosDBName": { - "value": "[variables('cosmosDBName')]" - }, - "aiSearchResourceId": { - "value": "[parameters('aiSearchResourceId')]" - }, - "aiSearchExists": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchExists.value]" - }, - "azureStorageAccountResourceId": { - "value": "[parameters('azureStorageAccountResourceId')]" - }, - "azureStorageExists": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageExists.value]" - }, - "cosmosDBResourceId": { - "value": "[parameters('azureCosmosDBAccountResourceId')]" - }, - "cosmosDBExists": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBExists.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "2754228344238136934" - } - }, - "parameters": { - "location": { - "type": "string", - "metadata": { - "description": "Azure region of the deployment" - } - }, - "aiSearchName": { - "type": "string", - "metadata": { - "description": "The name of the AI Search resource" - } - }, - "azureStorageName": { - "type": "string", - "metadata": { - "description": "Name of the storage account" - } - }, - "cosmosDBName": { - "type": "string", - "metadata": { - "description": "Name of the new Cosmos DB account" - } - }, - "aiSearchResourceId": { - "type": "string", - "metadata": { - "description": "The AI Search Service full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "azureStorageAccountResourceId": { - "type": "string", - "metadata": { - "description": "The AI Storage Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "cosmosDBResourceId": { - "type": "string", - "metadata": { - "description": "The Cosmos DB Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." - } - }, - "aiSearchExists": { - "type": "bool" - }, - "azureStorageExists": { - "type": "bool" - }, - "cosmosDBExists": { - "type": "bool" - }, - "noZRSRegions": { - "type": "array", - "defaultValue": [ - "southindia", - "westus" - ] - }, - "sku": { - "type": "object", - "defaultValue": "[if(contains(parameters('noZRSRegions'), parameters('location')), createObject('name', 'Standard_GRS'), createObject('name', 'Standard_ZRS'))]" - } - }, - "variables": { - "cosmosParts": "[split(parameters('cosmosDBResourceId'), '/')]", - "canaryRegions": [ - "eastus2euap", - "centraluseuap" - ], - "cosmosDbRegion": "[if(contains(variables('canaryRegions'), parameters('location')), 'westus', parameters('location'))]", - "acsParts": "[split(parameters('aiSearchResourceId'), '/')]", - "azureStorageParts": "[split(parameters('azureStorageAccountResourceId'), '/')]" - }, - "resources": [ - { - "condition": "[not(parameters('cosmosDBExists'))]", - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2024-11-15", - "name": "[parameters('cosmosDBName')]", - "location": "[variables('cosmosDbRegion')]", - "kind": "GlobalDocumentDB", - "properties": { - "consistencyPolicy": { - "defaultConsistencyLevel": "Session" - }, - "disableLocalAuth": true, - "enableAutomaticFailover": false, - "enableMultipleWriteLocations": false, - "publicNetworkAccess": "Disabled", - "enableFreeTier": false, - "locations": [ - { - "locationName": "[parameters('location')]", - "failoverPriority": 0, - "isZoneRedundant": false - } - ], - "databaseAccountOfferType": "Standard" - } - }, - { - "condition": "[not(parameters('aiSearchExists'))]", - "type": "Microsoft.Search/searchServices", - "apiVersion": "2024-06-01-preview", - "name": "[parameters('aiSearchName')]", - "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "disableLocalAuth": false, - "authOptions": { - "aadOrApiKey": { - "aadAuthFailureMode": "http401WithBearerChallenge" - } - }, - "encryptionWithCmk": { - "enforcement": "Unspecified" - }, - "hostingMode": "default", - "partitionCount": 1, - "publicNetworkAccess": "disabled", - "replicaCount": 1, - "semanticSearch": "disabled", - "networkRuleSet": { - "bypass": "None", - "ipRules": [] - } - }, - "sku": { - "name": "standard" - } - }, - { - "condition": "[not(parameters('azureStorageExists'))]", - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-05-01", - "name": "[parameters('azureStorageName')]", - "location": "[parameters('location')]", - "kind": "StorageV2", - "sku": "[parameters('sku')]", - "properties": { - "minimumTlsVersion": "TLS1_2", - "allowBlobPublicAccess": false, - "publicNetworkAccess": "Disabled", - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "Deny", - "virtualNetworkRules": [] - }, - "allowSharedKeyAccess": false - } - } - ], - "outputs": { - "aiSearchName": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), variables('acsParts')[8], parameters('aiSearchName'))]" - }, - "aiSearchID": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('acsParts')[2], variables('acsParts')[4]), 'Microsoft.Search/searchServices', variables('acsParts')[8]), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]" - }, - "aiSearchServiceResourceGroupName": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), variables('acsParts')[4], resourceGroup().name)]" - }, - "aiSearchServiceSubscriptionId": { - "type": "string", - "value": "[if(parameters('aiSearchExists'), variables('acsParts')[2], subscription().subscriptionId)]" - }, - "azureStorageName": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), variables('azureStorageParts')[8], parameters('azureStorageName'))]" - }, - "azureStorageId": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('azureStorageParts')[2], variables('azureStorageParts')[4]), 'Microsoft.Storage/storageAccounts', variables('azureStorageParts')[8]), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]" - }, - "azureStorageResourceGroupName": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), variables('azureStorageParts')[4], resourceGroup().name)]" - }, - "azureStorageSubscriptionId": { - "type": "string", - "value": "[if(parameters('azureStorageExists'), variables('azureStorageParts')[2], subscription().subscriptionId)]" - }, - "cosmosDBName": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), variables('cosmosParts')[8], parameters('cosmosDBName'))]" - }, - "cosmosDBId": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('cosmosParts')[2], variables('cosmosParts')[4]), 'Microsoft.DocumentDB/databaseAccounts', variables('cosmosParts')[8]), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]" - }, - "cosmosDBResourceGroupName": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), variables('cosmosParts')[4], resourceGroup().name)]" - }, - "cosmosDBSubscriptionId": { - "type": "string", - "value": "[if(parameters('cosmosDBExists'), variables('cosmosParts')[2], subscription().subscriptionId)]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-deployment', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-private-endpoint', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiAccountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))), '2025-04-01').outputs.accountName.value]" - }, - "aiSearchName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchName.value]" - }, - "storageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "cosmosDBName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "fabricWorkspaceResourceId": { - "value": "[parameters('fabricWorkspaceResourceId')]" - }, - "vnetName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.virtualNetworkName.value]" - }, - "peSubnetName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.peSubnetName.value]" - }, - "suffix": { - "value": "[variables('uniqueSuffix')]" - }, - "vnetResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.virtualNetworkResourceGroup.value]" - }, - "vnetSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix'))), '2025-04-01').outputs.virtualNetworkSubscriptionId.value]" - }, - "cosmosDBSubscriptionId": { - "value": "[variables('cosmosDBSubscriptionId')]" - }, - "cosmosDBResourceGroupName": { - "value": "[variables('cosmosDBResourceGroupName')]" - }, - "aiSearchSubscriptionId": { - "value": "[variables('aiSearchServiceSubscriptionId')]" - }, - "aiSearchResourceGroupName": { - "value": "[variables('aiSearchServiceResourceGroupName')]" - }, - "storageAccountResourceGroupName": { - "value": "[variables('azureStorageResourceGroupName')]" - }, - "storageAccountSubscriptionId": { - "value": "[variables('azureStorageSubscriptionId')]" - }, - "existingDnsZones": { - "value": "[parameters('existingDnsZones')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "10536644141407027053" - } - }, - "parameters": { - "aiAccountName": { - "type": "string", - "metadata": { - "description": "Name of the AI Foundry account" - } - }, - "aiSearchName": { - "type": "string", - "metadata": { - "description": "Name of the AI Search service" - } - }, - "storageName": { - "type": "string", - "metadata": { - "description": "Name of the storage account" - } - }, - "cosmosDBName": { - "type": "string", - "metadata": { - "description": "Name of the Cosmos DB account" - } - }, - "fabricWorkspaceResourceId": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The Microsoft Fabric Workspace full ARM Resource ID. Optional - leave empty to skip Fabric private endpoint." - } - }, - "vnetName": { - "type": "string", - "metadata": { - "description": "Name of the Vnet" - } - }, - "peSubnetName": { - "type": "string", - "metadata": { - "description": "Name of the Customer subnet" - } - }, - "suffix": { - "type": "string", - "metadata": { - "description": "Suffix for unique resource names" - } - }, - "vnetResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name for existing Virtual Network (if different from current resource group)" - } - }, - "vnetSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for Virtual Network" - } - }, - "storageAccountResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name for Storage Account" - } - }, - "storageAccountSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for Storage account" - } - }, - "aiSearchSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for AI Search service" - } - }, - "aiSearchResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource Group name for AI Search service" - } - }, - "cosmosDBSubscriptionId": { - "type": "string", - "defaultValue": "[subscription().subscriptionId]", - "metadata": { - "description": "Subscription ID for Cosmos DB account" - } - }, - "cosmosDBResourceGroupName": { - "type": "string", - "defaultValue": "[resourceGroup().name]", - "metadata": { - "description": "Resource group name for Cosmos DB account" - } - }, - "existingDnsZones": { - "type": "object", - "defaultValue": { - "privatelink.services.ai.azure.com": "", - "privatelink.openai.azure.com": "", - "privatelink.cognitiveservices.azure.com": "", - "privatelink.search.windows.net": "", - "[format('privatelink.blob.{0}', environment().suffixes.storage)]": "", - "privatelink.documents.azure.com": "", - "privatelink.fabric.microsoft.com": "" - }, - "metadata": { - "description": "Map of DNS zone FQDNs to resource group names. If provided, reference existing DNS zones in this resource group instead of creating them." - } - } - }, - "variables": { - "fabricPassedIn": "[not(equals(parameters('fabricWorkspaceResourceId'), ''))]", - "fabricParts": "[split(parameters('fabricWorkspaceResourceId'), '/')]", - "fabricWorkspaceName": "[if(variables('fabricPassedIn'), last(variables('fabricParts')), '')]", - "aiServicesDnsZoneName": "privatelink.services.ai.azure.com", - "openAiDnsZoneName": "privatelink.openai.azure.com", - "cognitiveServicesDnsZoneName": "privatelink.cognitiveservices.azure.com", - "aiSearchDnsZoneName": "privatelink.search.windows.net", - "storageDnsZoneName": "[format('privatelink.blob.{0}', environment().suffixes.storage)]", - "cosmosDBDnsZoneName": "privatelink.documents.azure.com", - "fabricDnsZoneName": "privatelink.fabric.microsoft.com", - "aiServicesDnsZoneRG": "[parameters('existingDnsZones')[variables('aiServicesDnsZoneName')]]", - "openAiDnsZoneRG": "[parameters('existingDnsZones')[variables('openAiDnsZoneName')]]", - "cognitiveServicesDnsZoneRG": "[parameters('existingDnsZones')[variables('cognitiveServicesDnsZoneName')]]", - "aiSearchDnsZoneRG": "[parameters('existingDnsZones')[variables('aiSearchDnsZoneName')]]", - "storageDnsZoneRG": "[parameters('existingDnsZones')[variables('storageDnsZoneName')]]", - "cosmosDBDnsZoneRG": "[parameters('existingDnsZones')[variables('cosmosDBDnsZoneName')]]", - "fabricDnsZoneRG": "[coalesce(tryGet(parameters('existingDnsZones'), 'fabricDnsZoneName'), '')]", - "aiServicesDnsZoneId": "[if(empty(variables('aiServicesDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('aiServicesDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName')))]", - "openAiDnsZoneId": "[if(empty(variables('openAiDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('openAiDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName')))]", - "cognitiveServicesDnsZoneId": "[if(empty(variables('cognitiveServicesDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('cognitiveServicesDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName')))]", - "aiSearchDnsZoneId": "[if(empty(variables('aiSearchDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('aiSearchDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName')))]", - "storageDnsZoneId": "[if(empty(variables('storageDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('storageDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('storageDnsZoneName')))]", - "cosmosDBDnsZoneId": "[if(empty(variables('cosmosDBDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('cosmosDBDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName')))]", - "fabricDnsZoneId": "[if(variables('fabricPassedIn'), if(empty(variables('fabricDnsZoneRG')), resourceId('Microsoft.Network/privateDnsZones', variables('fabricDnsZoneName')), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('fabricDnsZoneRG')), 'Microsoft.Network/privateDnsZones', variables('fabricDnsZoneName'))), '')]" - }, - "resources": [ - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('aiAccountName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('aiAccountName'))]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('aiAccountName'))]", - "groupIds": [ - "account" - ] - } - } - ] - } - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('aiSearchName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('aiSearchName'))]", - "properties": { - "privateLinkServiceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('aiSearchSubscriptionId'), parameters('aiSearchResourceGroupName')), 'Microsoft.Search/searchServices', parameters('aiSearchName'))]", - "groupIds": [ - "searchService" - ] - } - } - ] - } - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('storageName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('storageName'))]", - "properties": { - "privateLinkServiceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('storageAccountSubscriptionId'), parameters('storageAccountResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('storageName'))]", - "groupIds": [ - "blob" - ] - } - } - ] - } - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-private-endpoint', parameters('cosmosDBName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', parameters('cosmosDBName'))]", - "properties": { - "privateLinkServiceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", - "groupIds": [ - "Sql" - ] - } - } - ] - } - }, - { - "condition": "[variables('fabricPassedIn')]", - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-05-01", - "name": "[format('{0}-fabric-private-endpoint', variables('fabricWorkspaceName'))]", - "location": "[resourceGroup().location]", - "properties": { - "subnet": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('peSubnetName'))]" - }, - "privateLinkServiceConnections": [ - { - "name": "[format('{0}-private-link-service-connection', variables('fabricWorkspaceName'))]", - "properties": { - "privateLinkServiceId": "[parameters('fabricWorkspaceResourceId')]", - "groupIds": [ - "Fabric" - ] - } - } - ] - } - }, - { - "condition": "[empty(variables('aiServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('aiServicesDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('openAiDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('openAiDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('cognitiveServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('cognitiveServicesDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('aiSearchDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('aiSearchDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('storageDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('storageDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('cosmosDBDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('cosmosDBDnsZoneName')]", - "location": "global" - }, - { - "condition": "[and(variables('fabricPassedIn'), empty(variables('fabricDnsZoneRG')))]", - "type": "Microsoft.Network/privateDnsZones", - "apiVersion": "2020-06-01", - "name": "[variables('fabricDnsZoneName')]", - "location": "global" - }, - { - "condition": "[empty(variables('aiServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('aiServicesDnsZoneName'), format('aiServices-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('openAiDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('openAiDnsZoneName'), format('aiServicesOpenAI-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('cognitiveServicesDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('cognitiveServicesDnsZoneName'), format('aiServicesCognitiveServices-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('aiSearchDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('aiSearchDnsZoneName'), format('aiSearch-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('storageDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('storageDnsZoneName'), format('storage-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName'))]" - ] - }, - { - "condition": "[empty(variables('cosmosDBDnsZoneRG'))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('cosmosDBDnsZoneName'), format('cosmosDB-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName'))]" - ] - }, - { - "condition": "[and(variables('fabricPassedIn'), empty(variables('fabricDnsZoneRG')))]", - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2024-06-01", - "name": "[format('{0}/{1}', variables('fabricDnsZoneName'), format('fabric-{0}-link', parameters('suffix')))]", - "location": "global", - "properties": { - "virtualNetwork": { - "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('vnetSubscriptionId'), parameters('vnetResourceGroupName')), 'Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - }, - "registrationEnabled": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones', variables('fabricDnsZoneName'))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('aiAccountName')), format('{0}-dns-group', parameters('aiAccountName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-aiserv-config', parameters('aiAccountName'))]", - "properties": { - "privateDnsZoneId": "[variables('aiServicesDnsZoneId')]" - } - }, - { - "name": "[format('{0}-dns-openai-config', parameters('aiAccountName'))]", - "properties": { - "privateDnsZoneId": "[variables('openAiDnsZoneId')]" - } - }, - { - "name": "[format('{0}-dns-cogserv-config', parameters('aiAccountName'))]", - "properties": { - "privateDnsZoneId": "[variables('cognitiveServicesDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('aiAccountName')))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('aiServicesDnsZoneName'), format('aiServices-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiServicesDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('cognitiveServicesDnsZoneName'), format('aiServicesCognitiveServices-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('cognitiveServicesDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('openAiDnsZoneName'), format('aiServicesOpenAI-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('openAiDnsZoneName'))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('aiSearchName')), format('{0}-dns-group', parameters('aiSearchName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-config', parameters('aiSearchName'))]", - "properties": { - "privateDnsZoneId": "[variables('aiSearchDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('aiSearchDnsZoneName'), format('aiSearch-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('aiSearchDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('aiSearchName')))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('storageName')), format('{0}-dns-group', parameters('storageName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-config', parameters('storageName'))]", - "properties": { - "privateDnsZoneId": "[variables('storageDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('storageDnsZoneName'), format('storage-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('storageDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('storageName')))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-private-endpoint', parameters('cosmosDBName')), format('{0}-dns-group', parameters('cosmosDBName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-config', parameters('cosmosDBName'))]", - "properties": { - "privateDnsZoneId": "[variables('cosmosDBDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('cosmosDBDnsZoneName'), format('cosmosDB-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('cosmosDBDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-private-endpoint', parameters('cosmosDBName')))]" - ] - }, - { - "condition": "[variables('fabricPassedIn')]", - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-05-01", - "name": "[format('{0}/{1}', format('{0}-fabric-private-endpoint', variables('fabricWorkspaceName')), format('{0}-dns-group', variables('fabricWorkspaceName')))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[format('{0}-dns-config', variables('fabricWorkspaceName'))]", - "properties": { - "privateDnsZoneId": "[variables('fabricDnsZoneId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', variables('fabricDnsZoneName'), format('fabric-{0}-link', parameters('suffix')))]", - "[resourceId('Microsoft.Network/privateDnsZones', variables('fabricDnsZoneName'))]", - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-fabric-private-endpoint', variables('fabricWorkspaceName')))]" - ] - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('vnet-{0}-{1}-deployment', variables('trimVnetName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "projectName": { - "value": "[variables('projectName')]" - }, - "projectDescription": { - "value": "[parameters('projectDescription')]" - }, - "displayName": { - "value": "[parameters('displayName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "aiSearchName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchName.value]" - }, - "aiSearchServiceResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchServiceResourceGroupName.value]" - }, - "aiSearchServiceSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchServiceSubscriptionId.value]" - }, - "cosmosDBName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "cosmosDBSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBSubscriptionId.value]" - }, - "cosmosDBResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBResourceGroupName.value]" - }, - "azureStorageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "azureStorageSubscriptionId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageSubscriptionId.value]" - }, - "azureStorageResourceGroupName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageResourceGroupName.value]" - }, - "accountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))), '2025-04-01').outputs.accountName.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "5095087340309076800" - } - }, - "parameters": { - "accountName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "projectName": { - "type": "string" - }, - "projectDescription": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "aiSearchName": { - "type": "string" - }, - "aiSearchServiceResourceGroupName": { - "type": "string" - }, - "aiSearchServiceSubscriptionId": { - "type": "string" - }, - "cosmosDBName": { - "type": "string" - }, - "cosmosDBSubscriptionId": { - "type": "string" - }, - "cosmosDBResourceGroupName": { - "type": "string" - }, - "azureStorageName": { - "type": "string" - }, - "azureStorageSubscriptionId": { - "type": "string" - }, - "azureStorageResourceGroupName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('cosmosDBName'))]", - "properties": { - "category": "CosmosDB", - "target": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')), '2024-12-01-preview').documentEndpoint]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", - "location": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('cosmosDBSubscriptionId'), parameters('cosmosDBResourceGroupName')), 'Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')), '2024-12-01-preview', 'full').location]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - ] - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('azureStorageName'))]", - "properties": { - "category": "AzureStorageAccount", - "target": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('azureStorageSubscriptionId'), parameters('azureStorageResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('azureStorageName')), '2023-05-01').primaryEndpoints.blob]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('azureStorageSubscriptionId'), parameters('azureStorageResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('azureStorageName'))]", - "location": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('azureStorageSubscriptionId'), parameters('azureStorageResourceGroupName')), 'Microsoft.Storage/storageAccounts', parameters('azureStorageName')), '2023-05-01', 'full').location]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - ] - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('aiSearchName'))]", - "properties": { - "category": "CognitiveSearch", - "target": "[format('https://{0}.search.windows.net', parameters('aiSearchName'))]", - "authType": "AAD", - "metadata": { - "ApiType": "Azure", - "ResourceId": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('aiSearchServiceSubscriptionId'), parameters('aiSearchServiceResourceGroupName')), 'Microsoft.Search/searchServices', parameters('aiSearchName'))]", - "location": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('aiSearchServiceSubscriptionId'), parameters('aiSearchServiceResourceGroupName')), 'Microsoft.Search/searchServices', parameters('aiSearchName')), '2024-06-01-preview', 'full').location]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - ] - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('accountName'), parameters('projectName'))]", - "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "description": "[parameters('projectDescription')]", - "displayName": "[parameters('displayName')]" - } - } - ], - "outputs": { - "projectName": { - "type": "string", - "value": "[parameters('projectName')]" - }, - "projectId": { - "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName'))]" - }, - "projectPrincipalId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName')), '2025-04-01-preview', 'full').identity.principalId]" - }, - "projectWorkspaceId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('accountName'), parameters('projectName')), '2025-04-01-preview').internalId]" - }, - "cosmosDBConnection": { - "type": "string", - "value": "[parameters('cosmosDBName')]" - }, - "azureStorageConnection": { - "type": "string", - "value": "[parameters('azureStorageName')]" - }, - "aiSearchConnection": { - "type": "string", - "value": "[parameters('aiSearchName')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "projectWorkspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectWorkspaceId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "6910483561575524105" - } - }, - "parameters": { - "projectWorkspaceId": { - "type": "string" - } - }, - "variables": { - "part1": "[substring(parameters('projectWorkspaceId'), 0, 8)]", - "part2": "[substring(parameters('projectWorkspaceId'), 8, 4)]", - "part3": "[substring(parameters('projectWorkspaceId'), 12, 4)]", - "part4": "[substring(parameters('projectWorkspaceId'), 16, 4)]", - "part5": "[substring(parameters('projectWorkspaceId'), 20, 12)]", - "formattedGuid": "[format('{0}-{1}-{2}-{3}-{4}', variables('part1'), variables('part2'), variables('part3'), variables('part4'), variables('part5'))]" - }, - "resources": [], - "outputs": { - "projectWorkspaceIdGuid": { - "type": "string", - "value": "[variables('formattedGuid')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('storage-{0}-{1}-deployment', variables('azureStorageName'), variables('uniqueSuffix'))]", - "subscriptionId": "[variables('azureStorageSubscriptionId')]", - "resourceGroup": "[variables('azureStorageResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "azureStorageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "14683840003859985069" - } - }, - "parameters": { - "azureStorageName": { - "type": "string" - }, - "projectPrincipalId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('azureStorageName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", - "principalType": "ServicePrincipal" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('cosmos-account-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('cosmosDBSubscriptionId')]", - "resourceGroup": "[variables('cosmosDBResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "cosmosDBName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "25128059954858801" - } - }, - "parameters": { - "cosmosDBName": { - "type": "string", - "metadata": { - "description": "Name of the Cosmos DB resource" - } - }, - "projectPrincipalId": { - "type": "string", - "metadata": { - "description": "Principal ID of the AI project" - } - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", - "principalType": "ServicePrincipal" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('ai-search-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('aiSearchServiceSubscriptionId')]", - "resourceGroup": "[variables('aiSearchServiceResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiSearchName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchName.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "7968115481508840" - } - }, - "parameters": { - "aiSearchName": { - "type": "string", - "metadata": { - "description": "Name of the AI Search resource" - } - }, - "projectPrincipalId": { - "type": "string", - "metadata": { - "description": "Principal ID of the AI project" - } - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", - "principalType": "ServicePrincipal" - } - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", - "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", - "principalType": "ServicePrincipal" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('capabilityHost-configuration-{0}-deployment', variables('uniqueSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "accountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix'))), '2025-04-01').outputs.accountName.value]" - }, - "projectName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectName.value]" - }, - "cosmosDBConnection": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBConnection.value]" - }, - "azureStorageConnection": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageConnection.value]" - }, - "aiSearchConnection": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.aiSearchConnection.value]" - }, - "projectCapHost": { - "value": "[parameters('projectCapHost')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "17458377866351620215" - } - }, - "parameters": { - "cosmosDBConnection": { - "type": "string" - }, - "azureStorageConnection": { - "type": "string" - }, - "aiSearchConnection": { - "type": "string" - }, - "projectName": { - "type": "string" - }, - "accountName": { - "type": "string" - }, - "projectCapHost": { - "type": "string" - } - }, - "variables": { - "threadConnections": [ - "[format('{0}', parameters('cosmosDBConnection'))]" - ], - "storageConnections": [ - "[format('{0}', parameters('azureStorageConnection'))]" - ], - "vectorStoreConnections": [ - "[format('{0}', parameters('aiSearchConnection'))]" - ] - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts/projects/capabilityHosts", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}/{2}', parameters('accountName'), parameters('projectName'), parameters('projectCapHost'))]", - "properties": { - "capabilityHostKind": "Agents", - "vectorStoreConnections": "[variables('vectorStoreConnections')]", - "storageConnections": "[variables('storageConnections')]", - "threadStorageConnections": "[variables('threadConnections')]" - } - } - ], - "outputs": { - "projectCapHost": { - "type": "string", - "value": "[parameters('projectCapHost')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('accountName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('aiSearchServiceSubscriptionId'), variables('aiSearchServiceResourceGroupName')), 'Microsoft.Resources/deployments', format('ai-search-ra-{0}-deployment', variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('cosmosDBSubscriptionId'), variables('cosmosDBResourceGroupName')), 'Microsoft.Resources/deployments', format('cosmos-account-ra-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-private-endpoint', variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('azureStorageSubscriptionId'), variables('azureStorageResourceGroupName')), 'Microsoft.Resources/deployments', format('storage-{0}-{1}-deployment', variables('azureStorageName'), variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('storage-containers-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('azureStorageSubscriptionId')]", - "resourceGroup": "[variables('azureStorageResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiProjectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - }, - "storageName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.azureStorageName.value]" - }, - "workspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.projectWorkspaceIdGuid.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "13874725855824693255" - } - }, - "parameters": { - "storageName": { - "type": "string", - "metadata": { - "description": "Name of the storage account" - } - }, - "aiProjectPrincipalId": { - "type": "string", - "metadata": { - "description": "Principal ID of the AI Project" - } - }, - "workspaceId": { - "type": "string", - "metadata": { - "description": "Workspace Id of the AI Project" - } - } - }, - "variables": { - "conditionStr": "[format('((!(ActionMatches{{''Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/read''}}) AND !(ActionMatches{{''Microsoft.Storage/storageAccounts/blobServices/containers/blobs/filter/action''}}) AND !(ActionMatches{{''Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/write''}}) ) OR (@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringStartsWithIgnoreCase ''{0}'' AND @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringLikeIgnoreCase ''*-azureml-agent''))', parameters('workspaceId'))]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageName'))]", - "name": "[guid(resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'), resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')))]", - "properties": { - "principalId": "[parameters('aiProjectPrincipalId')]", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", - "principalType": "ServicePrincipal", - "conditionVersion": "2.0", - "condition": "[variables('conditionStr')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('capabilityHost-configuration-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "[format('cosmos-containers-ra-{0}-deployment', variables('uniqueSuffix'))]", - "subscriptionId": "[variables('cosmosDBSubscriptionId')]", - "resourceGroup": "[variables('cosmosDBResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "cosmosAccountName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.cosmosDBName.value]" - }, - "projectWorkspaceId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix'))), '2025-04-01').outputs.projectWorkspaceIdGuid.value]" - }, - "projectPrincipalId": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2025-04-01').outputs.projectPrincipalId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "16291470712974205281" - } - }, - "parameters": { - "cosmosAccountName": { - "type": "string", - "metadata": { - "description": "Name of the AI Search resource" - } - }, - "projectPrincipalId": { - "type": "string", - "metadata": { - "description": "Project name" - } - }, - "projectWorkspaceId": { - "type": "string" - } - }, - "variables": { - "roleDefinitionId": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('cosmosAccountName'), '00000000-0000-0000-0000-000000000002')]", - "accountScope": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/{2}', subscription().subscriptionId, resourceGroup().name, parameters('cosmosAccountName'))]" - }, - "resources": [ - { - "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", - "apiVersion": "2022-05-15", - "name": "[format('{0}/{1}', parameters('cosmosAccountName'), guid(parameters('projectWorkspaceId'), parameters('cosmosAccountName'), variables('roleDefinitionId'), parameters('projectPrincipalId')))]", - "properties": { - "principalId": "[parameters('projectPrincipalId')]", - "roleDefinitionId": "[variables('roleDefinitionId')]", - "scope": "[variables('accountScope')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', format('capabilityHost-configuration-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-deployment', variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", - "[resourceId('Microsoft.Resources/deployments', format('format-project-workspace-id-{0}-deployment', variables('uniqueSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('azureStorageSubscriptionId'), variables('azureStorageResourceGroupName')), 'Microsoft.Resources/deployments', format('storage-containers-ra-{0}-deployment', variables('uniqueSuffix')))]" - ] - } - ] -} \ No newline at end of file diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/mcp-http-server/Dockerfile b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/mcp-http-server/Dockerfile deleted file mode 100644 index 784c302f4..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/mcp-http-server/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -# Multi-Auth MCP Server Image -# -# This Dockerfile simply references the pre-built multi-auth MCP image. -# The source image is maintained at: retrievaltestacr.azurecr.io/multi-auth-mcp/api-multi-auth-mcp-env -# -# To use this image: -# 1. Import to your ACR: -# az acr import --name --source retrievaltestacr.azurecr.io/multi-auth-mcp/api-multi-auth-mcp-env:latest --image multi-auth-mcp:latest -# -# 2. Or build locally using this Dockerfile: -# docker build -t multi-auth-mcp:latest . -# -# The MCP server exposes the following endpoints: -# - /noauth/mcp - No authentication required (for testing) -# - /mcp - Requires authentication -# - /healthz - Health check endpoint -# -# Environment variables (optional): -# - PORT: Server port (default: 8080) -# - TENANT_ID: Azure AD tenant ID -# - MCP_APP_ID: MCP application ID -# - API_KEYS: Comma-separated API keys for authentication - -FROM retrievaltestacr.azurecr.io/multi-auth-mcp/api-multi-auth-mcp-env:latest - -# The base image already configures everything, just expose the port -EXPOSE 8080 diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/metadata.json b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/metadata.json deleted file mode 100644 index a91a5830a..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/metadata.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "Hybrid Private Resources Agent Setup", - "description": "Azure AI Foundry with public API access and private backend resources (AI Search, Cosmos DB, Storage). Enables portal-based agent testing with MCP servers and AI Search tools on private endpoints.", - "version": "1.0.0", - "keywords": [ - "hybrid", - "private-endpoints", - "data-proxy", - "mcp", - "ai-search", - "portal-access" - ], - "architecture": { - "ai_services_access": "public", - "backend_resources": "private", - "data_proxy": "enabled", - "portal_compatible": true - }, - "prerequisites": [ - "Azure subscription with Owner or Contributor role", - "Azure AI Account Owner role for creating AI Services", - "Sufficient quota for gpt-4o-mini model deployment" - ], - "resources_created": [ - "Microsoft.CognitiveServices/accounts (AI Services with public access)", - "Microsoft.CognitiveServices/accounts/projects", - "Microsoft.Search/searchServices (private endpoint)", - "Microsoft.DocumentDB/databaseAccounts (private endpoint)", - "Microsoft.Storage/storageAccounts (private endpoint)", - "Microsoft.Network/virtualNetworks", - "Microsoft.Network/privateEndpoints", - "Microsoft.Network/privateDnsZones" - ] -} \ No newline at end of file diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/add-project-capability-host.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/add-project-capability-host.bicep deleted file mode 100644 index dd2ac3297..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/add-project-capability-host.bicep +++ /dev/null @@ -1,34 +0,0 @@ -param cosmosDBConnection string -param azureStorageConnection string -param aiSearchConnection string -param projectName string -param accountName string -param projectCapHost string - -var threadConnections = ['${cosmosDBConnection}'] -var storageConnections = ['${azureStorageConnection}'] -var vectorStoreConnections = ['${aiSearchConnection}'] - - -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { - name: accountName -} - -resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { - name: projectName - parent: account -} - -resource projectCapabilityHost 'Microsoft.CognitiveServices/accounts/projects/capabilityHosts@2025-04-01-preview' = { - name: projectCapHost - parent: project - properties: { - capabilityHostKind: 'Agents' - vectorStoreConnections: vectorStoreConnections - storageConnections: storageConnections - threadStorageConnections: threadConnections - } - -} - -output projectCapHost string = projectCapabilityHost.name diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-account-identity.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-account-identity.bicep deleted file mode 100644 index 1689c4214..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-account-identity.bicep +++ /dev/null @@ -1,68 +0,0 @@ -param accountName string -param location string -param modelName string -param modelFormat string -param modelVersion string -param modelSkuName string -param modelCapacity int -param agentSubnetId string -param networkInjection string = 'true' - -// Hybrid setup: Public network access disabled by default for the Foundry resource -// The Data Proxy (networkInjections) routes tool calls to private resources - -#disable-next-line BCP036 -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { - name: accountName - location: location - sku: { - name: 'S0' - } - kind: 'AIServices' - identity: { - type: 'SystemAssigned' - } - properties: { - allowProjectManagement: true - customSubDomainName: accountName - networkAcls: { - defaultAction: 'Deny' - virtualNetworkRules: [] - ipRules: [] - bypass: 'AzureServices' - } - publicNetworkAccess: 'Disabled' - networkInjections: ((networkInjection == 'true') - ? [ - { - scenario: 'agent' - subnetArmId: agentSubnetId - useMicrosoftManagedNetwork: false - } - ] - : null) - disableLocalAuth: false - } -} - -#disable-next-line BCP081 -resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2025-04-01-preview' = { - parent: account - name: modelName - sku: { - capacity: modelCapacity - name: modelSkuName - } - properties: { - model: { - name: modelName - format: modelFormat - version: modelVersion - } - } -} - -output accountName string = account.name -output accountID string = account.id -output accountTarget string = account.properties.endpoint -output accountPrincipalId string = account.identity.principalId diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-project-identity-unique.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-project-identity-unique.bicep deleted file mode 100644 index 471e1fb98..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-project-identity-unique.bicep +++ /dev/null @@ -1,106 +0,0 @@ -param accountName string -param location string -param projectName string -param projectDescription string -param displayName string - -param aiSearchName string -param aiSearchServiceResourceGroupName string -param aiSearchServiceSubscriptionId string - -param cosmosDBName string -param cosmosDBSubscriptionId string -param cosmosDBResourceGroupName string - -param azureStorageName string -param azureStorageSubscriptionId string -param azureStorageResourceGroupName string - -// Add unique connection name parameter -param uniqueConnectionSuffix string = '' - -resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = { - name: aiSearchName - scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) -} -resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { - name: cosmosDBName - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) -} -resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { - name: azureStorageName - scope: resourceGroup(azureStorageSubscriptionId, azureStorageResourceGroupName) -} - -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { - name: accountName - scope: resourceGroup() -} - -resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { - parent: account - name: projectName - location: location - identity: { - type: 'SystemAssigned' - } - properties: { - description: projectDescription - displayName: displayName - } - - // Use unique connection names by appending the suffix - resource project_connection_cosmosdb_account 'connections@2025-04-01-preview' = { - name: '${cosmosDBName}${uniqueConnectionSuffix}' - properties: { - category: 'CosmosDB' - target: cosmosDBAccount.properties.documentEndpoint - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: cosmosDBAccount.id - location: cosmosDBAccount.location - } - } - } - - resource project_connection_azure_storage 'connections@2025-04-01-preview' = { - name: '${azureStorageName}${uniqueConnectionSuffix}' - properties: { - category: 'AzureStorageAccount' - target: storageAccount.properties.primaryEndpoints.blob - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: storageAccount.id - location: storageAccount.location - } - } - } - - resource project_connection_azureai_search 'connections@2025-04-01-preview' = { - name: '${aiSearchName}${uniqueConnectionSuffix}' - properties: { - category: 'CognitiveSearch' - target: 'https://${aiSearchName}.search.windows.net' - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: searchService.id - location: searchService.location - } - } - } -} - -output projectName string = project.name -output projectId string = project.id -output projectPrincipalId string = project.identity.principalId - -#disable-next-line BCP053 -output projectWorkspaceId string = project.properties.internalId - -// Return the unique connection names -output cosmosDBConnection string = '${cosmosDBName}${uniqueConnectionSuffix}' -output azureStorageConnection string = '${azureStorageName}${uniqueConnectionSuffix}' -output aiSearchConnection string = '${aiSearchName}${uniqueConnectionSuffix}' diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-project-identity.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-project-identity.bicep deleted file mode 100644 index 90aebfbd3..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-project-identity.bicep +++ /dev/null @@ -1,103 +0,0 @@ -param accountName string -param location string -param projectName string -param projectDescription string -param displayName string - -param aiSearchName string -param aiSearchServiceResourceGroupName string -param aiSearchServiceSubscriptionId string - -param cosmosDBName string -param cosmosDBSubscriptionId string -param cosmosDBResourceGroupName string - -param azureStorageName string -param azureStorageSubscriptionId string -param azureStorageResourceGroupName string - -resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = { - name: aiSearchName - scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) -} -resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { - name: cosmosDBName - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) -} -resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { - name: azureStorageName - scope: resourceGroup(azureStorageSubscriptionId, azureStorageResourceGroupName) -} - -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { - name: accountName - scope: resourceGroup() -} - -resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { - parent: account - name: projectName - location: location - identity: { - type: 'SystemAssigned' - } - properties: { - description: projectDescription - displayName: displayName - } - - resource project_connection_cosmosdb_account 'connections@2025-04-01-preview' = { - name: cosmosDBName - properties: { - category: 'CosmosDB' - target: cosmosDBAccount.properties.documentEndpoint - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: cosmosDBAccount.id - location: cosmosDBAccount.location - } - } - } - - resource project_connection_azure_storage 'connections@2025-04-01-preview' = { - name: azureStorageName - properties: { - category: 'AzureStorageAccount' - target: storageAccount.properties.primaryEndpoints.blob - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: storageAccount.id - location: storageAccount.location - } - } - } - - resource project_connection_azureai_search 'connections@2025-04-01-preview' = { - name: aiSearchName - properties: { - category: 'CognitiveSearch' - target: 'https://${aiSearchName}.search.windows.net' - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: searchService.id - location: searchService.location - } - } - } - -} - -output projectName string = project.name -output projectId string = project.id -output projectPrincipalId string = project.identity.principalId - -#disable-next-line BCP053 -output projectWorkspaceId string = project.properties.internalId - -// return the BYO connection names -output cosmosDBConnection string = cosmosDBName -output azureStorageConnection string = azureStorageName -output aiSearchConnection string = aiSearchName diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-search-role-assignments.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-search-role-assignments.bicep deleted file mode 100644 index 715663a6c..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/ai-search-role-assignments.bicep +++ /dev/null @@ -1,43 +0,0 @@ -// Assigns the necessary roles to the AI project - -@description('Name of the AI Search resource') -param aiSearchName string - -@description('Principal ID of the AI project') -param projectPrincipalId string - -resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = { - name: aiSearchName - scope: resourceGroup() -} - -// search roles -resource searchIndexDataContributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { - name: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' - scope: resourceGroup() -} - -resource searchIndexDataContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: searchService - name: guid(projectPrincipalId, searchIndexDataContributorRole.id, searchService.id) - properties: { - principalId: projectPrincipalId - roleDefinitionId: searchIndexDataContributorRole.id - principalType: 'ServicePrincipal' - } -} - -resource searchServiceContributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { - name: '7ca78c08-252a-4471-8644-bb5ff32d4ba0' - scope: resourceGroup() -} - -resource searchServiceContributorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: searchService - name: guid(projectPrincipalId, searchServiceContributorRole.id, searchService.id) - properties: { - principalId: projectPrincipalId - roleDefinitionId: searchServiceContributorRole.id - principalType: 'ServicePrincipal' - } -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/azure-storage-account-role-assignment.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/azure-storage-account-role-assignment.bicep deleted file mode 100644 index afc355a48..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/azure-storage-account-role-assignment.bicep +++ /dev/null @@ -1,24 +0,0 @@ -param azureStorageName string -param projectPrincipalId string - -resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { - name: azureStorageName - scope: resourceGroup() -} - -// Blob Storage Owner: b7e6dc6d-f1e8-4753-8033-0f276bb0955b -// Blob Storage Contributor: ba92f5b4-2d11-453d-a403-e96b0029c9fe -resource storageBlobDataContributor 'Microsoft.Authorization/roleDefinitions@2022-05-01-preview' existing = { - name: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' - scope: resourceGroup() -} - -resource storageBlobDataContributorRoleAssignmentProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: storageAccount - name: guid(projectPrincipalId, storageBlobDataContributor.id, storageAccount.id) - properties: { - principalId: projectPrincipalId - roleDefinitionId: storageBlobDataContributor.id - principalType: 'ServicePrincipal' - } -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/blob-storage-container-role-assignments-unique.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/blob-storage-container-role-assignments-unique.bicep deleted file mode 100644 index 2535a42c9..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/blob-storage-container-role-assignments-unique.bicep +++ /dev/null @@ -1,38 +0,0 @@ -@description('Name of the storage account') -param storageName string - -@description('Principal ID of the AI Project') -param aiProjectPrincipalId string - -@description('Workspace Id of the AI Project') -param workspaceId string - -@description('Unique suffix to make role assignment unique') -param uniqueSuffix string - -// Reference existing storage account -resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' existing = { - name: storageName - scope: resourceGroup() -} - -// Storage Blob Data Owner Role -resource storageBlobDataOwner 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { - name: 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b' // Built-in role ID - scope: resourceGroup() -} - -var conditionStr= '((!(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/read\'}) AND !(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/filter/action\'}) AND !(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/write\'}) ) OR (@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringStartsWithIgnoreCase \'${workspaceId}\' AND @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringLikeIgnoreCase \'*-azureml-agent\'))' - -// Assign Storage Blob Data Owner role with unique name -resource storageBlobDataOwnerAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: storage - name: guid(storageBlobDataOwner.id, storage.id, aiProjectPrincipalId, uniqueSuffix) - properties: { - principalId: aiProjectPrincipalId - roleDefinitionId: storageBlobDataOwner.id - principalType: 'ServicePrincipal' - conditionVersion: '2.0' - condition: conditionStr - } -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/blob-storage-container-role-assignments.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/blob-storage-container-role-assignments.bicep deleted file mode 100644 index 71abc97d6..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/blob-storage-container-role-assignments.bicep +++ /dev/null @@ -1,36 +0,0 @@ -@description('Name of the storage account') -param storageName string - -@description('Principal ID of the AI Project') -param aiProjectPrincipalId string - -@description('Workspace Id of the AI Project') -param workspaceId string - - -// Reference existing storage account -resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' existing = { - name: storageName - scope: resourceGroup() -} - -// Storage Blob Data Owner Role -resource storageBlobDataOwner 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { - name: 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b' // Built-in role ID - scope: resourceGroup() -} - -var conditionStr= '((!(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/read\'}) AND !(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/filter/action\'}) AND !(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/tags/write\'}) ) OR (@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringStartsWithIgnoreCase \'${workspaceId}\' AND @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringLikeIgnoreCase \'*-azureml-agent\'))' - -// Assign Storage Blob Data Owner role -resource storageBlobDataOwnerAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: storage - name: guid(storageBlobDataOwner.id, storage.id) - properties: { - principalId: aiProjectPrincipalId - roleDefinitionId: storageBlobDataOwner.id - principalType: 'ServicePrincipal' - conditionVersion: '2.0' - condition: conditionStr - } -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/cosmos-container-role-assignments.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/cosmos-container-role-assignments.bicep deleted file mode 100644 index a196cf80e..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/cosmos-container-role-assignments.bicep +++ /dev/null @@ -1,32 +0,0 @@ -// Assigns the necessary roles to the AI project - -@description('Name of the AI Search resource') -param cosmosAccountName string - -@description('Project name') -param projectPrincipalId string - -param projectWorkspaceId string - -resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { - name: cosmosAccountName - scope: resourceGroup() -} - -var roleDefinitionId = resourceId( - 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', - cosmosAccountName, - '00000000-0000-0000-0000-000000000002' -) - -var accountScope = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccountName}' - -resource containerRoleAssignmentUserContainer 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { - parent: cosmosAccount - name: guid(projectWorkspaceId, cosmosAccountName, roleDefinitionId, projectPrincipalId) - properties: { - principalId: projectPrincipalId - roleDefinitionId: roleDefinitionId - scope: accountScope - } -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/cosmosdb-account-role-assignment.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/cosmosdb-account-role-assignment.bicep deleted file mode 100644 index d5d083486..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/cosmosdb-account-role-assignment.bicep +++ /dev/null @@ -1,27 +0,0 @@ -// Assigns Role Cosmos DB Operator to the Project Principal ID -@description('Name of the Cosmos DB resource') -param cosmosDBName string - -@description('Principal ID of the AI project') -param projectPrincipalId string - - -resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { - name: cosmosDBName - scope: resourceGroup() -} - -resource cosmosDBOperatorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { - name: '230815da-be43-4aae-9cb4-875f7bd000aa' - scope: resourceGroup() -} - -resource cosmosDBOperatorRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - scope: cosmosDBAccount - name: guid(projectPrincipalId, cosmosDBOperatorRole.id, cosmosDBAccount.id) - properties: { - principalId: projectPrincipalId - roleDefinitionId: cosmosDBOperatorRole.id - principalType: 'ServicePrincipal' - } -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/existing-vnet.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/existing-vnet.bicep deleted file mode 100644 index b464dedba..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/existing-vnet.bicep +++ /dev/null @@ -1,116 +0,0 @@ -/* -Virtual Network Module -This module works with existing virtual networks and required subnets. - -1. Flexibility: - - Works with any existing VNet address space - - Can use existing subnets or create new ones - - Cross-resource group support - -2. Security Features: - - Network isolation - - Subnet delegation for containerized workloads - - Private endpoint subnet for secure connectivity -*/ - -@description('The name of the existing virtual network') -param vnetName string - -@description('Subscription ID of virtual network (if different from current subscription)') -param vnetSubscriptionId string = subscription().subscriptionId - -@description('Resource Group name of the existing VNet (if different from current resource group)') -param vnetResourceGroupName string = resourceGroup().name - -@description('The name of Agents Subnet') -param agentSubnetName string = 'agent-subnet' - -@description('The name of Private Endpoint subnet') -param peSubnetName string = 'pe-subnet' - -@description('The name of MCP subnet for user-deployed Container Apps') -param mcpSubnetName string = 'mcp-subnet' - -@description('Address prefix for the agent subnet (only needed if creating new subnet)') -param agentSubnetPrefix string = '' - -@description('Address prefix for the private endpoint subnet (only needed if creating new subnet)') -param peSubnetPrefix string = '' - -@description('Address prefix for the MCP subnet (only needed if creating new subnet)') -param mcpSubnetPrefix string = '' - -// Get the address space (array of CIDR strings) -var vnetAddressSpace = existingVNet.properties.addressSpace.addressPrefixes[0] - -var agentSubnetSpaces = empty(agentSubnetPrefix) ? cidrSubnet(vnetAddressSpace, 24, 0) : agentSubnetPrefix -var peSubnetSpaces = empty(peSubnetPrefix) ? cidrSubnet(vnetAddressSpace, 24, 1) : peSubnetPrefix -var mcpSubnetSpaces = empty(mcpSubnetPrefix) ? cidrSubnet(vnetAddressSpace, 24, 2) : mcpSubnetPrefix - -// Reference the existing virtual network -resource existingVNet 'Microsoft.Network/virtualNetworks@2024-05-01' existing = { - name: vnetName - scope: resourceGroup(vnetResourceGroupName) -} - -// Create the agent subnet if requested -module agentSubnet 'subnet.bicep' = { - name: 'agent-subnet-${uniqueString(deployment().name, agentSubnetName)}' - scope: resourceGroup(vnetResourceGroupName) - params: { - vnetName: vnetName - subnetName: agentSubnetName - addressPrefix: agentSubnetSpaces - delegations: [ - { - name: 'Microsoft.App/environments' - properties: { - serviceName: 'Microsoft.App/environments' - } - } - ] - } -} - -// Create the private endpoint subnet if requested -module peSubnet 'subnet.bicep' = { - name: 'pe-subnet-${uniqueString(deployment().name, peSubnetName)}' - scope: resourceGroup(vnetResourceGroupName) - params: { - vnetName: vnetName - subnetName: peSubnetName - addressPrefix: peSubnetSpaces - delegations: [] - } -} - -// Create the MCP subnet for user-deployed Container Apps -module mcpSubnet 'subnet.bicep' = { - name: 'mcp-subnet-${uniqueString(deployment().name, mcpSubnetName)}' - scope: resourceGroup(vnetResourceGroupName) - params: { - vnetName: vnetName - subnetName: mcpSubnetName - addressPrefix: mcpSubnetSpaces - delegations: [ - { - name: 'Microsoft.App/environments' - properties: { - serviceName: 'Microsoft.App/environments' - } - } - ] - } -} - -// Output variables -output peSubnetName string = peSubnetName -output agentSubnetName string = agentSubnetName -output mcpSubnetName string = mcpSubnetName -output agentSubnetId string = '${existingVNet.id}/subnets/${agentSubnetName}' -output peSubnetId string = '${existingVNet.id}/subnets/${peSubnetName}' -output mcpSubnetId string = '${existingVNet.id}/subnets/${mcpSubnetName}' -output virtualNetworkName string = existingVNet.name -output virtualNetworkId string = existingVNet.id -output virtualNetworkResourceGroup string = vnetResourceGroupName -output virtualNetworkSubscriptionId string = vnetSubscriptionId diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/format-project-workspace-id.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/format-project-workspace-id.bicep deleted file mode 100644 index ac7d0c3f2..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/format-project-workspace-id.bicep +++ /dev/null @@ -1,12 +0,0 @@ - -param projectWorkspaceId string - -var part1 = substring(projectWorkspaceId, 0, 8) // First 8 characters -var part2 = substring(projectWorkspaceId, 8, 4) // Next 4 characters -var part3 = substring(projectWorkspaceId, 12, 4) // Next 4 characters -var part4 = substring(projectWorkspaceId, 16, 4) // Next 4 characters -var part5 = substring(projectWorkspaceId, 20, 12) // Remaining 12 characters - -var formattedGuid = '${part1}-${part2}-${part3}-${part4}-${part5}' - -output projectWorkspaceIdGuid string = formattedGuid diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/network-agent-vnet.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/network-agent-vnet.bicep deleted file mode 100644 index 7be3fa966..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/network-agent-vnet.bicep +++ /dev/null @@ -1,87 +0,0 @@ -@description('Azure region for the deployment') -param location string - -@description('The name of the virtual network') -param vnetName string - -@description('Indicates if an existing VNet should be used') -param useExistingVnet bool = false - -@description('Subscription ID of the existing VNet (if different from current subscription)') -param existingVnetSubscriptionId string = subscription().subscriptionId - -@description('Resource Group name of the existing VNet (if different from current resource group)') -param existingVnetResourceGroupName string = resourceGroup().name - -@description('The name of Agents Subnet') -param agentSubnetName string = 'agent-subnet' - -@description('The name of Private Endpoint subnet') -param peSubnetName string = 'pe-subnet' - -@description('The name of MCP subnet for user-deployed Container Apps') -param mcpSubnetName string = 'mcp-subnet' - -@description('Address space for the VNet (only used for new VNet)') -param vnetAddressPrefix string = '' - -@description('Address prefix for the agent subnet') -param agentSubnetPrefix string = '' - -@description('Address prefix for the private endpoint subnet') -param peSubnetPrefix string = '' - -@description('Address prefix for the MCP subnet') -param mcpSubnetPrefix string = '' - -// Create new VNet if needed -module newVNet 'vnet.bicep' = if (!useExistingVnet) { - name: 'vnet-deployment' - params: { - location: location - vnetName: vnetName - agentSubnetName: agentSubnetName - peSubnetName: peSubnetName - mcpSubnetName: mcpSubnetName - vnetAddressPrefix: vnetAddressPrefix - agentSubnetPrefix: agentSubnetPrefix - peSubnetPrefix: peSubnetPrefix - mcpSubnetPrefix: mcpSubnetPrefix - } -} - -// Use existing VNet if requested -module existingVNet 'existing-vnet.bicep' = if (useExistingVnet) { - name: 'existing-vnet-deployment' - params: { - vnetName: vnetName - vnetResourceGroupName: existingVnetResourceGroupName - vnetSubscriptionId: existingVnetSubscriptionId - agentSubnetName: agentSubnetName - peSubnetName: peSubnetName - mcpSubnetName: mcpSubnetName - agentSubnetPrefix: agentSubnetPrefix - peSubnetPrefix: peSubnetPrefix - mcpSubnetPrefix: mcpSubnetPrefix - } -} - -// Provide unified outputs regardless of which module was used -output virtualNetworkName string = useExistingVnet - ? existingVNet.outputs.virtualNetworkName - : newVNet.outputs.virtualNetworkName -output virtualNetworkId string = useExistingVnet - ? existingVNet.outputs.virtualNetworkId - : newVNet.outputs.virtualNetworkId -output virtualNetworkSubscriptionId string = useExistingVnet - ? existingVNet.outputs.virtualNetworkSubscriptionId - : newVNet.outputs.virtualNetworkSubscriptionId -output virtualNetworkResourceGroup string = useExistingVnet - ? existingVNet.outputs.virtualNetworkResourceGroup - : newVNet.outputs.virtualNetworkResourceGroup -output agentSubnetName string = agentSubnetName -output peSubnetName string = peSubnetName -output mcpSubnetName string = mcpSubnetName -output agentSubnetId string = useExistingVnet ? existingVNet.outputs.agentSubnetId : newVNet.outputs.agentSubnetId -output peSubnetId string = useExistingVnet ? existingVNet.outputs.peSubnetId : newVNet.outputs.peSubnetId -output mcpSubnetId string = useExistingVnet ? existingVNet.outputs.mcpSubnetId : newVNet.outputs.mcpSubnetId diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/private-endpoint-and-dns.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/private-endpoint-and-dns.bicep deleted file mode 100644 index 96387c417..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/private-endpoint-and-dns.bicep +++ /dev/null @@ -1,478 +0,0 @@ -/* -Private Endpoint and DNS Configuration Module ------------------------------------------- -This module configures private network access for Azure services using: - -1. Private Endpoints: - - Creates network interfaces in the specified subnet - - Establishes private connections to Azure services - - Enables secure access without public internet exposure - -2. Private DNS Zones: - - Enables custom DNS resolution for private endpoints - -3. DNS Zone Links: - - Links private DNS zones to the VNet - - Enables name resolution for resources in the VNet - - Prevents DNS resolution conflicts - -Security Benefits: -- Eliminates public internet exposure -- Enables secure access from within VNet -- Prevents data exfiltration through network -*/ - -// Resource names and identifiers -@description('Name of the AI Foundry account') -param aiAccountName string -@description('Name of the AI Search service') -param aiSearchName string -@description('Name of the storage account') -param storageName string -@description('Name of the Cosmos DB account') -param cosmosDBName string -@description('The Microsoft Fabric Workspace full ARM Resource ID. Optional - leave empty to skip Fabric private endpoint.') -param fabricWorkspaceResourceId string = '' -@description('Name of the Vnet') -param vnetName string -@description('Name of the Customer subnet') -param peSubnetName string -@description('Suffix for unique resource names') -param suffix string - -@description('Resource Group name for existing Virtual Network (if different from current resource group)') -param vnetResourceGroupName string = resourceGroup().name - -@description('Subscription ID for Virtual Network') -param vnetSubscriptionId string = subscription().subscriptionId - -@description('Resource Group name for Storage Account') -param storageAccountResourceGroupName string = resourceGroup().name - -@description('Subscription ID for Storage account') -param storageAccountSubscriptionId string = subscription().subscriptionId - -@description('Subscription ID for AI Search service') -param aiSearchSubscriptionId string = subscription().subscriptionId - -@description('Resource Group name for AI Search service') -param aiSearchResourceGroupName string = resourceGroup().name - -@description('Subscription ID for Cosmos DB account') -param cosmosDBSubscriptionId string = subscription().subscriptionId - -@description('Resource group name for Cosmos DB account') -param cosmosDBResourceGroupName string = resourceGroup().name - -@description('Map of DNS zone FQDNs to resource group names. If provided, reference existing DNS zones in this resource group instead of creating them.') -param existingDnsZones object = { - 'privatelink.services.ai.azure.com': '' - 'privatelink.openai.azure.com': '' - 'privatelink.cognitiveservices.azure.com': '' - 'privatelink.search.windows.net': '' - 'privatelink.blob.${environment().suffixes.storage}': '' - 'privatelink.documents.azure.com': '' - 'privatelink.fabric.microsoft.com': '' -} - -// ---- Resource references ---- -resource aiAccount 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: aiAccountName - scope: resourceGroup() -} - -resource aiSearch 'Microsoft.Search/searchServices@2023-11-01' existing = { - name: aiSearchName - scope: resourceGroup(aiSearchSubscriptionId, aiSearchResourceGroupName) -} - -resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { - name: storageName - scope: resourceGroup(storageAccountSubscriptionId, storageAccountResourceGroupName) -} - -resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' existing = { - name: cosmosDBName - scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) -} - -// ---- Fabric resource reference (conditional) ---- -var fabricPassedIn = fabricWorkspaceResourceId != '' -var fabricParts = split(fabricWorkspaceResourceId, '/') -var fabricWorkspaceName = fabricPassedIn ? last(fabricParts) : '' - -// Reference existing network resources -resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' existing = { - name: vnetName - scope: resourceGroup(vnetSubscriptionId, vnetResourceGroupName) -} -resource peSubnet 'Microsoft.Network/virtualNetworks/subnets@2024-05-01' existing = { - parent: vnet - name: peSubnetName -} - -/* -------------------------------------------- AI Foundry Account Private Endpoint -------------------------------------------- */ - -// Private endpoint for AI Services account -// - Creates network interface in customer hub subnet -// - Establishes private connection to AI Services account -resource aiAccountPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { - name: '${aiAccountName}-private-endpoint' - location: resourceGroup().location - properties: { - subnet: { id: peSubnet.id } // Deploy in customer hub subnet - privateLinkServiceConnections: [ - { - name: '${aiAccountName}-private-link-service-connection' - properties: { - privateLinkServiceId: aiAccount.id - groupIds: ['account'] // Target AI Services account - } - } - ] - } -} - -/* -------------------------------------------- AI Search Private Endpoint -------------------------------------------- */ - -// Private endpoint for AI Search -// - Creates network interface in customer hub subnet -// - Establishes private connection to AI Search service -resource aiSearchPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { - name: '${aiSearchName}-private-endpoint' - location: resourceGroup().location - properties: { - subnet: { id: peSubnet.id } // Deploy in customer hub subnet - privateLinkServiceConnections: [ - { - name: '${aiSearchName}-private-link-service-connection' - properties: { - privateLinkServiceId: aiSearch.id - groupIds: ['searchService'] // Target search service - } - } - ] - } -} - -/* -------------------------------------------- Storage Private Endpoint -------------------------------------------- */ - -// Private endpoint for Storage Account -// - Creates network interface in customer hub subnet -// - Establishes private connection to blob storage -resource storagePrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { - name: '${storageName}-private-endpoint' - location: resourceGroup().location - properties: { - subnet: { id: peSubnet.id } // Deploy in customer hub subnet - privateLinkServiceConnections: [ - { - name: '${storageName}-private-link-service-connection' - properties: { - privateLinkServiceId: storageAccount.id // Target blob storage - groupIds: ['blob'] - } - } - ] - } -} - -/*--------------------------------------------- Cosmos DB Private Endpoint -------------------------------------*/ - -resource cosmosDBPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { - name: '${cosmosDBName}-private-endpoint' - location: resourceGroup().location - properties: { - subnet: { id: peSubnet.id } // Deploy in customer hub subnet - privateLinkServiceConnections: [ - { - name: '${cosmosDBName}-private-link-service-connection' - properties: { - privateLinkServiceId: cosmosDBAccount.id // Target Cosmos DB account - groupIds: ['Sql'] - } - } - ] - } -} - -/*--------------------------------------------- Microsoft Fabric Private Endpoint -------------------------------------*/ - -// Private endpoint for Microsoft Fabric Workspace -// - Creates network interface in customer private endpoint subnet -// - Establishes private connection to Fabric workspace -// - Only created if fabricWorkspaceResourceId is provided -resource fabricPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = if (fabricPassedIn) { - name: '${fabricWorkspaceName}-fabric-private-endpoint' - location: resourceGroup().location - properties: { - subnet: { id: peSubnet.id } // Deploy in customer private endpoint subnet - privateLinkServiceConnections: [ - { - name: '${fabricWorkspaceName}-private-link-service-connection' - properties: { - privateLinkServiceId: fabricWorkspaceResourceId // Target Fabric workspace - groupIds: ['Fabric'] // Fabric private link group - } - } - ] - } -} - -/* -------------------------------------------- Private DNS Zones -------------------------------------------- */ - -// Format: 1) Private DNS Zone -// 2) Link Private DNS Zone to VNet -// 3) Create DNS Zone Group for Private Endpoint - -// Private DNS Zone for AI Services (Account) -// 1) Enables custom DNS resolution for AI Services private endpoint - -var aiServicesDnsZoneName = 'privatelink.services.ai.azure.com' -var openAiDnsZoneName = 'privatelink.openai.azure.com' -var cognitiveServicesDnsZoneName = 'privatelink.cognitiveservices.azure.com' -var aiSearchDnsZoneName = 'privatelink.search.windows.net' -var storageDnsZoneName = 'privatelink.blob.${environment().suffixes.storage}' -var cosmosDBDnsZoneName = 'privatelink.documents.azure.com' -var fabricDnsZoneName = 'privatelink.fabric.microsoft.com' - -// ---- DNS Zone Resource Group lookups ---- -var aiServicesDnsZoneRG = existingDnsZones[aiServicesDnsZoneName] -var openAiDnsZoneRG = existingDnsZones[openAiDnsZoneName] -var cognitiveServicesDnsZoneRG = existingDnsZones[cognitiveServicesDnsZoneName] -var aiSearchDnsZoneRG = existingDnsZones[aiSearchDnsZoneName] -var storageDnsZoneRG = existingDnsZones[storageDnsZoneName] -var cosmosDBDnsZoneRG = existingDnsZones[cosmosDBDnsZoneName] -var fabricDnsZoneRG = existingDnsZones.?fabricDnsZoneName ?? '' - -// ---- DNS Zone Resources and References ---- -resource aiServicesPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (empty(aiServicesDnsZoneRG)) { - name: aiServicesDnsZoneName - location: 'global' -} - -// Reference existing private DNS zone if provided -resource existingAiServicesPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (!empty(aiServicesDnsZoneRG)) { - name: aiServicesDnsZoneName - scope: resourceGroup(aiServicesDnsZoneRG) -} -//creating condition if user pass existing dns zones or not -var aiServicesDnsZoneId = empty(aiServicesDnsZoneRG) ? aiServicesPrivateDnsZone.id : existingAiServicesPrivateDnsZone.id - -resource openAiPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (empty(openAiDnsZoneRG)) { - name: openAiDnsZoneName - location: 'global' -} - -// Reference existing private DNS zone if provided -resource existingOpenAiPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (!empty(openAiDnsZoneRG)) { - name: openAiDnsZoneName - scope: resourceGroup(openAiDnsZoneRG) -} -//creating condition if user pass existing dns zones or not -var openAiDnsZoneId = empty(openAiDnsZoneRG) ? openAiPrivateDnsZone.id : existingOpenAiPrivateDnsZone.id - -resource cognitiveServicesPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (empty(cognitiveServicesDnsZoneRG)) { - name: cognitiveServicesDnsZoneName - location: 'global' -} - -// Reference existing private DNS zone if provided -resource existingCognitiveServicesPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (!empty(cognitiveServicesDnsZoneRG)) { - name: cognitiveServicesDnsZoneName - scope: resourceGroup(cognitiveServicesDnsZoneRG) -} -//creating condition if user pass existing dns zones or not -var cognitiveServicesDnsZoneId = empty(cognitiveServicesDnsZoneRG) - ? cognitiveServicesPrivateDnsZone.id - : existingCognitiveServicesPrivateDnsZone.id - -resource aiSearchPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (empty(aiSearchDnsZoneRG)) { - name: aiSearchDnsZoneName - location: 'global' -} - -// Reference existing private DNS zone if provided -resource existingAiSearchPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (!empty(aiSearchDnsZoneRG)) { - name: aiSearchDnsZoneName - scope: resourceGroup(aiSearchDnsZoneRG) -} -//creating condition if user pass existing dns zones or not -var aiSearchDnsZoneId = empty(aiSearchDnsZoneRG) ? aiSearchPrivateDnsZone.id : existingAiSearchPrivateDnsZone.id - -resource storagePrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (empty(storageDnsZoneRG)) { - name: storageDnsZoneName - location: 'global' -} - -// Reference existing private DNS zone if provided -resource existingStoragePrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (!empty(storageDnsZoneRG)) { - name: storageDnsZoneName - scope: resourceGroup(storageDnsZoneRG) -} -//creating condition if user pass existing dns zones or not -var storageDnsZoneId = empty(storageDnsZoneRG) ? storagePrivateDnsZone.id : existingStoragePrivateDnsZone.id - -resource cosmosDBPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (empty(cosmosDBDnsZoneRG)) { - name: cosmosDBDnsZoneName - location: 'global' -} - -// Reference existing private DNS zone if provided -resource existingCosmosDBPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (!empty(cosmosDBDnsZoneRG)) { - name: cosmosDBDnsZoneName - scope: resourceGroup(cosmosDBDnsZoneRG) -} -//creating condition if user pass existing dns zones or not -var cosmosDBDnsZoneId = empty(cosmosDBDnsZoneRG) ? cosmosDBPrivateDnsZone.id : existingCosmosDBPrivateDnsZone.id - -// Microsoft Fabric Private DNS Zone - only created if Fabric workspace is provided -resource fabricPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = if (fabricPassedIn && empty(fabricDnsZoneRG)) { - name: fabricDnsZoneName - location: 'global' -} - -// Reference existing Fabric private DNS zone if provided -resource existingFabricPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' existing = if (fabricPassedIn && !empty(fabricDnsZoneRG)) { - name: fabricDnsZoneName - scope: resourceGroup(fabricDnsZoneRG) -} -// Fabric DNS Zone ID - conditional based on whether Fabric is configured -var fabricDnsZoneId = fabricPassedIn - ? (empty(fabricDnsZoneRG) ? fabricPrivateDnsZone.id : existingFabricPrivateDnsZone.id) - : '' - -// ---- DNS VNet Links ---- -resource aiServicesLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = if (empty(aiServicesDnsZoneRG)) { - parent: aiServicesPrivateDnsZone - location: 'global' - name: 'aiServices-${suffix}-link' - properties: { - virtualNetwork: { id: vnet.id } - registrationEnabled: false - } -} -resource openAiLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = if (empty(openAiDnsZoneRG)) { - parent: openAiPrivateDnsZone - location: 'global' - name: 'aiServicesOpenAI-${suffix}-link' - properties: { - virtualNetwork: { id: vnet.id } - registrationEnabled: false - } -} -resource cognitiveServicesLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = if (empty(cognitiveServicesDnsZoneRG)) { - parent: cognitiveServicesPrivateDnsZone - location: 'global' - name: 'aiServicesCognitiveServices-${suffix}-link' - properties: { - virtualNetwork: { id: vnet.id } - registrationEnabled: false - } -} -resource aiSearchLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = if (empty(aiSearchDnsZoneRG)) { - parent: aiSearchPrivateDnsZone - location: 'global' - name: 'aiSearch-${suffix}-link' - properties: { - virtualNetwork: { id: vnet.id } - registrationEnabled: false - } -} -resource storageLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = if (empty(storageDnsZoneRG)) { - parent: storagePrivateDnsZone - location: 'global' - name: 'storage-${suffix}-link' - properties: { - virtualNetwork: { id: vnet.id } - registrationEnabled: false - } -} -resource cosmosDBLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = if (empty(cosmosDBDnsZoneRG)) { - parent: cosmosDBPrivateDnsZone - location: 'global' - name: 'cosmosDB-${suffix}-link' - properties: { - virtualNetwork: { id: vnet.id } - registrationEnabled: false - } -} - -// Fabric VNet Link - only created if Fabric workspace is provided -resource fabricLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = if (fabricPassedIn && empty(fabricDnsZoneRG)) { - parent: fabricPrivateDnsZone - location: 'global' - name: 'fabric-${suffix}-link' - properties: { - virtualNetwork: { id: vnet.id } - registrationEnabled: false - } -} - -// ---- DNS Zone Groups ---- -resource aiServicesDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { - parent: aiAccountPrivateEndpoint - name: '${aiAccountName}-dns-group' - properties: { - privateDnsZoneConfigs: [ - { name: '${aiAccountName}-dns-aiserv-config', properties: { privateDnsZoneId: aiServicesDnsZoneId } } - { name: '${aiAccountName}-dns-openai-config', properties: { privateDnsZoneId: openAiDnsZoneId } } - { name: '${aiAccountName}-dns-cogserv-config', properties: { privateDnsZoneId: cognitiveServicesDnsZoneId } } - ] - } - dependsOn: [ - empty(aiServicesDnsZoneRG) ? aiServicesLink : null - empty(openAiDnsZoneRG) ? openAiLink : null - empty(cognitiveServicesDnsZoneRG) ? cognitiveServicesLink : null - ] -} -resource aiSearchDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { - parent: aiSearchPrivateEndpoint - name: '${aiSearchName}-dns-group' - properties: { - privateDnsZoneConfigs: [ - { name: '${aiSearchName}-dns-config', properties: { privateDnsZoneId: aiSearchDnsZoneId } } - ] - } - dependsOn: [ - empty(aiSearchDnsZoneRG) ? aiSearchLink : null - ] -} -resource storageDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { - parent: storagePrivateEndpoint - name: '${storageName}-dns-group' - properties: { - privateDnsZoneConfigs: [ - { name: '${storageName}-dns-config', properties: { privateDnsZoneId: storageDnsZoneId } } - ] - } - dependsOn: [ - empty(storageDnsZoneRG) ? storageLink : null - ] -} -resource cosmosDBDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { - parent: cosmosDBPrivateEndpoint - name: '${cosmosDBName}-dns-group' - properties: { - privateDnsZoneConfigs: [ - { name: '${cosmosDBName}-dns-config', properties: { privateDnsZoneId: cosmosDBDnsZoneId } } - ] - } - dependsOn: [ - empty(cosmosDBDnsZoneRG) ? cosmosDBLink : null - ] -} - -// Fabric DNS Zone Group - only created if Fabric workspace is provided -resource fabricDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = if (fabricPassedIn) { - parent: fabricPrivateEndpoint - name: '${fabricWorkspaceName}-dns-group' - properties: { - privateDnsZoneConfigs: [ - { name: '${fabricWorkspaceName}-dns-config', properties: { privateDnsZoneId: fabricDnsZoneId } } - ] - } - dependsOn: [ - (fabricPassedIn && empty(fabricDnsZoneRG)) ? fabricLink : null - ] -} diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/standard-dependent-resources.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/standard-dependent-resources.bicep deleted file mode 100644 index c4c9fb657..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/standard-dependent-resources.bicep +++ /dev/null @@ -1,148 +0,0 @@ -// Creates Azure dependent resources for Azure AI Agent Service standard agent setup - -@description('Azure region of the deployment') -param location string - -// @description('The name of the Key Vault') -// param keyvaultName string - -@description('The name of the AI Search resource') -param aiSearchName string - -@description('Name of the storage account') -param azureStorageName string - -@description('Name of the new Cosmos DB account') -param cosmosDBName string - -@description('The AI Search Service full ARM Resource ID. This is an optional field, and if not provided, the resource will be created.') -param aiSearchResourceId string - -@description('The AI Storage Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created.') -param azureStorageAccountResourceId string - -@description('The Cosmos DB Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created.') -param cosmosDBResourceId string - -// param aiServiceExists bool -param aiSearchExists bool -param azureStorageExists bool -param cosmosDBExists bool - -var cosmosParts = split(cosmosDBResourceId, '/') - -resource existingCosmosDB 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' existing = if (cosmosDBExists) { - name: cosmosParts[8] - scope: resourceGroup(cosmosParts[2], cosmosParts[4]) -} - -// CosmosDB creation - -var canaryRegions = ['eastus2euap', 'centraluseuap'] -var cosmosDbRegion = contains(canaryRegions, location) ? 'westus' : location -resource cosmosDB 'Microsoft.DocumentDB/databaseAccounts@2024-11-15' = if(!cosmosDBExists) { - name: cosmosDBName - location: cosmosDbRegion - kind: 'GlobalDocumentDB' - properties: { - consistencyPolicy: { - defaultConsistencyLevel: 'Session' - } - disableLocalAuth: true - enableAutomaticFailover: false - enableMultipleWriteLocations: false - publicNetworkAccess: 'Disabled' - enableFreeTier: false - locations: [ - { - locationName: location - failoverPriority: 0 - isZoneRedundant: false - } - ] - databaseAccountOfferType: 'Standard' - } -} - -var acsParts = split(aiSearchResourceId, '/') - -resource existingSearchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = if (aiSearchExists) { - name: acsParts[8] - scope: resourceGroup(acsParts[2], acsParts[4]) -} - -// AI Search creation - -resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = if(!aiSearchExists) { - name: aiSearchName - location: location - identity: { - type: 'SystemAssigned' - } - properties: { - disableLocalAuth: false - authOptions: { aadOrApiKey: { aadAuthFailureMode: 'http401WithBearerChallenge'}} - encryptionWithCmk: { - enforcement: 'Unspecified' - } - hostingMode: 'default' - partitionCount: 1 - publicNetworkAccess: 'disabled' - replicaCount: 1 - semanticSearch: 'disabled' - networkRuleSet: { - bypass: 'None' - ipRules: [] - } - } - sku: { - name: 'standard' - } -} - -var azureStorageParts = split(azureStorageAccountResourceId, '/') - -resource existingAzureStorageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = if (azureStorageExists) { - name: azureStorageParts[8] - scope: resourceGroup(azureStorageParts[2], azureStorageParts[4]) -} - -// Some regions doesn't support Standard Zone-Redundant storage, need to use Geo-redundant storage -param noZRSRegions array = ['southindia', 'westus'] -param sku object = contains(noZRSRegions, location) ? { name: 'Standard_GRS' } : { name: 'Standard_ZRS' } - -// Storage creation - -resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = if(!azureStorageExists) { - name: azureStorageName - location: location - kind: 'StorageV2' - sku: sku - properties: { - minimumTlsVersion: 'TLS1_2' - allowBlobPublicAccess: false - publicNetworkAccess: 'Disabled' - networkAcls: { - bypass: 'AzureServices' - defaultAction: 'Deny' - virtualNetworkRules: [] - } - allowSharedKeyAccess: false - } -} - -output aiSearchName string = aiSearchExists ? existingSearchService.name : aiSearch.name -output aiSearchID string = aiSearchExists ? existingSearchService.id : aiSearch.id -output aiSearchServiceResourceGroupName string = aiSearchExists ? acsParts[4] : resourceGroup().name -output aiSearchServiceSubscriptionId string = aiSearchExists ? acsParts[2] : subscription().subscriptionId - -output azureStorageName string = azureStorageExists ? existingAzureStorageAccount.name : storage.name -output azureStorageId string = azureStorageExists ? existingAzureStorageAccount.id : storage.id -output azureStorageResourceGroupName string = azureStorageExists ? azureStorageParts[4] : resourceGroup().name -output azureStorageSubscriptionId string = azureStorageExists ? azureStorageParts[2] : subscription().subscriptionId - -output cosmosDBName string = cosmosDBExists ? existingCosmosDB.name : cosmosDB.name -output cosmosDBId string = cosmosDBExists ? existingCosmosDB.id : cosmosDB.id -output cosmosDBResourceGroupName string = cosmosDBExists ? cosmosParts[4] : resourceGroup().name -output cosmosDBSubscriptionId string = cosmosDBExists ? cosmosParts[2] : subscription().subscriptionId -// output keyvaultId string = keyVault.id diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/subnet.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/subnet.bicep deleted file mode 100644 index bf81553d8..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/subnet.bicep +++ /dev/null @@ -1,22 +0,0 @@ -@description('Name of the virtual network') -param vnetName string - -@description('Name of the subnet') -param subnetName string - -@description('Address prefix for the subnet') -param addressPrefix string - -@description('Array of subnet delegations') -param delegations array = [] - -resource subnet 'Microsoft.Network/virtualNetworks/subnets@2024-05-01' = { - name: '${vnetName}/${subnetName}' - properties: { - addressPrefix: addressPrefix - delegations: delegations - } -} - -output subnetId string = subnet.id -output subnetName string = subnetName diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/validate-existing-resources.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/validate-existing-resources.bicep deleted file mode 100644 index f798e5e27..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/validate-existing-resources.bicep +++ /dev/null @@ -1,91 +0,0 @@ -// @description('Resource ID of the AI Service Account. ') -// param aiServiceAccountResourceId string - -@description('Resource ID of the AI Search Service.') -param aiSearchResourceId string - -@description('Resource ID of the Azure Storage Account.') -param azureStorageAccountResourceId string - -@description('ResourceId of Cosmos DB Account') -param azureCosmosDBAccountResourceId string - -// Check if existing resources have been passed in -var storagePassedIn = azureStorageAccountResourceId != '' -var searchPassedIn = aiSearchResourceId != '' -var cosmosPassedIn = azureCosmosDBAccountResourceId != '' - -var storageParts = split(azureStorageAccountResourceId, '/') -var azureStorageSubscriptionId = storagePassedIn && length(storageParts) > 2 ? storageParts[2] : subscription().subscriptionId -var azureStorageResourceGroupName = storagePassedIn && length(storageParts) > 4 ? storageParts[4] : resourceGroup().name - -var acsParts = split(aiSearchResourceId, '/') -var aiSearchServiceSubscriptionId = searchPassedIn && length(acsParts) > 2 ? acsParts[2] : subscription().subscriptionId -var aiSearchServiceResourceGroupName = searchPassedIn && length(acsParts) > 4 ? acsParts[4] : resourceGroup().name - -var cosmosParts = split(azureCosmosDBAccountResourceId, '/') -var cosmosDBSubscriptionId = cosmosPassedIn && length(cosmosParts) > 2 ? cosmosParts[2] : subscription().subscriptionId -var cosmosDBResourceGroupName = cosmosPassedIn && length(cosmosParts) > 4 ? cosmosParts[4] : resourceGroup().name - -// Validate AI Search -resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' existing = if (searchPassedIn) { - name: last(split(aiSearchResourceId, '/')) - scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) -} - -// Validate Cosmos DB Account -resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = if (cosmosPassedIn) { - name: last(split(azureCosmosDBAccountResourceId, '/')) - scope: resourceGroup(cosmosDBSubscriptionId,cosmosDBResourceGroupName) -} - -// Validate Storage Account -resource azureStorageAccount 'Microsoft.Storage/storageAccounts@2024-01-01' existing = if (storagePassedIn) { - name: last(split(azureStorageAccountResourceId, '/')) - scope: resourceGroup(azureStorageSubscriptionId,azureStorageResourceGroupName) -} - -// output aiServiceExists bool = aiServicesPassedIn && (aiServiceAccount.name == aiServiceParts[8]) -output aiSearchExists bool = searchPassedIn && (aiSearch.name == acsParts[8]) -output cosmosDBExists bool = cosmosPassedIn && (cosmosDBAccount.name == cosmosParts[8]) -output azureStorageExists bool = storagePassedIn && (azureStorageAccount.name == storageParts[8]) - -output aiSearchServiceSubscriptionId string = aiSearchServiceSubscriptionId -output aiSearchServiceResourceGroupName string = aiSearchServiceResourceGroupName - -output cosmosDBSubscriptionId string = cosmosDBSubscriptionId -output cosmosDBResourceGroupName string = cosmosDBResourceGroupName - -output azureStorageSubscriptionId string = azureStorageSubscriptionId -output azureStorageResourceGroupName string = azureStorageResourceGroupName - -// Adding DNS Zone Check - -@description('Object mapping DNS zone names to their resource group, or empty string to indicate creation') -param existingDnsZones object - -@description('List of private DNS zone names to validate') -param dnsZoneNames array - -var dnsZoneTypes = [ - 'Microsoft.Network/privateDnsZones' -] - -// Output whether each DNS zone exists -output dnsZoneExists array = [ - for zoneName in dnsZoneNames: { - name: zoneName - exists: !empty(existingDnsZones[zoneName]) - } -] - -/* -// Helper function to check existence -function resourceExists(resourceType: string, name: string, rg: string): bool { - // Use the existing resource reference to check - var res = existing resource dnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { - name: name - scope: resourceGroup(rg) - } - return !empty(res.id) -}*/ diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/vnet.bicep b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/vnet.bicep deleted file mode 100644 index 8d013eb3c..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/modules-network-secured/vnet.bicep +++ /dev/null @@ -1,107 +0,0 @@ -/* -Virtual Network Module -This module deploys the core network infrastructure with security controls: - -1. Address Space: - - VNet CIDR: 172.16.0.0/16 OR 192.168.0.0/16 - - Agents Subnet: 172.16.0.0/24 OR 192.168.0.0/24 (reserved for Azure AI Foundry) - - Private Endpoint Subnet: 172.16.1.0/24 OR 192.168.1.0/24 - - MCP Subnet: 172.16.2.0/24 OR 192.168.2.0/24 (for user Container Apps) - -2. Security Features: - - Network isolation - - Subnet delegation - - Private endpoint subnet -*/ - -@description('Azure region for the deployment') -param location string - -@description('The name of the virtual network') -param vnetName string = 'agents-vnet-test' - -@description('The name of Agents Subnet') -param agentSubnetName string = 'agent-subnet' - -@description('The name of Hub subnet') -param peSubnetName string = 'pe-subnet' - -@description('The name of MCP subnet for user-deployed Container Apps') -param mcpSubnetName string = 'mcp-subnet' - -@description('Address space for the VNet') -param vnetAddressPrefix string = '' - -@description('Address prefix for the agent subnet') -param agentSubnetPrefix string = '' - -@description('Address prefix for the private endpoint subnet') -param peSubnetPrefix string = '' - -@description('Address prefix for the MCP subnet') -param mcpSubnetPrefix string = '' - -var defaultVnetAddressPrefix = '192.168.0.0/16' -var vnetAddress = empty(vnetAddressPrefix) ? defaultVnetAddressPrefix : vnetAddressPrefix -var agentSubnet = empty(agentSubnetPrefix) ? cidrSubnet(vnetAddress, 24, 0) : agentSubnetPrefix -var peSubnet = empty(peSubnetPrefix) ? cidrSubnet(vnetAddress, 24, 1) : peSubnetPrefix -var mcpSubnet = empty(mcpSubnetPrefix) ? cidrSubnet(vnetAddress, 24, 2) : mcpSubnetPrefix - -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { - name: vnetName - location: location - properties: { - addressSpace: { - addressPrefixes: [ - vnetAddress - ] - } - subnets: [ - { - name: agentSubnetName - properties: { - addressPrefix: agentSubnet - delegations: [ - { - name: 'Microsoft.app/environments' - properties: { - serviceName: 'Microsoft.App/environments' - } - } - ] - } - } - { - name: peSubnetName - properties: { - addressPrefix: peSubnet - } - } - { - name: mcpSubnetName - properties: { - addressPrefix: mcpSubnet - delegations: [ - { - name: 'Microsoft.App/environments' - properties: { - serviceName: 'Microsoft.App/environments' - } - } - ] - } - } - ] - } -} -// Output variables -output peSubnetName string = peSubnetName -output agentSubnetName string = agentSubnetName -output mcpSubnetName string = mcpSubnetName -output agentSubnetId string = '${virtualNetwork.id}/subnets/${agentSubnetName}' -output peSubnetId string = '${virtualNetwork.id}/subnets/${peSubnetName}' -output mcpSubnetId string = '${virtualNetwork.id}/subnets/${mcpSubnetName}' -output virtualNetworkName string = virtualNetwork.name -output virtualNetworkId string = virtualNetwork.id -output virtualNetworkResourceGroup string = resourceGroup().name -output virtualNetworkSubscriptionId string = subscription().subscriptionId diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/TESTING-GUIDE.md b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/TESTING-GUIDE.md deleted file mode 100644 index 1209075c6..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/TESTING-GUIDE.md +++ /dev/null @@ -1,462 +0,0 @@ -# Hybrid Private Resources - Testing Guide - -This guide covers testing Azure AI Foundry agents with tools that access private resources (AI Search, MCP servers). By default, the Foundry (AI Services) resource has **public network access disabled**. You can optionally [switch to public access](#switching-the-foundry-resource-to-public-access) for easier development. - -> **Private Foundry (default):** You need a secure connection (VPN Gateway, ExpressRoute, or Azure Bastion) to reach the Foundry resource and run SDK tests. See [Connecting to a Private Foundry Resource](#connecting-to-a-private-foundry-resource). - ---- - -## Table of Contents - -1. [Prerequisites](#prerequisites) -2. [Connecting to a Private Foundry Resource](#connecting-to-a-private-foundry-resource) -3. [Switching the Foundry Resource to Public Access](#switching-the-foundry-resource-to-public-access) -4. [Step 1: Deploy the Template](#step-1-deploy-the-template) -5. [Step 2: Verify Private Endpoints](#step-2-verify-private-endpoints) -6. [Step 3: Create Test Data in AI Search](#step-3-create-test-data-in-ai-search) -7. [Step 4: Deploy MCP Server](#step-4-deploy-mcp-server) -8. [Step 5: Test via SDK](#step-5-test-via-sdk) -9. [Troubleshooting](#troubleshooting) -10. [Test Results Summary](#test-results-summary) - ---- - -## Prerequisites - -- Azure CLI installed and authenticated -- Owner or Contributor role on the subscription -- Python 3.10+ (for SDK testing) - ---- - -## Connecting to a Private Foundry Resource - -When the Foundry resource has public network access **disabled** (the default), you must connect to the Azure VNet before you can reach the Foundry endpoint for SDK testing or portal access. - -Azure provides three methods: - -| Method | Use Case | -|--------|----------| -| **Azure VPN Gateway** | Connect from your local machine/network over an encrypted tunnel | -| **Azure ExpressRoute** | Private, dedicated connection from on-premises infrastructure | -| **Azure Bastion** | Access a jump box VM on the VNet securely through the Azure portal | - -For step-by-step setup instructions, see: [Securely connect to Azure AI Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/configure-private-link?view=foundry#securely-connect-to-foundry). - -Once connected to the VNet, all SDK commands and portal interactions in this guide will work as documented. - ---- - -## Switching the Foundry Resource to Public Access - -If your security policy permits, you can enable public network access on the Foundry resource so that SDK tests and portal access work directly from the internet without VPN/ExpressRoute/Bastion. - -In `modules-network-secured/ai-account-identity.bicep`, change: - -```bicep -// Change from: -publicNetworkAccess: 'Disabled' -// To: -publicNetworkAccess: 'Enabled' - -// Also change: -defaultAction: 'Deny' -// To: -defaultAction: 'Allow' -``` - -Then redeploy the template. Backend resources (AI Search, Cosmos DB, Storage) remain on private endpoints regardless of this setting. - -To revert to private, set `publicNetworkAccess: 'Disabled'` and `defaultAction: 'Deny'`, then redeploy. - ---- - -## Step 1: Deploy the Template - -```bash -# Set variables -RESOURCE_GROUP="rg-hybrid-agent-test" -LOCATION="westus2" - -# Create resource group -az group create --name $RESOURCE_GROUP --location $LOCATION - -# Deploy the template -az deployment group create \ - --resource-group $RESOURCE_GROUP \ - --template-file main.bicep \ - --parameters location=$LOCATION - -# Get the deployment outputs -AI_SERVICES_NAME=$(az cognitiveservices account list -g $RESOURCE_GROUP --query "[0].name" -o tsv) -echo "AI Services: $AI_SERVICES_NAME" -``` - ---- - -## Step 2: Verify Private Endpoints - -Confirm that backend resources have private endpoints: - -```bash -# List private endpoints -az network private-endpoint list -g $RESOURCE_GROUP -o table - -# Expected: Private endpoints for: -# - AI Search (*search-private-endpoint) -# - Cosmos DB (*cosmosdb-private-endpoint) -# - Storage (*storage-private-endpoint) -# - AI Services (*-private-endpoint) - -# If public access is ENABLED, verify AI Services is publicly accessible: -AI_ENDPOINT=$(az cognitiveservices account show -g $RESOURCE_GROUP -n $AI_SERVICES_NAME --query "properties.endpoint" -o tsv) -curl -I $AI_ENDPOINT -# Should return HTTP 200 (accessible from internet) - -# If public access is DISABLED (default), the curl above will fail. -# You must connect via VPN/ExpressRoute/Bastion to reach the endpoint. -# See: Connecting to a Private Foundry Resource -``` - ---- - -## Step 3: Create Test Data in AI Search - -Since AI Search has a private endpoint, you need to access it from within the VNet or temporarily allow public access. - -### Option A: Temporarily Enable Public Access on AI Search - -```bash -AI_SEARCH_NAME=$(az search service list -g $RESOURCE_GROUP --query "[0].name" -o tsv) - -# Temporarily enable public access -az search service update -g $RESOURCE_GROUP -n $AI_SEARCH_NAME \ - --public-network-access enabled - -# Get admin key -ADMIN_KEY=$(az search admin-key show -g $RESOURCE_GROUP --service-name $AI_SEARCH_NAME --query "primaryKey" -o tsv) - -# Create test index -curl -X POST "https://${AI_SEARCH_NAME}.search.windows.net/indexes?api-version=2023-11-01" \ - -H "Content-Type: application/json" \ - -H "api-key: ${ADMIN_KEY}" \ - -d '{ - "name": "test-index", - "fields": [ - {"name": "id", "type": "Edm.String", "key": true}, - {"name": "content", "type": "Edm.String", "searchable": true} - ] - }' - -# Add a test document -curl -X POST "https://${AI_SEARCH_NAME}.search.windows.net/indexes/test-index/docs/index?api-version=2023-11-01" \ - -H "Content-Type: application/json" \ - -H "api-key: ${ADMIN_KEY}" \ - -d '{ - "value": [ - {"@search.action": "upload", "id": "1", "content": "This is a test document for validating AI Search integration with Azure AI Foundry agents."} - ] - }' - -# Disable public access again -az search service update -g $RESOURCE_GROUP -n $AI_SEARCH_NAME \ - --public-network-access disabled -``` - ---- - -## Step 4: Deploy MCP Server - -Deploy an HTTP-based MCP server using the pre-built multi-auth MCP image. - -> **Important**: Azure AI Agents require MCP servers that implement the **Streamable HTTP transport** (JSON-RPC over HTTP with session management). The multi-auth MCP server provides this with a `/noauth/mcp` endpoint for testing. - -### 4.1 Import the Multi-Auth MCP Image - -```bash -# Create ACR if needed -ACR_NAME="mcpacr$(date +%s | tail -c 5)" -az acr create --name $ACR_NAME --resource-group $RESOURCE_GROUP --sku Basic --location $LOCATION - -# Import the pre-built multi-auth MCP image -az acr import \ - --name $ACR_NAME \ - --source retrievaltestacr.azurecr.io/multi-auth-mcp/api-multi-auth-mcp-env:latest \ - --image multi-auth-mcp:latest - -# Create user-assigned identity with AcrPull role -az identity create --name mcp-identity --resource-group $RESOURCE_GROUP --location $LOCATION -IDENTITY_ID=$(az identity show --name mcp-identity -g $RESOURCE_GROUP --query "id" -o tsv) -IDENTITY_PRINCIPAL=$(az identity show --name mcp-identity -g $RESOURCE_GROUP --query "principalId" -o tsv) -ACR_ID=$(az acr show --name $ACR_NAME --query "id" -o tsv) -az role assignment create --assignee $IDENTITY_PRINCIPAL --role AcrPull --scope $ACR_ID - -# Wait for role assignment to propagate -sleep 30 -``` - -### 4.2 Create Container Apps Environment - -```bash -VNET_NAME=$(az network vnet list -g $RESOURCE_GROUP --query "[0].name" -o tsv) -MCP_SUBNET_ID=$(az network vnet subnet show -g $RESOURCE_GROUP --vnet-name $VNET_NAME -n "mcp-subnet" --query "id" -o tsv) - -# Create internal Container Apps environment -az containerapp env create \ - --resource-group $RESOURCE_GROUP \ - --name "mcp-env" \ - --location $LOCATION \ - --infrastructure-subnet-resource-id $MCP_SUBNET_ID \ - --internal-only true -``` - -### 4.3 Deploy the MCP Server - -```bash -# Deploy container app with multi-auth MCP image -# Note: The image runs on port 8080 -az containerapp create \ - --resource-group $RESOURCE_GROUP \ - --name "mcp-http-server" \ - --environment "mcp-env" \ - --image "${ACR_NAME}.azurecr.io/multi-auth-mcp:latest" \ - --target-port 8080 \ - --ingress external \ - --min-replicas 1 \ - --user-assigned $IDENTITY_ID \ - --registry-server "${ACR_NAME}.azurecr.io" \ - --registry-identity $IDENTITY_ID - -# Get the MCP server URL -MCP_FQDN=$(az containerapp show -g $RESOURCE_GROUP -n "mcp-http-server" --query "properties.configuration.ingress.fqdn" -o tsv) -echo "MCP Server URL: https://${MCP_FQDN}/noauth/mcp" -``` - -### 4.4 Configure Private DNS - -```bash -MCP_STATIC_IP=$(az containerapp env show -g $RESOURCE_GROUP -n "mcp-env" --query "properties.staticIp" -o tsv) -DEFAULT_DOMAIN=$(az containerapp env show -g $RESOURCE_GROUP -n "mcp-env" --query "properties.defaultDomain" -o tsv) - -# Create private DNS zone -az network private-dns zone create -g $RESOURCE_GROUP -n $DEFAULT_DOMAIN - -# Link to VNet -VNET_ID=$(az network vnet show -g $RESOURCE_GROUP -n $VNET_NAME --query "id" -o tsv) -az network private-dns link vnet create \ - -g $RESOURCE_GROUP \ - -z $DEFAULT_DOMAIN \ - -n "containerapp-link" \ - -v $VNET_ID \ - --registration-enabled false - -# Add wildcard A record -az network private-dns record-set a add-record -g $RESOURCE_GROUP -z $DEFAULT_DOMAIN -n "*" -a $MCP_STATIC_IP -``` - -### 4.5 (Optional) Deploy Public MCP Server for Testing - -For easier testing without VNet constraints, you can also deploy a public MCP server: - -```bash -# Create public Container Apps environment -az containerapp env create \ - --resource-group $RESOURCE_GROUP \ - --name "mcp-env-public" \ - --location $LOCATION - -# Deploy public MCP server -az containerapp create \ - --resource-group $RESOURCE_GROUP \ - --name "mcp-http-server-public" \ - --environment "mcp-env-public" \ - --image "${ACR_NAME}.azurecr.io/multi-auth-mcp:latest" \ - --target-port 8080 \ - --ingress external \ - --min-replicas 1 \ - --user-assigned $IDENTITY_ID \ - --registry-server "${ACR_NAME}.azurecr.io" \ - --registry-identity $IDENTITY_ID - -# Get public MCP URL -PUBLIC_MCP_FQDN=$(az containerapp show -g $RESOURCE_GROUP -n "mcp-http-server-public" --query "properties.configuration.ingress.fqdn" -o tsv) -echo "Public MCP Server URL: https://${PUBLIC_MCP_FQDN}/noauth/mcp" -``` - ---- - -## Step 5: Test via SDK - -Two test scripts are provided: - -| Script | Description | -|--------|-------------| -| `test_agents_v2.py` | Full test suite: basic agent, AI Search, MCP tools | -| `test_mcp_tools_agents_v2.py` | Focused MCP testing: connectivity + public/private agent tests | - -### 5.1 Install Dependencies - -```bash -pip install azure-ai-projects azure-identity openai -``` - -### 5.2 Configure Environment - -```bash -# Set the project endpoint (get from Azure Portal -> AI Services -> Projects -> Properties) -export PROJECT_ENDPOINT="https://.services.ai.azure.com/api/projects/" - -# Optional: Override MCP server URLs -export MCP_SERVER_PUBLIC="https:///noauth/mcp" -export MCP_SERVER_PRIVATE="https:///noauth/mcp" -``` - -### 5.3 Run Full Test Suite - -```bash -# Run all tests (basic agent, AI Search, MCP) -python test_agents_v2.py - -# Run specific test -python test_agents_v2.py --test basic_agent -python test_agents_v2.py --test ai_search -python test_agents_v2.py --test mcp_tool -``` - -### 5.4 Run MCP-Focused Tests - -```bash -# Run all MCP tests (connectivity + public + private) -python test_mcp_tools_agents_v2.py - -# Test only public MCP server -python test_mcp_tools_agents_v2.py --test public - -# Test only private MCP server -python test_mcp_tools_agents_v2.py --test private - -# With retries (useful for transient Hyena cluster routing issues) -python test_mcp_tools_agents_v2.py --test public --retry 3 -``` - -### 5.5 Understanding Test Results - -**MCP Connectivity Test**: Direct HTTP test to verify the MCP server responds correctly: -- Sends `initialize` request and captures `mcp-session-id` header -- Sends `tools/list` to enumerate available tools -- Sends `tools/call` to execute the `add` tool - -**MCP Tool via Agent Test**: Tests the full agent workflow: -- Creates an agent with MCP tool configuration -- Sends a request that triggers the MCP tool -- Validates the agent can call MCP tools through the Data Proxy - -> **Known Issue**: Agent tests may fail ~50% of the time with `TaskCanceledException` due to Hyena cluster routing. The Data Proxy is only deployed on one of two scale units, and the load balancer routes in round-robin fashion. Use `--retry` to mitigate. - ---- - -## Troubleshooting - -### Agent Can't Access AI Search - -1. **Verify private endpoint exists**: - ```bash - az network private-endpoint list -g $RESOURCE_GROUP --query "[?contains(name,'search')]" - ``` - -2. **Check Data Proxy configuration**: - ```bash - az cognitiveservices account show -g $RESOURCE_GROUP -n $AI_SERVICES_NAME \ - --query "properties.networkInjections" - ``` - -3. **Verify AI Search connection in project**: - - Go to the portal → Project → Settings → Connections - - Confirm AI Search connection exists - -### MCP Tool Fails with TaskCanceledException - -This is a **known issue** with the Hyena cluster infrastructure: -- The Data Proxy is deployed on only **one of two scale units** -- The load balancer routes requests in **round-robin** fashion -- ~50% of requests hit the wrong scale unit and get `TaskCanceledException` - -**Workaround**: Use `--retry` flag when running tests: -```bash -python test_mcp_tools_agents_v2.py --test public --retry 3 -``` - -### MCP Tool Fails with 400 Bad Request - -Check the error message for details: -- **404 Not Found**: Verify the MCP server URL includes the correct path (`/noauth/mcp`) -- **DNS resolution**: Ensure private DNS zone is configured correctly for Container Apps - -### MCP Server Not Responding - -1. **Check container app health**: - ```bash - az containerapp show -g $RESOURCE_GROUP -n "mcp-http-server" --query "properties.runningStatus" - ``` - -2. **Check container logs**: - ```bash - az containerapp logs show -g $RESOURCE_GROUP -n "mcp-http-server" --tail 50 - ``` - -3. **Verify ingress port is 8080** (not 80): - ```bash - az containerapp ingress show -g $RESOURCE_GROUP -n "mcp-http-server" --query "targetPort" - ``` - -### Portal Shows "New Foundry Not Supported" - -This is expected when network injection is configured. Use SDK testing instead - it works perfectly with network injection. - ---- - -## Test Results Summary - -### Test Scripts - -| Script | Purpose | -|--------|---------| -| `test_agents_v2.py` | Full test suite: OpenAI API, basic agent, AI Search, MCP | -| `test_mcp_tools_agents_v2.py` | Focused MCP testing with retry support | - -### Validated ✅ - -| Test | Status | Notes | -|------|--------|-------| -| OpenAI Responses API (direct) | ✅ Pass | Works from anywhere | -| Basic Agent (no tools) | ✅ Pass | Works from anywhere | -| AI Search Tool | ✅ Pass | Data Proxy routes to private endpoint | -| MCP Connectivity (direct HTTP) | ✅ Pass | Server responds correctly | -| MCP Tool via Agent (public server) | ✅ Pass* | *~50% fail rate due to Hyena routing | - -### Known Limitations ⚠️ - -| Issue | Cause | Workaround | -|-------|-------|------------| -| ~50% TaskCanceledException | Hyena cluster has 2 scale units, Data Proxy only on 1 | Use `--retry` flag | -| Portal "New Foundry" blocked | Network injection not supported in portal | Use SDK testing | -| Private MCP via Data Proxy | DNS resolution issues for Container Apps | Use public MCP server | - -### Architecture Notes - -1. **AI Search Tool works** because it uses Azure Private Endpoints with built-in DNS integration (`privatelink.search.windows.net`). - -2. **MCP uses Streamable HTTP transport** - The multi-auth MCP server implements proper session management with `mcp-session-id` headers required by Azure's MCP client. - -3. **Container Apps require port 8080** - The multi-auth MCP image runs on port 8080, not 80. - -4. **Use `/noauth/mcp` endpoint** for testing without authentication. Production deployments should use `/mcp` with proper auth configuration. - ---- - -## Cleanup - -```bash -# Delete all resources -az group delete --name $RESOURCE_GROUP --yes --no-wait -``` diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_agents_v2.py b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_agents_v2.py deleted file mode 100644 index 711246355..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_agents_v2.py +++ /dev/null @@ -1,646 +0,0 @@ -#!/usr/bin/env python3 -""" -Hybrid Private Resources - Agents v2 Test Script - -This script tests that agents can use tools that connect to private resources -via the Data Proxy when AI Services has PUBLIC access enabled. - -Template 19: AI Services (public) → Data Proxy → Private Resources (VNet) - -Key tests: -1. Basic agent - validates public API access works using Responses API -2. AI Search tool - validates Data Proxy routes to private AI Search -3. MCP tool - validates Data Proxy routes to private MCP server - -This script can be run from ANYWHERE (no jump box required for API access). -However, MCP connectivity test requires access to the private VNet. - -Uses the new Agents v2 SDK pattern: -- AIProjectClient with context manager -- project_client.get_openai_client() for OpenAI-compatible API -- openai_client.responses.create() for the Responses API -- project_client.agents.create_version() with PromptAgentDefinition -- openai_client.conversations.create() for conversation threads -""" - -import os -import sys -import logging - -# ============================================================================ -# LOGGING CONFIGURATION - Enable HTTP request/response logging for debugging -# ============================================================================ -# Set to logging.DEBUG for full request/response bodies, INFO for headers only -LOG_LEVEL = logging.INFO - -# Configure basic logging format -logging.basicConfig( - level=LOG_LEVEL, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) - -# Azure SDK HTTP logging (captures request IDs, headers, URLs) -logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(LOG_LEVEL) - -# OpenAI client uses httpx for HTTP requests -logging.getLogger("httpx").setLevel(LOG_LEVEL) - -# Optional: urllib3 for lower-level HTTP debugging -logging.getLogger("urllib3").setLevel(logging.WARNING) # Set to DEBUG for full details - -# Reduce noise from other loggers -logging.getLogger("azure.identity").setLevel(logging.WARNING) - -# ============================================================================ - -from azure.ai.projects import AIProjectClient -from azure.ai.projects.models import ( - MCPTool, - PromptAgentDefinition, - AzureAISearchAgentTool, - AzureAISearchToolResource, - AISearchIndexResource, - AzureAISearchQueryType, -) -from azure.identity import DefaultAzureCredential -from openai.types.responses import ResponseInputParam -from openai.types.responses.response_input_param import McpApprovalResponse - -# ============================================================================ -# CONFIGURATION - Update these values for your deployment -# ============================================================================ -# NOTE: Use the project-scoped endpoint from Azure Portal: -# AI Services resource -> Projects -> -> Properties -> "AI Foundry API" endpoint -PROJECT_ENDPOINT = os.environ.get( - "PROJECT_ENDPOINT", - "https://aiservicesaxy3.services.ai.azure.com/api/projects/projectaxy3" -) -MODEL_NAME = os.environ.get("MODEL_NAME", "gpt-4o-mini") - -# AI Search configuration -# AI_SEARCH_CONNECTION_NAME = os.environ.get("AI_SEARCH_CONNECTION_NAME", "") -AI_SEARCH_CONNECTION_NAME = "aiservicesaxy3search" -AI_SEARCH_INDEX_NAME = os.environ.get("AI_SEARCH_INDEX_NAME", "test-index") - -# MCP Server configuration - Using multi-auth MCP image deployed to your Container Apps -# Private (internal to VNet): mcp-http-server.jollydune-20a0f709.westus2.azurecontainerapps.io -# Public (external): mcp-http-server-public.victoriousfield-89c08f4e.westus2.azurecontainerapps.io -MCP_SERVER_URL = os.environ.get( - "MCP_SERVER_URL", - "https://mcp-http-server-public.victoriousfield-89c08f4e.westus2.azurecontainerapps.io/noauth/mcp" -) - -# ============================================================================ - - -def log_response_info(response, label="Response"): - """Extract and log useful debugging info from OpenAI response objects.""" - logger = logging.getLogger(__name__) - try: - # Try to get request ID from response - if hasattr(response, '_request_id'): - logger.info(f"{label} - Request ID: {response._request_id}") - if hasattr(response, 'id'): - logger.info(f"{label} - Response ID: {response.id}") - # For openai responses, the request_id is often in headers - if hasattr(response, '_response') and hasattr(response._response, 'headers'): - headers = response._response.headers - if 'x-request-id' in headers: - logger.info(f"{label} - x-request-id: {headers['x-request-id']}") - if 'x-ms-request-id' in headers: - logger.info(f"{label} - x-ms-request-id: {headers['x-ms-request-id']}") - except Exception as e: - logger.debug(f"Could not extract response info: {e}") - - -def log_exception_info(exception, label="Exception"): - """Extract and log request info from OpenAI exceptions for debugging failed requests.""" - logger = logging.getLogger(__name__) - try: - # OpenAI exceptions have a response attribute with the HTTP response - if hasattr(exception, 'response') and exception.response is not None: - resp = exception.response - headers = resp.headers if hasattr(resp, 'headers') else {} - - # Log common request identifiers - request_id = headers.get('x-request-id', 'N/A') - ms_request_id = headers.get('x-ms-request-id', 'N/A') - - logger.error(f"{label} - x-request-id: {request_id}") - logger.error(f"{label} - x-ms-request-id: {ms_request_id}") - - # Also print to console for visibility - print(f" 📋 Request ID (x-request-id): {request_id}") - print(f" 📋 MS Request ID (x-ms-request-id): {ms_request_id}") - - # Log status code - if hasattr(resp, 'status_code'): - logger.error(f"{label} - HTTP Status: {resp.status_code}") - - # Also try to get request_id attribute directly - if hasattr(exception, 'request_id'): - logger.error(f"{label} - request_id attribute: {exception.request_id}") - print(f" 📋 Request ID: {exception.request_id}") - - except Exception as e: - logger.debug(f"Could not extract exception info: {e}") - - -def test_mcp_server_connectivity(): - """Test MCP server with full session workflow: initialize → list tools → call tool.""" - print("\n" + "=" * 60) - print("TEST 1: MCP Server Connectivity (Full Session Flow)") - print("=" * 60) - - import urllib.request - import ssl - import json - - try: - # Create SSL context - ctx = ssl.create_default_context() - - print(f" Target MCP Server: {MCP_SERVER_URL}") - - # ===================================================================== - # Step 1: Initialize - Get mcp-session-id - # ===================================================================== - print("\n--- Step 1: Initialize (get mcp-session-id) ---") - - init_data = json.dumps({ - "method": "initialize", - "params": { - "protocolVersion": "2025-11-25", - "capabilities": { - "sampling": {}, - "elicitation": {}, - "roots": { - "listChanged": True - } - }, - "clientInfo": { - "name": "test-mcp-client", - "version": "1.0.0" - } - }, - "jsonrpc": "2.0", - "id": 0 - }).encode('utf-8') - - init_req = urllib.request.Request( - MCP_SERVER_URL, - data=init_data, - headers={ - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream" - }, - method="POST" - ) - - with urllib.request.urlopen(init_req, timeout=10, context=ctx) as response: - status = response.getcode() - body = response.read().decode('utf-8') - mcp_session_id = response.getheader('mcp-session-id') - - print(f" ✓ HTTP Status: {status}") - print(f" ✓ Response: {body[:300]}...") - - if mcp_session_id: - print(f" ✓ MCP Session ID: {mcp_session_id}") - else: - print(" ✗ No mcp-session-id header in response!") - print("\n✗ TEST FAILED: MCP server did not return session ID") - return False - - # ===================================================================== - # Step 2: List Tools - Using mcp-session-id - # ===================================================================== - print("\n--- Step 2: List Tools (using session ID) ---") - - list_data = json.dumps({ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/list", - "params": {} - }).encode('utf-8') - - list_req = urllib.request.Request( - MCP_SERVER_URL, - data=list_data, - headers={ - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream", - "mcp-session-id": mcp_session_id - }, - method="POST" - ) - - with urllib.request.urlopen(list_req, timeout=10, context=ctx) as response: - status = response.getcode() - body = response.read().decode('utf-8') - result = json.loads(body) - - print(f" ✓ HTTP Status: {status}") - - if "result" in result and "tools" in result["result"]: - tools = result["result"]["tools"] - print(f" ✓ Found {len(tools)} tools:") - for tool in tools: - print(f" - {tool.get('name', 'unknown')}: {tool.get('description', '')[:50]}") - else: - print(f" ✓ Response: {body[:300]}...") - - # ===================================================================== - # Step 3: Call Tool - Using mcp-session-id - # ===================================================================== - print("\n--- Step 3: Call Tool 'add' (using session ID) ---") - - call_data = json.dumps({ - "jsonrpc": "2.0", - "id": 2, - "method": "tools/call", - "params": { - "name": "add", - "arguments": { - "a": 2, - "b": 4 - } - } - }).encode('utf-8') - - call_req = urllib.request.Request( - MCP_SERVER_URL, - data=call_data, - headers={ - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream", - "mcp-session-id": mcp_session_id - }, - method="POST" - ) - - with urllib.request.urlopen(call_req, timeout=10, context=ctx) as response: - status = response.getcode() - body = response.read().decode('utf-8') - result = json.loads(body) - - print(f" ✓ HTTP Status: {status}") - print(f" ✓ Response: {body}") - - # Check if we got the expected result (2 + 4 = 6) - if "result" in result: - print(f" ✓ Tool call successful!") - else: - print(f" ⚠ Unexpected response format") - - print("\n" + "=" * 60) - print("✓ TEST PASSED: MCP server session flow working correctly") - print("=" * 60) - return True - - except Exception as e: - print(f"\n✗ TEST FAILED: MCP server error: {str(e)}") - import traceback - traceback.print_exc() - print(" Note: This test requires network access to the MCP server.") - return False - - -def test_basic_agent(): - """Test basic agent creation and execution using Responses API.""" - print("\n" + "=" * 60) - print("TEST 2: Basic Agent Creation and Execution (Responses API)") - print("=" * 60) - - agent = None - - try: - with ( - DefaultAzureCredential() as credential, - AIProjectClient( - credential=credential, - endpoint=PROJECT_ENDPOINT - ) as project_client, - project_client.get_openai_client() as openai_client, - ): - print(f"✓ Connected to AI Project at {PROJECT_ENDPOINT}") - - # Create a simple agent without tools - agent = project_client.agents.create_version( - agent_name="basic-test-agent", - definition=PromptAgentDefinition( - model=MODEL_NAME, - instructions="You are a helpful assistant. Answer briefly and concisely.", - ), - ) - print(f"✓ Created agent (id: {agent.id}, name: {agent.name}, version: {agent.version})") - - # Create a conversation thread - conversation = openai_client.conversations.create() - print(f"✓ Created conversation: {conversation.id}") - - # Send a request using the Responses API - response = openai_client.responses.create( - conversation=conversation.id, - input="Say hello and confirm you are working. Keep it brief.", - extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, - ) - log_response_info(response, "Basic Agent Response") - - print(f"\n✓ Agent response: {response.output_text}") - print("\n✓ TEST PASSED: Basic agent works with Responses API") - - # Cleanup - project_client.agents.delete_version( - agent_name=agent.name, - agent_version=agent.version - ) - print(f" Cleaned up agent: {agent.name}") - - return True - - except Exception as e: - print(f"\n✗ TEST FAILED: {str(e)}") - log_exception_info(e, "Basic Agent Error") - import traceback - traceback.print_exc() - return False - - -def test_ai_search_tool(): - """Test that an agent can use AI Search tool to query private AI Search.""" - print("\n" + "=" * 60) - print("TEST 3: AI Search Tool → Private AI Search") - print("=" * 60) - - if not AI_SEARCH_CONNECTION_NAME: - print(" ⚠ AI_SEARCH_CONNECTION_NAME not set, skipping this test") - print(" Set it with: export AI_SEARCH_CONNECTION_NAME=") - return None - - agent = None - - try: - with ( - DefaultAzureCredential() as credential, - AIProjectClient( - credential=credential, - endpoint=PROJECT_ENDPOINT - ) as project_client, - project_client.get_openai_client() as openai_client, - ): - print(f"✓ Connected to AI Project at {PROJECT_ENDPOINT}") - - # Create AI Search tool with SIMPLE query type (our index doesn't have vector fields) - search_tool = AzureAISearchAgentTool( - azure_ai_search=AzureAISearchToolResource(indexes=[ - AISearchIndexResource( - project_connection_id=AI_SEARCH_CONNECTION_NAME, - index_name=AI_SEARCH_INDEX_NAME, - query_type=AzureAISearchQueryType.SIMPLE, # Use simple text search - ) - ]) - ) - - # Create an agent with AI Search tool - agent = project_client.agents.create_version( - agent_name="search-test-agent", - definition=PromptAgentDefinition( - model=MODEL_NAME, - instructions="""You are a helpful assistant that searches for information. - When asked a question, use the search tool to find relevant information.""", - tools=[search_tool], - ), - ) - print(f"✓ Created agent with AI Search tool (id: {agent.id})") - - # Create a conversation thread - conversation = openai_client.conversations.create() - print(f"✓ Created conversation: {conversation.id}") - - # Send a request that should trigger the search tool - response = openai_client.responses.create( - conversation=conversation.id, - input="Search for any documents in the index and tell me what you find.", - extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, - ) - log_response_info(response, "AI Search Response") - - print(f"\n✓ Agent response: {response.output_text[:500]}...") - print("\n✓ TEST PASSED: AI Search tool successfully queried private AI Search") - - # Cleanup - project_client.agents.delete_version( - agent_name=agent.name, - agent_version=agent.version - ) - print(f" Cleaned up agent: {agent.name}") - - return True - - except Exception as e: - print(f"\n✗ TEST FAILED: {str(e)}") - log_exception_info(e, "AI Search Error") - import traceback - traceback.print_exc() - return False - - -def test_mcp_tool_with_agent(): - """Test that an agent can use MCP tool to call the private MCP server.""" - print("\n" + "=" * 60) - print("TEST 4: MCP Tool → Private MCP Server") - print("=" * 60) - - agent = None - - try: - with ( - DefaultAzureCredential() as credential, - AIProjectClient( - credential=credential, - endpoint=PROJECT_ENDPOINT - ) as project_client, - project_client.get_openai_client() as openai_client, - ): - print(f"✓ Connected to AI Project at {PROJECT_ENDPOINT}") - - # Create MCP tool pointing to our private MCP server - mcp_tool = MCPTool( - server_label="hello-mcp", - server_url=MCP_SERVER_URL, - require_approval="never", # Auto-approve for testing - ) - - # Create an agent with MCP tool - agent = project_client.agents.create_version( - agent_name="mcp-test-agent", - definition=PromptAgentDefinition( - model=MODEL_NAME, - instructions="""You are a helpful agent that can use MCP tools. - Use the available MCP tools to answer questions and perform tasks. - When asked to greet someone, use the hello tool from the MCP server.""", - tools=[mcp_tool], - ), - ) - print(f"✓ Created agent with MCP tool (id: {agent.id})") - print(f" MCP Server URL: {MCP_SERVER_URL}") - - # Create a conversation thread - conversation = openai_client.conversations.create() - print(f"✓ Created conversation: {conversation.id}") - - # Send a request that should trigger the MCP tool - print(" Sending request to use MCP hello tool...") - response = openai_client.responses.create( - conversation=conversation.id, - input="Please, calculate 1 + 2 using the MCP tool and print the response.", - extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, - ) - log_response_info(response, "MCP Tool Response") - - # Check if we got an MCP approval request (if require_approval was set) - for item in response.output: - if hasattr(item, 'type') and item.type == "mcp_approval_request": - print(f" MCP approval requested for: {item.server_label}") - - # Auto-approve - input_list: ResponseInputParam = [ - McpApprovalResponse( - type="mcp_approval_response", - approve=True, - approval_request_id=item.id, - ) - ] - - response = openai_client.responses.create( - input=input_list, - previous_response_id=response.id, - extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, - ) - - print(f"\n✓ Agent response: {response.output_text}") - - # Check if the response mentions the MCP server or greeting - if "hello" in response.output_text.lower() or "greet" in response.output_text.lower(): - print("\n✓ TEST PASSED: MCP tool connected to private MCP server") - result = True - else: - print("\n⚠ TEST UNCERTAIN: Got response but unclear if MCP tool was used") - result = True # Still consider it a pass if we got a response - - # Cleanup - project_client.agents.delete_version( - agent_name=agent.name, - agent_version=agent.version - ) - print(f" Cleaned up agent: {agent.name}") - - return result - - except Exception as e: - print(f"\n✗ TEST FAILED: {str(e)}") - log_exception_info(e, "MCP Tool Error") - import traceback - traceback.print_exc() - - # Check for specific error patterns - error_str = str(e) - if "424" in error_str or "Failed Dependency" in error_str: - print("\n ⚠ This is the known DNS resolution issue:") - print(" The Data Proxy cannot resolve the private Container Apps DNS.") - print(" The MCP server IS reachable from VNet VMs (Test 1), but not via Data Proxy.") - - return False - - -def test_openai_responses_api(): - """Test direct usage of OpenAI Responses API without an agent.""" - print("\n" + "=" * 60) - print("TEST 5: OpenAI Responses API (Direct)") - print("=" * 60) - - try: - with ( - DefaultAzureCredential() as credential, - AIProjectClient( - credential=credential, - endpoint=PROJECT_ENDPOINT - ) as project_client, - project_client.get_openai_client() as openai_client, - ): - print(f"✓ Connected to AI Project at {PROJECT_ENDPOINT}") - - # Use the Responses API directly without an agent - response = openai_client.responses.create( - model=MODEL_NAME, - input="What is 2 + 2? Answer with just the number.", - ) - log_response_info(response, "Direct OpenAI Response") - - print(f"\n✓ Response: {response.output_text}") - print("\n✓ TEST PASSED: OpenAI Responses API works directly") - return True - - except Exception as e: - print(f"\n✗ TEST FAILED: {str(e)}") - log_exception_info(e, "OpenAI API Error") - import traceback - traceback.print_exc() - return False - - -def main(): - print("=" * 60) - print("AGENTS V2 END-TO-END TEST") - print("Using new Responses API and Agent Versioning") - print("=" * 60) - print(f"\nConfiguration:") - print(f" Project Endpoint: {PROJECT_ENDPOINT}") - print(f" Model: {MODEL_NAME}") - print(f" AI Search Index: {AI_SEARCH_INDEX_NAME}") - print(f" AI Search Connection: {AI_SEARCH_CONNECTION_NAME or '(not set)'}") - print(f" MCP Server: {MCP_SERVER_URL}") - - results = {} - - # Test 1: MCP Server Connectivity (direct HTTP) - results['mcp_connectivity'] = test_mcp_server_connectivity() - - # Test 2: OpenAI Responses API (direct) - results['responses_api'] = test_openai_responses_api() - - # Test 3: Basic Agent with Responses API - results['basic_agent'] = test_basic_agent() - - # Test 4: AI Search Tool (optional) - ai_search_result = test_ai_search_tool() - if ai_search_result is not None: - results['ai_search'] = ai_search_result - - # Test 5: MCP Tool with Agent - mcp_tool_result = test_mcp_tool_with_agent() - if mcp_tool_result is not None: - results['mcp_tool'] = mcp_tool_result - - # Summary - print("\n" + "=" * 60) - print("TEST SUMMARY") - print("=" * 60) - for test_name, passed in results.items(): - status = "✓ PASSED" if passed else "✗ FAILED" - print(f" {test_name}: {status}") - - all_passed = all(results.values()) - print("\n" + ("=" * 60)) - if all_passed: - print("ALL TESTS PASSED - Agents v2 API is working!") - else: - print("SOME TESTS FAILED - Check the output above for details") - print("=" * 60) - - return 0 if all_passed else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_ai_search_tool_agents_v2.py b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_ai_search_tool_agents_v2.py deleted file mode 100644 index ab2223892..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_ai_search_tool_agents_v2.py +++ /dev/null @@ -1,388 +0,0 @@ -#!/usr/bin/env python3 -""" -AI Search Tool Test Script - -This script focuses on testing Azure AI Search tool integration -with Azure AI Foundry Agents v2. - -Tests: -1. AI Search Connectivity - Direct REST API test to AI Search service -2. AI Search Tool via Agent - Test AI Search tool via agent (uses Data Proxy) - -The agent test validates that: -- The Data Proxy can resolve private endpoint DNS -- The AI Search connection is properly configured -- The agent can query documents from the private AI Search index -""" - -import os -import sys -import logging -import argparse -import json -import urllib.request -import urllib.error -import ssl - -# ============================================================================ -# LOGGING CONFIGURATION -# ============================================================================ -LOG_LEVEL = logging.INFO - -logging.basicConfig( - level=LOG_LEVEL, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) - -logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(LOG_LEVEL) -logging.getLogger("httpx").setLevel(LOG_LEVEL) -logging.getLogger("urllib3").setLevel(logging.WARNING) -logging.getLogger("azure.identity").setLevel(logging.WARNING) - -# ============================================================================ - -from azure.ai.projects import AIProjectClient -from azure.ai.projects.models import ( - AzureAISearchAgentTool, - AzureAISearchToolResource, - AISearchIndexResource, - AzureAISearchQueryType, - PromptAgentDefinition, -) -from azure.identity import DefaultAzureCredential - -# ============================================================================ -# CONFIGURATION -# ============================================================================ -PROJECT_ENDPOINT = os.environ.get( - "PROJECT_ENDPOINT", - "https://aiservicesaxy3.services.ai.azure.com/api/projects/projectaxy3" -) -MODEL_NAME = os.environ.get("MODEL_NAME", "gpt-4o-mini") - -# AI Search Configuration -AI_SEARCH_CONNECTION_NAME = os.environ.get( - "AI_SEARCH_CONNECTION_NAME", - "aiservicesaxy3search" # Default connection name from template deployment -) -AI_SEARCH_INDEX_NAME = os.environ.get("AI_SEARCH_INDEX_NAME", "test-index") - -# AI Search endpoint for direct connectivity test (optional) -# This is only used for the connectivity test, not the agent test -AI_SEARCH_ENDPOINT = os.environ.get( - "AI_SEARCH_ENDPOINT", - "" # e.g., "https://aiservicesaxy3search.search.windows.net" -) - -# ============================================================================ - - -def log_response_info(response, label="Response"): - """Extract and log useful debugging info from OpenAI response objects.""" - logger = logging.getLogger(__name__) - try: - if hasattr(response, '_request_id'): - logger.info(f"{label} - Request ID: {response._request_id}") - if hasattr(response, 'id'): - logger.info(f"{label} - Response ID: {response.id}") - if hasattr(response, '_response') and hasattr(response._response, 'headers'): - headers = response._response.headers - if 'x-request-id' in headers: - logger.info(f"{label} - x-request-id: {headers['x-request-id']}") - if 'x-ms-request-id' in headers: - logger.info(f"{label} - x-ms-request-id: {headers['x-ms-request-id']}") - except Exception as e: - logger.debug(f"Could not extract response info: {e}") - - -def log_exception_info(exception, label="Exception"): - """Extract and log request info from OpenAI exceptions.""" - logger = logging.getLogger(__name__) - try: - if hasattr(exception, 'response') and exception.response is not None: - resp = exception.response - headers = resp.headers if hasattr(resp, 'headers') else {} - - request_id = headers.get('x-request-id', 'N/A') - ms_request_id = headers.get('x-ms-request-id', 'N/A') - - logger.error(f"{label} - x-request-id: {request_id}") - logger.error(f"{label} - x-ms-request-id: {ms_request_id}") - logger.error(f"{label} - Status: {resp.status_code if hasattr(resp, 'status_code') else 'N/A'}") - - if hasattr(resp, 'text'): - logger.error(f"{label} - Body: {resp.text[:500]}") - except Exception as e: - logger.debug(f"Could not extract exception info: {e}") - - -def test_ai_search_connectivity(): - """ - Test direct connectivity to AI Search service. - - Note: This test requires AI_SEARCH_ENDPOINT to be set and will only work - from within the VNet (e.g., jump box) for private AI Search endpoints. - """ - print("\n" + "=" * 60) - print("TEST: AI Search Connectivity (Direct REST API)") - print("=" * 60) - - if not AI_SEARCH_ENDPOINT: - print(" ⚠ AI_SEARCH_ENDPOINT not set, skipping connectivity test") - print(" Set it with: export AI_SEARCH_ENDPOINT=https://.search.windows.net") - print(" Note: This test only works from within the VNet for private endpoints") - return None - - print(f" Target: {AI_SEARCH_ENDPOINT}") - print(f" Index: {AI_SEARCH_INDEX_NAME}") - - try: - # Get Azure AD token for AI Search - credential = DefaultAzureCredential() - token = credential.get_token("https://search.azure.com/.default") - - # Query the index - url = f"{AI_SEARCH_ENDPOINT}/indexes/{AI_SEARCH_INDEX_NAME}/docs?api-version=2024-07-01&search=*&$top=1" - - ctx = ssl.create_default_context() - headers = { - "Authorization": f"Bearer {token.token}", - "Content-Type": "application/json" - } - - req = urllib.request.Request(url, headers=headers, method="GET") - - print("\n--- Querying AI Search Index ---") - with urllib.request.urlopen(req, timeout=15, context=ctx) as response: - status = response.status - body = response.read().decode('utf-8') - result = json.loads(body) - - print(f" ✓ HTTP Status: {status}") - doc_count = len(result.get('value', [])) - print(f" ✓ Documents found: {doc_count}") - - if doc_count > 0: - print(f" ✓ Sample document keys: {list(result['value'][0].keys())[:5]}") - - print("\n" + "=" * 60) - print("✓ TEST PASSED: AI Search connectivity working") - print("=" * 60) - return True - - except urllib.error.URLError as e: - print(f"\n✗ TEST FAILED: {e}") - if "Name or service not known" in str(e): - print(" Note: This is expected if running from outside the VNet") - print(" The AI Search endpoint is only accessible via private endpoint") - return False - except Exception as e: - print(f"\n✗ TEST FAILED: {e}") - import traceback - traceback.print_exc() - return False - - -def test_ai_search_tool_via_agent(): - """ - Test AI Search tool via Azure AI Agent. - - This test validates that: - - The Data Proxy can reach the private AI Search endpoint - - The AI Search connection is properly configured - - The agent can query and retrieve documents - """ - print("\n" + "=" * 60) - print("TEST: AI Search Tool via Agent") - print("=" * 60) - - if not AI_SEARCH_CONNECTION_NAME: - print(" ⚠ AI_SEARCH_CONNECTION_NAME not set, skipping this test") - print(" Set it with: export AI_SEARCH_CONNECTION_NAME=") - return None - - print(f" Connection: {AI_SEARCH_CONNECTION_NAME}") - print(f" Index: {AI_SEARCH_INDEX_NAME}") - - agent = None - - try: - with ( - DefaultAzureCredential() as credential, - AIProjectClient( - credential=credential, - endpoint=PROJECT_ENDPOINT - ) as project_client, - project_client.get_openai_client() as openai_client, - ): - print(f"✓ Connected to AI Project at {PROJECT_ENDPOINT}") - - # Create AI Search tool with SIMPLE query type - search_tool = AzureAISearchAgentTool( - azure_ai_search=AzureAISearchToolResource(indexes=[ - AISearchIndexResource( - project_connection_id=AI_SEARCH_CONNECTION_NAME, - index_name=AI_SEARCH_INDEX_NAME, - query_type=AzureAISearchQueryType.SIMPLE, - ) - ]) - ) - - # Create an agent with AI Search tool - agent = project_client.agents.create_version( - agent_name="search-tool-test", - definition=PromptAgentDefinition( - model=MODEL_NAME, - instructions="""You are a helpful assistant that searches for information. - When asked a question, use the search tool to find relevant information. - Always summarize what you found from the search results.""", - tools=[search_tool], - ), - ) - print(f"✓ Created agent with AI Search tool (id: {agent.id})") - print(f" Connection: {AI_SEARCH_CONNECTION_NAME}") - print(f" Index: {AI_SEARCH_INDEX_NAME}") - - # Create a conversation - conversation = openai_client.conversations.create() - print(f"✓ Created conversation: {conversation.id}") - - # Send a request that should trigger the search tool - print(" Sending search request to agent...") - response = openai_client.responses.create( - conversation=conversation.id, - input="Search for any documents in the index and tell me what you find. List any document titles or content you discover.", - extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, - ) - log_response_info(response, "AI Search Response") - - # Display response (truncate if too long) - output_text = response.output_text - if len(output_text) > 500: - print(f"\n✓ Agent response: {output_text[:500]}...") - else: - print(f"\n✓ Agent response: {output_text}") - - # Cleanup - project_client.agents.delete_version( - agent_name=agent.name, - agent_version=agent.version - ) - print(f" Cleaned up agent: {agent.name}") - - print("\n" + "=" * 60) - print("✓ TEST PASSED: AI Search tool via agent") - print("=" * 60) - return True - - except Exception as e: - print(f"\n✗ TEST FAILED: {str(e)}") - log_exception_info(e, "AI Search Error") - import traceback - traceback.print_exc() - - # Cleanup on failure - if agent: - try: - project_client.agents.delete_version( - agent_name=agent.name, - agent_version=agent.version - ) - except: - pass - - return False - - -def main(): - parser = argparse.ArgumentParser( - description="Test AI Search tool integration with Azure AI Foundry Agents v2", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Examples: - python test_ai_search_tool_agents_v2.py # Run all tests - python test_ai_search_tool_agents_v2.py --test connectivity # Only connectivity test - python test_ai_search_tool_agents_v2.py --test agent # Only agent test - python test_ai_search_tool_agents_v2.py --retry 3 # Retry failed tests up to 3 times - -Environment variables: - PROJECT_ENDPOINT - Azure AI project endpoint - MODEL_NAME - Model to use (default: gpt-4o-mini) - AI_SEARCH_CONNECTION_NAME - AI Search connection name in the project - AI_SEARCH_INDEX_NAME - AI Search index name (default: test-index) - AI_SEARCH_ENDPOINT - AI Search endpoint URL (for connectivity test only) -""" - ) - parser.add_argument( - "--test", - choices=["connectivity", "agent", "all"], - default="all", - help="Which test to run (default: all)" - ) - parser.add_argument( - "--retry", - type=int, - default=0, - help="Number of times to retry failed tests (default: 0)" - ) - - args = parser.parse_args() - - print("=" * 60) - print("AI SEARCH TOOL TEST") - print("=" * 60) - print() - print("Configuration:") - print(f" Project Endpoint: {PROJECT_ENDPOINT}") - print(f" Model: {MODEL_NAME}") - print(f" AI Search Connection: {AI_SEARCH_CONNECTION_NAME or '(not set)'}") - print(f" AI Search Index: {AI_SEARCH_INDEX_NAME}") - print(f" AI Search Endpoint: {AI_SEARCH_ENDPOINT or '(not set - connectivity test skipped)'}") - - results = {} - - # Run connectivity test - if args.test in ["connectivity", "all"]: - result = test_ai_search_connectivity() - if result is not None: - results['connectivity'] = result - - # Run agent test - if args.test in ["agent", "all"]: - for attempt in range(args.retry + 1): - if attempt > 0: - print(f"\n--- Retry attempt {attempt}/{args.retry} ---") - - result = test_ai_search_tool_via_agent() - if result is not None: - results['agent'] = result - if result: - break # Success, no need to retry - else: - break # Skipped, no need to retry - - # Summary - print("\n" + "=" * 60) - print("TEST SUMMARY") - print("=" * 60) - - for test_name, passed in results.items(): - status = "✓ PASSED" if passed else "✗ FAILED" - print(f" {test_name}: {status}") - - # Exit with appropriate code - all_passed = all(results.values()) if results else True - if all_passed: - print("\n" + "=" * 60) - print("ALL TESTS PASSED!") - print("=" * 60) - sys.exit(0) - else: - print("\n" + "=" * 60) - print("SOME TESTS FAILED") - print("=" * 60) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_mcp_tools_agents_v2.py b/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_mcp_tools_agents_v2.py deleted file mode 100644 index 32698baf1..000000000 --- a/infrastructure/infrastructure-setup-bicep/19-hybrid-private-resources-agent-setup/tests/test_mcp_tools_agents_v2.py +++ /dev/null @@ -1,455 +0,0 @@ -#!/usr/bin/env python3 -""" -MCP Tools Test Script - -This script focuses on testing MCP (Model Context Protocol) tool integration -with Azure AI Foundry Agents v2. - -Tests: -1. MCP Connectivity (Direct HTTP) - Direct session flow test to MCP server -2. MCP Tool via Agent (Public) - Test MCP tool via public Container App -3. MCP Tool via Agent (Private) - Test MCP tool via private Container App (VNet) - -Note: Tests 2 and 3 may intermittently fail due to known Hyena cluster routing -issue where ~50% of requests hit a scale unit without Data Proxy deployed. -""" - -import os -import sys -import logging -import argparse - -# ============================================================================ -# LOGGING CONFIGURATION -# ============================================================================ -LOG_LEVEL = logging.INFO - -logging.basicConfig( - level=LOG_LEVEL, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) - -logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(LOG_LEVEL) -logging.getLogger("httpx").setLevel(LOG_LEVEL) -logging.getLogger("urllib3").setLevel(logging.WARNING) -logging.getLogger("azure.identity").setLevel(logging.WARNING) - -# ============================================================================ - -from azure.ai.projects import AIProjectClient -from azure.ai.projects.models import ( - MCPTool, - PromptAgentDefinition, -) -from azure.identity import DefaultAzureCredential -from openai.types.responses import ResponseInputParam -from openai.types.responses.response_input_param import McpApprovalResponse - -# ============================================================================ -# CONFIGURATION -# ============================================================================ -PROJECT_ENDPOINT = os.environ.get( - "PROJECT_ENDPOINT", - "https://aiservicesaxy3.services.ai.azure.com/api/projects/projectaxy3" -) -MODEL_NAME = os.environ.get("MODEL_NAME", "gpt-4o-mini") - -# MCP Server URLs -# Public MCP server (external, accessible from anywhere) -MCP_SERVER_PUBLIC = os.environ.get( - "MCP_SERVER_PUBLIC", - "https://multi-auth-mcp.victorioussmoke-7859ae09.uksouth.azurecontainerapps.io/noauth/mcp" -) - -# Private MCP server (internal, only accessible from VNet via Data Proxy) -MCP_SERVER_PRIVATE = os.environ.get( - "MCP_SERVER_PRIVATE", - "https://mcp-http-server.jollydune-20a0f709.westus2.azurecontainerapps.io/noauth/mcp" -) - -# ============================================================================ - - -def log_response_info(response, label="Response"): - """Extract and log useful debugging info from OpenAI response objects.""" - logger = logging.getLogger(__name__) - try: - if hasattr(response, '_request_id'): - logger.info(f"{label} - Request ID: {response._request_id}") - if hasattr(response, 'id'): - logger.info(f"{label} - Response ID: {response.id}") - if hasattr(response, '_response') and hasattr(response._response, 'headers'): - headers = response._response.headers - if 'x-request-id' in headers: - logger.info(f"{label} - x-request-id: {headers['x-request-id']}") - if 'x-ms-request-id' in headers: - logger.info(f"{label} - x-ms-request-id: {headers['x-ms-request-id']}") - except Exception as e: - logger.debug(f"Could not extract response info: {e}") - - -def log_exception_info(exception, label="Exception"): - """Extract and log request info from OpenAI exceptions.""" - logger = logging.getLogger(__name__) - try: - if hasattr(exception, 'response') and exception.response is not None: - resp = exception.response - headers = resp.headers if hasattr(resp, 'headers') else {} - - request_id = headers.get('x-request-id', 'N/A') - ms_request_id = headers.get('x-ms-request-id', 'N/A') - - logger.error(f"{label} - x-request-id: {request_id}") - logger.error(f"{label} - x-ms-request-id: {ms_request_id}") - - print(f" 📋 Request ID (x-request-id): {request_id}") - print(f" 📋 MS Request ID (x-ms-request-id): {ms_request_id}") - - if hasattr(resp, 'status_code'): - logger.error(f"{label} - HTTP Status: {resp.status_code}") - - if hasattr(exception, 'request_id'): - logger.error(f"{label} - request_id attribute: {exception.request_id}") - print(f" 📋 Request ID: {exception.request_id}") - - except Exception as e: - logger.debug(f"Could not extract exception info: {e}") - - -def test_mcp_connectivity(mcp_url: str, label: str = "MCP Server"): - """Test MCP server with full session workflow: initialize → list tools → call tool.""" - print("\n" + "=" * 60) - print(f"TEST: MCP Connectivity - {label}") - print("=" * 60) - - import urllib.request - import ssl - import json - - try: - ctx = ssl.create_default_context() - - print(f" Target MCP Server: {mcp_url}") - - # Step 1: Initialize - Get mcp-session-id - print("\n--- Step 1: Initialize (get mcp-session-id) ---") - - init_data = json.dumps({ - "method": "initialize", - "params": { - "protocolVersion": "2025-11-25", - "capabilities": { - "sampling": {}, - "elicitation": {}, - "roots": {"listChanged": True} - }, - "clientInfo": { - "name": "test-mcp-client", - "version": "1.0.0" - } - }, - "jsonrpc": "2.0", - "id": 0 - }).encode('utf-8') - - init_req = urllib.request.Request( - mcp_url, - data=init_data, - headers={ - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream" - }, - method="POST" - ) - - with urllib.request.urlopen(init_req, timeout=15, context=ctx) as response: - status = response.getcode() - body = response.read().decode('utf-8') - mcp_session_id = response.getheader('mcp-session-id') - - print(f" ✓ HTTP Status: {status}") - print(f" ✓ Response: {body[:300]}...") - - if mcp_session_id: - print(f" ✓ MCP Session ID: {mcp_session_id}") - else: - print(" ✗ No mcp-session-id header in response!") - return False - - # Step 2: List Tools - print("\n--- Step 2: List Tools (using session ID) ---") - - list_data = json.dumps({ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/list", - "params": {} - }).encode('utf-8') - - list_req = urllib.request.Request( - mcp_url, - data=list_data, - headers={ - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream", - "mcp-session-id": mcp_session_id - }, - method="POST" - ) - - with urllib.request.urlopen(list_req, timeout=10, context=ctx) as response: - status = response.getcode() - body = response.read().decode('utf-8') - result = json.loads(body) - - print(f" ✓ HTTP Status: {status}") - - if "result" in result and "tools" in result["result"]: - tools = result["result"]["tools"] - print(f" ✓ Found {len(tools)} tools:") - for tool in tools: - print(f" - {tool.get('name', 'unknown')}: {tool.get('description', '')[:50]}") - else: - print(f" ✓ Response: {body[:300]}...") - - # Step 3: Call Tool 'add' - print("\n--- Step 3: Call Tool 'add' (using session ID) ---") - - call_data = json.dumps({ - "jsonrpc": "2.0", - "id": 2, - "method": "tools/call", - "params": { - "name": "add", - "arguments": {"a": 2, "b": 4} - } - }).encode('utf-8') - - call_req = urllib.request.Request( - mcp_url, - data=call_data, - headers={ - "Content-Type": "application/json", - "Accept": "application/json, text/event-stream", - "mcp-session-id": mcp_session_id - }, - method="POST" - ) - - with urllib.request.urlopen(call_req, timeout=10, context=ctx) as response: - status = response.getcode() - body = response.read().decode('utf-8') - result = json.loads(body) - - print(f" ✓ HTTP Status: {status}") - print(f" ✓ Response: {body}") - - if "result" in result: - print(f" ✓ Tool call successful!") - else: - print(f" ⚠ Unexpected response format") - - print("\n" + "=" * 60) - print(f"✓ TEST PASSED: {label} session flow working correctly") - print("=" * 60) - return True - - except Exception as e: - print(f"\n✗ TEST FAILED: {str(e)}") - import traceback - traceback.print_exc() - return False - - -def test_mcp_tool_via_agent(mcp_url: str, label: str = "MCP Server"): - """Test that an agent can use MCP tool via the Data Proxy.""" - print("\n" + "=" * 60) - print(f"TEST: MCP Tool via Agent - {label}") - print("=" * 60) - - agent = None - - try: - with ( - DefaultAzureCredential() as credential, - AIProjectClient( - credential=credential, - endpoint=PROJECT_ENDPOINT - ) as project_client, - project_client.get_openai_client() as openai_client, - ): - print(f"✓ Connected to AI Project at {PROJECT_ENDPOINT}") - - # Create MCP tool - mcp_tool = MCPTool( - server_label="test-mcp", - server_url=mcp_url, - require_approval="never", - ) - - # Create agent with MCP tool - agent = project_client.agents.create_version( - agent_name="mcp-tool-test", - definition=PromptAgentDefinition( - model=MODEL_NAME, - instructions="""You are a helpful agent that can use MCP tools. - When asked to calculate, use the 'add' tool from the MCP server.""", - tools=[mcp_tool], - ), - ) - print(f"✓ Created agent with MCP tool (id: {agent.id})") - print(f" MCP Server URL: {mcp_url}") - - # Create conversation - conversation = openai_client.conversations.create() - print(f"✓ Created conversation: {conversation.id}") - - # Send request - print(" Sending request to use MCP add tool...") - response = openai_client.responses.create( - conversation=conversation.id, - input="Please calculate 1 + 2 using the MCP add tool and tell me the result.", - extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, - ) - log_response_info(response, "MCP Tool Response") - - # Handle MCP approval if needed - for item in response.output: - if hasattr(item, 'type') and item.type == "mcp_approval_request": - print(f" MCP approval requested for: {item.server_label}") - input_list: ResponseInputParam = [ - McpApprovalResponse( - type="mcp_approval_response", - approve=True, - approval_request_id=item.id, - ) - ] - response = openai_client.responses.create( - input=input_list, - previous_response_id=response.id, - extra_body={"agent": {"name": agent.name, "type": "agent_reference"}}, - ) - - print(f"\n✓ Agent response: {response.output_text}") - - # Cleanup - project_client.agents.delete_version( - agent_name=agent.name, - agent_version=agent.version - ) - print(f" Cleaned up agent: {agent.name}") - - print(f"\n✓ TEST PASSED: MCP tool via {label}") - return True - - except Exception as e: - error_str = str(e) - print(f"\n✗ TEST FAILED: {error_str}") - log_exception_info(e, "MCP Tool Error") - - # Provide context for known issues - if "TaskCanceledException" in error_str: - print("\n ⚠ Known Issue: TaskCanceledException") - print(" This occurs when request hits the wrong Hyena scale unit") - print(" (Data Proxy is only deployed on one of two scale units)") - print(" Re-running the test may succeed on the next attempt.") - elif "424" in error_str or "Failed Dependency" in error_str: - print("\n ⚠ Known Issue: DNS Resolution") - print(" Data Proxy cannot resolve private Container Apps DNS.") - - import traceback - traceback.print_exc() - - # Cleanup agent if created - if agent is not None: - try: - with ( - DefaultAzureCredential() as credential, - AIProjectClient(credential=credential, endpoint=PROJECT_ENDPOINT) as project_client, - ): - project_client.agents.delete_version(agent_name=agent.name, agent_version=agent.version) - print(f" Cleaned up agent: {agent.name}") - except: - pass - - return False - - -def main(): - parser = argparse.ArgumentParser(description="MCP Tools Test Script") - parser.add_argument( - "--test", - choices=["public", "private", "all"], - default="all", - help="Which MCP server to test via agent: public, private, or all (default: all)" - ) - parser.add_argument( - "--retry", - type=int, - default=1, - help="Number of retries for agent tests (default: 1)" - ) - args = parser.parse_args() - - print("=" * 60) - print("MCP TOOLS TEST") - print("=" * 60) - print(f"\nConfiguration:") - print(f" Project Endpoint: {PROJECT_ENDPOINT}") - print(f" Model: {MODEL_NAME}") - print(f" Public MCP Server: {MCP_SERVER_PUBLIC}") - print(f" Private MCP Server: {MCP_SERVER_PRIVATE}") - - results = {} - - # Always run connectivity test first (opens the session) - mcp_url = MCP_SERVER_PUBLIC if args.test in ["public", "all"] else MCP_SERVER_PRIVATE - results['connectivity'] = test_mcp_connectivity(mcp_url, "Public MCP Server" if mcp_url == MCP_SERVER_PUBLIC else "Private MCP Server") - - # Test: MCP Tool via Agent (Public) - if args.test in ["public", "all"]: - for attempt in range(args.retry): - if attempt > 0: - print(f"\n Retry attempt {attempt + 1}/{args.retry}...") - result = test_mcp_tool_via_agent(MCP_SERVER_PUBLIC, "Public MCP Server") - if result: - results['agent_public'] = True - break - else: - results['agent_public'] = False - - # Test: MCP Tool via Agent (Private) - if args.test in ["private", "all"]: - for attempt in range(args.retry): - if attempt > 0: - print(f"\n Retry attempt {attempt + 1}/{args.retry}...") - result = test_mcp_tool_via_agent(MCP_SERVER_PRIVATE, "Private MCP Server") - if result: - results['agent_private'] = True - break - else: - results['agent_private'] = False - - # Summary - print("\n" + "=" * 60) - print("TEST SUMMARY") - print("=" * 60) - for test_name, passed in results.items(): - status = "✓ PASSED" if passed else "✗ FAILED" - print(f" {test_name}: {status}") - - all_passed = all(results.values()) - print("\n" + "=" * 60) - if all_passed: - print("ALL TESTS PASSED!") - else: - print("SOME TESTS FAILED") - print("Note: Agent tests may fail due to Hyena cluster routing (~50% chance)") - print(" Use --retry N to retry failed tests") - print("=" * 60) - - return 0 if all_passed else 1 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/infrastructure/infrastructure-setup-bicep/20-user-assigned-identity/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/20-user-assigned-identity/azuredeploy.json index a87e40609..06c33519b 100644 --- a/infrastructure/infrastructure-setup-bicep/20-user-assigned-identity/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/20-user-assigned-identity/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11385179130648578926" + "version": "0.39.26.7824", + "templateHash": "3240349148352159726" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/30-customer-managed-keys/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/30-customer-managed-keys/azuredeploy.json index 6cb7392cd..64192cbd4 100644 --- a/infrastructure/infrastructure-setup-bicep/30-customer-managed-keys/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/30-customer-managed-keys/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16822506733457783114" + "version": "0.39.26.7824", + "templateHash": "16694821934772097765" } }, "parameters": { @@ -142,8 +142,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "812724488663360404" + "version": "0.39.26.7824", + "templateHash": "10984074837058238866" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/31-customer-managed-keys-standard-agent/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/31-customer-managed-keys-standard-agent/azuredeploy.json index 2df23eb63..80fece3fc 100644 --- a/infrastructure/infrastructure-setup-bicep/31-customer-managed-keys-standard-agent/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/31-customer-managed-keys-standard-agent/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "18062982936332919565" + "version": "0.39.26.7824", + "templateHash": "6166861745321957187" } }, "parameters": { @@ -152,8 +152,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "4091539512693342510" + "version": "0.39.26.7824", + "templateHash": "10572111282973522005" } }, "parameters": { @@ -288,8 +288,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7266831742569425479" + "version": "0.39.26.7824", + "templateHash": "4777074434873577497" } }, "parameters": { @@ -546,8 +546,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "10231344616002774243" + "version": "0.39.26.7824", + "templateHash": "17728000413021798670" } }, "parameters": { @@ -710,8 +710,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12192296260645390578" + "version": "0.39.26.7824", + "templateHash": "8294688920685850261" } }, "parameters": { @@ -886,8 +886,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15991712531324316353" + "version": "0.39.26.7824", + "templateHash": "14683840003859985069" } }, "parameters": { @@ -902,7 +902,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('azureStorageName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -943,8 +943,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11201886913940098363" + "version": "0.39.26.7824", + "templateHash": "2161753938341361575" } }, "parameters": { @@ -965,7 +965,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1007,8 +1007,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15867124606695536257" + "version": "0.39.26.7824", + "templateHash": "7968115481508840" } }, "parameters": { @@ -1029,7 +1029,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1040,7 +1040,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1096,8 +1096,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11180853619796693081" + "version": "0.39.26.7824", + "templateHash": "16815875843770432836" } }, "parameters": { @@ -1193,8 +1193,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "9077220512228030688" + "version": "0.39.26.7824", + "templateHash": "8346749807649424278" } }, "parameters": { @@ -1263,8 +1263,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "3625425571119261380" + "version": "0.39.26.7824", + "templateHash": "13874725855824693255" } }, "parameters": { @@ -1294,7 +1294,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageName'))]", "name": "[guid(resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'), resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')))]", "properties": { "principalId": "[parameters('aiProjectPrincipalId')]", diff --git a/infrastructure/infrastructure-setup-bicep/32-customer-managed-keys-user-assigned-identity/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/32-customer-managed-keys-user-assigned-identity/azuredeploy.json deleted file mode 100644 index c9dbbfd7f..000000000 --- a/infrastructure/infrastructure-setup-bicep/32-customer-managed-keys-user-assigned-identity/azuredeploy.json +++ /dev/null @@ -1,428 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15203946428285185650" - } - }, - "parameters": { - "aiFoundryName": { - "type": "string", - "defaultValue": "ai-foundry-complete-cmk", - "metadata": { - "description": "That name is the name of our application. It has to be unique." - } - }, - "aiProjectName": { - "type": "string", - "defaultValue": "[format('{0}-proj', parameters('aiFoundryName'))]", - "metadata": { - "description": "Name of the AI Foundry project" - } - }, - "location": { - "type": "string", - "defaultValue": "eastus2", - "metadata": { - "description": "Location for all resources." - } - }, - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Key Vault target" - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Key Vault key" - } - }, - "keyVersion": { - "type": "string", - "metadata": { - "description": "Version of the Azure Key Vault key" - } - }, - "userAssignedIdentityId": { - "type": "string", - "metadata": { - "description": "Resource ID of the user-assigned managed identity to use for CMK encryption" - } - }, - "userAssignedIdentityClientId": { - "type": "string", - "metadata": { - "description": "Client ID of the user-assigned managed identity" - } - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "foundryAccount", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiFoundryName": { - "value": "[parameters('aiFoundryName')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "userAssignedIdentityId": { - "value": "[parameters('userAssignedIdentityId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11667222887400574489" - } - }, - "parameters": { - "aiFoundryName": { - "type": "string", - "metadata": { - "description": "Name of the AI Foundry account" - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Location for the resource" - } - }, - "userAssignedIdentityId": { - "type": "string", - "metadata": { - "description": "Resource ID of the user-assigned managed identity" - } - } - }, - "variables": { - "identityConfig": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', parameters('userAssignedIdentityId'))]": {} - } - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('aiFoundryName')]", - "location": "[parameters('location')]", - "identity": "[variables('identityConfig')]", - "kind": "AIServices", - "sku": { - "name": "S0" - }, - "properties": { - "allowProjectManagement": true, - "publicNetworkAccess": "Enabled", - "customSubDomainName": "[parameters('aiFoundryName')]", - "disableLocalAuth": true - } - } - ], - "outputs": { - "accountId": { - "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('aiFoundryName'))]" - }, - "accountName": { - "type": "string", - "value": "[parameters('aiFoundryName')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "cmkEncryption", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiFoundryName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'foundryAccount'), '2025-04-01').outputs.accountName.value]" - }, - "location": { - "value": "[parameters('location')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "keyName": { - "value": "[parameters('keyName')]" - }, - "keyVersion": { - "value": "[parameters('keyVersion')]" - }, - "userAssignedIdentityId": { - "value": "[parameters('userAssignedIdentityId')]" - }, - "userAssignedIdentityClientId": { - "value": "[parameters('userAssignedIdentityClientId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "14977133416255499203" - } - }, - "parameters": { - "aiFoundryName": { - "type": "string", - "metadata": { - "description": "Name of the AI Foundry account" - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Location for the resource" - } - }, - "keyVaultName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Key Vault" - } - }, - "keyName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Key Vault key" - } - }, - "keyVersion": { - "type": "string", - "metadata": { - "description": "Version of the Azure Key Vault key" - } - }, - "userAssignedIdentityId": { - "type": "string", - "metadata": { - "description": "Resource ID of the user-assigned managed identity" - } - }, - "userAssignedIdentityClientId": { - "type": "string", - "metadata": { - "description": "Client ID of the user-assigned managed identity" - } - } - }, - "variables": { - "keyVaultUri": "[format('https://{0}{1}/', parameters('keyVaultName'), environment().suffixes.keyvaultDns)]" - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('aiFoundryName')]", - "location": "[parameters('location')]", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', parameters('userAssignedIdentityId'))]": {} - } - }, - "kind": "AIServices", - "sku": { - "name": "S0" - }, - "properties": { - "encryption": { - "keySource": "Microsoft.KeyVault", - "keyVaultProperties": { - "keyVaultUri": "[variables('keyVaultUri')]", - "keyName": "[parameters('keyName')]", - "keyVersion": "[parameters('keyVersion')]", - "identityClientId": "[parameters('userAssignedIdentityClientId')]" - } - }, - "allowProjectManagement": true, - "publicNetworkAccess": "Enabled", - "customSubDomainName": "[parameters('aiFoundryName')]", - "disableLocalAuth": true - } - } - ], - "outputs": { - "encryptionStatus": { - "type": "string", - "value": "CMK encryption enabled" - }, - "keyVaultUri": { - "type": "string", - "value": "[variables('keyVaultUri')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'foundryAccount')]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2025-04-01", - "name": "foundryProject", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "aiFoundryName": { - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'foundryAccount'), '2025-04-01').outputs.accountName.value]" - }, - "projectName": { - "value": "[parameters('aiProjectName')]" - }, - "projectDisplayName": { - "value": "[parameters('aiProjectName')]" - }, - "projectDescription": { - "value": "AI Foundry project with customer-managed keys" - }, - "location": { - "value": "[parameters('location')]" - }, - "userAssignedIdentityId": { - "value": "[parameters('userAssignedIdentityId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1706744676486014899" - } - }, - "parameters": { - "aiFoundryName": { - "type": "string", - "metadata": { - "description": "Name of the AI Foundry account (parent)" - } - }, - "location": { - "type": "string", - "metadata": { - "description": "Location for the resource" - } - }, - "projectName": { - "type": "string", - "metadata": { - "description": "Name of the project" - } - }, - "projectDisplayName": { - "type": "string", - "metadata": { - "description": "Display name for the project" - } - }, - "projectDescription": { - "type": "string", - "metadata": { - "description": "Description for the project" - } - }, - "userAssignedIdentityId": { - "type": "string", - "metadata": { - "description": "Resource ID of the user-assigned managed identity" - } - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts/projects", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('aiFoundryName'), parameters('projectName'))]", - "location": "[parameters('location')]", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', parameters('userAssignedIdentityId'))]": {} - } - }, - "properties": { - "displayName": "[parameters('projectDisplayName')]", - "description": "[parameters('projectDescription')]" - } - } - ], - "outputs": { - "projectId": { - "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiFoundryName'), parameters('projectName'))]" - }, - "projectName": { - "type": "string", - "value": "[parameters('projectName')]" - } - } - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Resources/deployments', 'foundryAccount')]", - "[resourceId('Microsoft.Resources/deployments', 'cmkEncryption')]" - ] - } - ], - "outputs": { - "accountId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'foundryAccount'), '2025-04-01').outputs.accountId.value]" - }, - "accountName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'foundryAccount'), '2025-04-01').outputs.accountName.value]" - }, - "projectId": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'foundryProject'), '2025-04-01').outputs.projectId.value]" - }, - "projectName": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'foundryProject'), '2025-04-01').outputs.projectName.value]" - }, - "keyVaultUri": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Resources/deployments', 'cmkEncryption'), '2025-04-01').outputs.keyVaultUri.value]" - } - } -} \ No newline at end of file diff --git a/infrastructure/infrastructure-setup-bicep/40-basic-agent-setup/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/40-basic-agent-setup/azuredeploy.json index aaaa380f0..8d7b06f37 100644 --- a/infrastructure/infrastructure-setup-bicep/40-basic-agent-setup/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/40-basic-agent-setup/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16994587194208622939" + "version": "0.39.26.7824", + "templateHash": "17632891943305566295" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/41-standard-agent-setup/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/41-standard-agent-setup/azuredeploy.json index 76c4fb72a..3449d1659 100644 --- a/infrastructure/infrastructure-setup-bicep/41-standard-agent-setup/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/41-standard-agent-setup/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "6214563059109390518" + "version": "0.39.26.7824", + "templateHash": "17836798406056740836" } }, "parameters": { @@ -187,8 +187,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "4091539512693342510" + "version": "0.39.26.7824", + "templateHash": "10572111282973522005" } }, "parameters": { @@ -314,8 +314,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "949742597496737540" + "version": "0.39.26.7824", + "templateHash": "6621069906435874124" } }, "parameters": { @@ -562,8 +562,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "619228671809139369" + "version": "0.39.26.7824", + "templateHash": "12622173730539075929" } }, "parameters": { @@ -718,8 +718,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1007872875847158534" + "version": "0.39.26.7824", + "templateHash": "5095087340309076800" } }, "parameters": { @@ -892,8 +892,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12606978205101260380" + "version": "0.39.26.7824", + "templateHash": "6910483561575524105" } }, "parameters": { @@ -947,8 +947,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15991712531324316353" + "version": "0.39.26.7824", + "templateHash": "14683840003859985069" } }, "parameters": { @@ -963,7 +963,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('azureStorageName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1004,8 +1004,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11201886913940098363" + "version": "0.39.26.7824", + "templateHash": "2161753938341361575" } }, "parameters": { @@ -1026,7 +1026,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1068,8 +1068,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15867124606695536257" + "version": "0.39.26.7824", + "templateHash": "7968115481508840" } }, "parameters": { @@ -1090,7 +1090,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1101,7 +1101,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1157,8 +1157,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11180853619796693081" + "version": "0.39.26.7824", + "templateHash": "16815875843770432836" } }, "parameters": { @@ -1257,8 +1257,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "890248754515065430" + "version": "0.39.26.7824", + "templateHash": "16535347639253167655" } }, "parameters": { @@ -1288,7 +1288,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageName'))]", "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), parameters('aiProjectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'), parameters('workspaceId'))]", "properties": { "principalId": "[parameters('aiProjectPrincipalId')]", @@ -1336,8 +1336,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "14722474518981746838" + "version": "0.39.26.7824", + "templateHash": "17187611271934567223" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/42-basic-agent-setup-with-customization/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/42-basic-agent-setup-with-customization/azuredeploy.json index 3abb8f3d8..3d2f2bd45 100644 --- a/infrastructure/infrastructure-setup-bicep/42-basic-agent-setup-with-customization/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/42-basic-agent-setup-with-customization/azuredeploy.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12681773111736129347" + "version": "0.39.26.7824", + "templateHash": "14667354563602935622" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/43-standard-agent-setup-with-customization/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/43-standard-agent-setup-with-customization/azuredeploy.json index 888db2440..4ab72e331 100644 --- a/infrastructure/infrastructure-setup-bicep/43-standard-agent-setup-with-customization/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/43-standard-agent-setup-with-customization/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "17570657245063969848" + "version": "0.39.26.7824", + "templateHash": "12453864151003563685" } }, "parameters": { @@ -167,8 +167,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7565081239538254993" + "version": "0.39.26.7824", + "templateHash": "18106522243622185101" } }, "parameters": { @@ -317,8 +317,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "949742597496737540" + "version": "0.39.26.7824", + "templateHash": "6621069906435874124" } }, "parameters": { @@ -550,8 +550,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "4544502123013939487" + "version": "0.39.26.7824", + "templateHash": "9372436613092246432" } }, "parameters": { @@ -684,8 +684,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7242163695078660841" + "version": "0.39.26.7824", + "templateHash": "17727575287719616015" } }, "parameters": { @@ -893,8 +893,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "12606978205101260380" + "version": "0.39.26.7824", + "templateHash": "6910483561575524105" } }, "parameters": { @@ -948,8 +948,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15991712531324316353" + "version": "0.39.26.7824", + "templateHash": "14683840003859985069" } }, "parameters": { @@ -964,7 +964,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('azureStorageName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'), resourceId('Microsoft.Storage/storageAccounts', parameters('azureStorageName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1005,8 +1005,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11201886913940098363" + "version": "0.39.26.7824", + "templateHash": "2161753938341361575" } }, "parameters": { @@ -1027,7 +1027,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName'))]", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1069,8 +1069,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "15867124606695536257" + "version": "0.39.26.7824", + "templateHash": "7968115481508840" } }, "parameters": { @@ -1091,7 +1091,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1102,7 +1102,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Search/searchServices', parameters('aiSearchName'))]", + "scope": "[format('Microsoft.Search/searchServices/{0}', parameters('aiSearchName'))]", "name": "[guid(parameters('projectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", "properties": { "principalId": "[parameters('projectPrincipalId')]", @@ -1164,8 +1164,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16625483348629189251" + "version": "0.39.26.7824", + "templateHash": "8489333536894699406" } }, "parameters": { @@ -1290,8 +1290,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "890248754515065430" + "version": "0.39.26.7824", + "templateHash": "16535347639253167655" } }, "parameters": { @@ -1321,7 +1321,7 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageName'))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageName'))]", "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('storageName')), parameters('aiProjectPrincipalId'), resourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'), parameters('workspaceId'))]", "properties": { "principalId": "[parameters('aiProjectPrincipalId')]", @@ -1369,8 +1369,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "14722474518981746838" + "version": "0.39.26.7824", + "templateHash": "17187611271934567223" } }, "parameters": { diff --git a/infrastructure/infrastructure-setup-bicep/45-basic-agent-bing/azuredeploy.json b/infrastructure/infrastructure-setup-bicep/45-basic-agent-bing/azuredeploy.json index 2fc4cd91f..9c5a0bde1 100644 --- a/infrastructure/infrastructure-setup-bicep/45-basic-agent-bing/azuredeploy.json +++ b/infrastructure/infrastructure-setup-bicep/45-basic-agent-bing/azuredeploy.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "8968481198143661871" + "version": "0.39.26.7824", + "templateHash": "7778551102092775636" } }, "parameters": { @@ -97,8 +97,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "9662938821485102145" + "version": "0.39.26.7824", + "templateHash": "18028369509636259517" } }, "parameters": { @@ -218,8 +218,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "1170667204161260016" + "version": "0.39.26.7824", + "templateHash": "9956949102179771191" } }, "parameters": { @@ -294,8 +294,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "2322306944964107716" + "version": "0.39.26.7824", + "templateHash": "1420647491421234827" } }, "parameters": { diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/AgentThreadAndHITL.csproj b/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/AgentThreadAndHITL.csproj index dce28b582..c438e5e08 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/AgentThreadAndHITL.csproj +++ b/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/AgentThreadAndHITL.csproj @@ -12,10 +12,10 @@ - - + + - - + + diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/Program.cs b/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/Program.cs index 43a4c2967..d356d491e 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/Program.cs +++ b/samples/csharp/hosted-agents/AgentFramework/AgentThreadAndHITL/Program.cs @@ -8,6 +8,7 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using OpenAI; +using OpenAI.Chat; namespace AgengThreadAndHITL; @@ -31,7 +32,7 @@ static string GetWeather([Description("The location to get the weather for.")] s new Uri(endpoint), new AzureCliCredential()) .GetChatClient(deploymentName) - .CreateAIAgent( + .AsAIAgent( instructions: "You are a helpful assistant", tools: [new ApprovalRequiredAIFunction(AIFunctionFactory.Create(GetWeather))] ); diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/AgentWithHostedMCP.csproj b/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/AgentWithHostedMCP.csproj index ba80d2c1b..dfba6f51b 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/AgentWithHostedMCP.csproj +++ b/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/AgentWithHostedMCP.csproj @@ -11,11 +11,11 @@ - - + + - - + + diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/Program.cs b/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/Program.cs index 9cbea8b73..3a8908876 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/Program.cs +++ b/samples/csharp/hosted-agents/AgentFramework/AgentWithHostedMCP/Program.cs @@ -10,6 +10,7 @@ using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using OpenAI; +using OpenAI.Responses; var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; @@ -25,8 +26,8 @@ AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetOpenAIResponseClient(deploymentName) - .CreateAIAgent( + .GetResponsesClient(deploymentName) + .AsAIAgent( instructions: "You answer questions by searching the Microsoft Learn content only.", name: "MicrosoftLearnAgent", tools: [mcpTool]); diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentWithLocalTools/AgentWithLocalTools.csproj b/samples/csharp/hosted-agents/AgentFramework/AgentWithLocalTools/AgentWithLocalTools.csproj index 8a17c304d..8600536a6 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentWithLocalTools/AgentWithLocalTools.csproj +++ b/samples/csharp/hosted-agents/AgentFramework/AgentWithLocalTools/AgentWithLocalTools.csproj @@ -12,6 +12,6 @@ - + diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj b/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj index 40c9a2544..ff2c1ca0c 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj +++ b/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj @@ -9,11 +9,11 @@ - - + + - - + + diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/Program.cs b/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/Program.cs index 94552a801..c3389988f 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/Program.cs +++ b/samples/csharp/hosted-agents/AgentFramework/AgentWithTextSearchRag/Program.cs @@ -8,9 +8,8 @@ using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; -using Microsoft.Agents.AI.Data; using Microsoft.Extensions.AI; -using OpenAI; +using OpenAI.Chat; var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set."); var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; @@ -26,10 +25,13 @@ new Uri(endpoint), new DefaultAzureCredential()) .GetChatClient(deploymentName) - .CreateAIAgent(new ChatClientAgentOptions + .AsAIAgent(new ChatClientAgentOptions { - Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.", - AIContextProviderFactory = ctx => new TextSearchProvider(MockSearchAsync, ctx.SerializedState, ctx.JsonSerializerOptions, textSearchOptions) + ChatOptions = new ChatOptions + { + Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.", + }, + AIContextProviders = [new TextSearchProvider(MockSearchAsync, textSearchOptions)] }); await agent.RunAIAgentAsync(); diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentWithTools/AgentWithTools.csproj b/samples/csharp/hosted-agents/AgentFramework/AgentWithTools/AgentWithTools.csproj index 85c5105fd..048213625 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentWithTools/AgentWithTools.csproj +++ b/samples/csharp/hosted-agents/AgentFramework/AgentWithTools/AgentWithTools.csproj @@ -8,10 +8,10 @@ - - + + - + diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/AgentsInWorkflows.csproj b/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/AgentsInWorkflows.csproj index a25b0b6b0..f6184dee0 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/AgentsInWorkflows.csproj +++ b/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/AgentsInWorkflows.csproj @@ -9,11 +9,11 @@ - + - - + + diff --git a/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/Program.cs b/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/Program.cs index 279d1de21..ede01fa33 100644 --- a/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/Program.cs +++ b/samples/csharp/hosted-agents/AgentFramework/AgentsInWorkflows/Program.cs @@ -32,7 +32,7 @@ .AddEdge(frenchAgent, spanishAgent) .AddEdge(spanishAgent, englishAgent) .Build() - .AsAgent(); + .AsAIAgent(); return Task.FromResult(agent); }; diff --git a/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/.dockerignore b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/.dockerignore new file mode 100644 index 000000000..6bfa65a8f --- /dev/null +++ b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/.dockerignore @@ -0,0 +1,57 @@ +# Build outputs +bin/ +obj/ +out/ + +# IDE and editor files +.vs/ +.vscode/ +*.user +*.suo +*.sln.docstates +.foundry/ + +# Git +.git/ +.gitignore + +# Documentation and samples (not needed in container) +*.md +*.http + +# Ignore files +.dockerignore + +# Logs +*.log + +# Temporary files +*.tmp +*.temp + +# OS files +.DS_Store +Thumbs.db + +# Package manager directories +node_modules/ +packages/ + +# Test results +TestResults/ +*.trx + +# Coverage reports +coverage/ +*.coverage +*.coveragexml + +# Environment files with secrets +.env +.env.* +*.local +appsettings.*.json +!appsettings.json + +.venv/ +__pycache__/ diff --git a/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/AzureAIAgentsInWorkflow.csproj b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/AzureAIAgentsInWorkflow.csproj new file mode 100644 index 000000000..dd6ab3c5f --- /dev/null +++ b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/AzureAIAgentsInWorkflow.csproj @@ -0,0 +1,19 @@ + + + Exe + net10.0 + enable + enable + true + + + + + + + + + + + + diff --git a/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/Dockerfile b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/Dockerfile new file mode 100644 index 000000000..102e36e08 --- /dev/null +++ b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/Dockerfile @@ -0,0 +1,20 @@ +# Build the application +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build +WORKDIR /src + +# Copy files from the current directory on the host to the working directory in the container +COPY . . + +RUN dotnet restore +RUN dotnet build -c Release --no-restore +RUN dotnet publish -c Release --no-build -o /app -f net10.0 + +# Run the application +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final +WORKDIR /app + +# Copy everything needed to run the app from the "build" stage. +COPY --from=build /app . + +EXPOSE 8088 +ENTRYPOINT ["dotnet", "AzureAIAgentsInWorkflow.dll"] diff --git a/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/Program.cs b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/Program.cs new file mode 100644 index 000000000..c80b373e7 --- /dev/null +++ b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/Program.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates a multi-agent workflow with Writer and Reviewer agents +// using Azure AI Foundry AIProjectClient and the Agent Framework WorkflowBuilder. + +using Azure.AI.AgentServer.AgentFramework.Extensions; +using Azure.AI.Projects; +using Azure.Identity; +using Microsoft.Agents.AI; +using Microsoft.Agents.AI.Workflows; + +var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") + ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set."); +var deploymentName = Environment.GetEnvironmentVariable("MODEL_DEPLOYMENT_NAME") ?? "gpt-4o-mini"; + +Console.WriteLine($"Using Azure AI endpoint: {endpoint}"); +Console.WriteLine($"Using model deployment: {deploymentName}"); + +// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production. +// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid +// latency issues, unintended credential probing, and potential security risks from fallback mechanisms. +AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential()); + +// Create Foundry agents +AIAgent writerAgent = await aiProjectClient.CreateAIAgentAsync( + name: "Writer", + model: deploymentName, + instructions: "You are an excellent content writer. You create new content and edit contents based on the feedback."); + +AIAgent reviewerAgent = await aiProjectClient.CreateAIAgentAsync( + name: "Reviewer", + model: deploymentName, + instructions: "You are an excellent content reviewer. Provide actionable feedback to the writer about the provided content. Provide the feedback in the most concise manner possible."); + +try +{ + var workflow = new WorkflowBuilder(writerAgent) + .AddEdge(writerAgent, reviewerAgent) + .Build(); + + Console.WriteLine("Starting Writer-Reviewer Workflow Agent Server on http://localhost:8088"); + await workflow.AsAgent().RunAIAgentAsync(); +} +finally +{ + // Cleanup server-side agents + await aiProjectClient.Agents.DeleteAgentAsync(writerAgent.Name); + await aiProjectClient.Agents.DeleteAgentAsync(reviewerAgent.Name); +} \ No newline at end of file diff --git a/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/agent.yaml b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/agent.yaml new file mode 100644 index 000000000..283c1a194 --- /dev/null +++ b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/agent.yaml @@ -0,0 +1,28 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml + +kind: hosted +name: AzureAIAgentsInWorkflow +description: > + A multi-agent workflow featuring a Writer and Reviewer that collaborate + to create and refine content. +metadata: + authors: + - Microsoft + tags: + - Azure AI AgentServer + - Microsoft Agent Framework + - Multi-Agent Workflow + - Writer-Reviewer + - Content Creation +protocols: + - protocol: responses + version: v1 +environment_variables: + - name: AZURE_AI_PROJECT_ENDPOINT + value: ${AZURE_OPENAI_ENDPOINT} + - name: MODEL_DEPLOYMENT_NAME + value: "{{chat}}" +resources: + - name: chat + kind: model + id: gpt-4o-mini diff --git a/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/run-requests.http b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/run-requests.http new file mode 100644 index 000000000..2fcdb2499 --- /dev/null +++ b/samples/csharp/hosted-agents/AgentFramework/AzureAIAgentsInWorkflow/run-requests.http @@ -0,0 +1,34 @@ +@host = http://localhost:8088 +@endpoint = {{host}}/responses + +### Health Check +GET {{host}}/readiness + +### Simple string input - Content creation request +POST {{endpoint}} +Content-Type: application/json + +{ + "input": "Create a slogan for a new electric SUV that is affordable and fun to drive", + "stream": false +} + +### Explicit input format +POST {{endpoint}} +Content-Type: application/json + +{ + "input": [ + { + "type": "message", + "role": "user", + "content": [ + { + "type": "input_text", + "text": "Write a short product description for a smart water bottle that tracks hydration" + } + ] + } + ], + "stream": false +} diff --git a/samples/csharp/hosted-agents/AgentWithCustomFramework/SystemUtilityAgent/SystemUtilityAgent.csproj b/samples/csharp/hosted-agents/AgentWithCustomFramework/SystemUtilityAgent/SystemUtilityAgent.csproj index 4d8f573c6..f46080b1d 100644 --- a/samples/csharp/hosted-agents/AgentWithCustomFramework/SystemUtilityAgent/SystemUtilityAgent.csproj +++ b/samples/csharp/hosted-agents/AgentWithCustomFramework/SystemUtilityAgent/SystemUtilityAgent.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/.dockerignore b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/.dockerignore new file mode 100644 index 000000000..79cc80773 --- /dev/null +++ b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/.dockerignore @@ -0,0 +1,51 @@ +# Build artifacts +bin/ +obj/ + +# IDE and editor files +.vs/ +.vscode/ +*.user +*.suo +.foundry/ + +# Source control +.git/ + +# Documentation +README.md + +# Ignore files +.gitignore +.dockerignore + +# Logs +*.log + +# Temporary files +*.tmp +*.temp + +# OS files +.DS_Store +Thumbs.db + +# Package manager directories +node_modules/ +packages/ + +# Test results +TestResults/ +*.trx + +# Coverage reports +coverage/ +*.coverage +*.coveragexml + +# Local development config +appsettings.Development.json +.env + +.venv/ +__pycache__/ diff --git a/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/.env.sample b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/.env.sample new file mode 100644 index 000000000..7a7d4d5ec --- /dev/null +++ b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/.env.sample @@ -0,0 +1,3 @@ +# IMPORTANT: Never commit .env to version control - add it to .gitignore +PROJECT_ENDPOINT= +MODEL_DEPLOYMENT_NAME= \ No newline at end of file diff --git a/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/Dockerfile b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/Dockerfile new file mode 100644 index 000000000..0cc939d9b --- /dev/null +++ b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY . user_agent/ +WORKDIR /app/user_agent + +RUN if [ -f requirements.txt ]; then \ + pip install -r requirements.txt; \ + else \ + echo "No requirements.txt found"; \ + fi + +EXPOSE 8088 + +CMD ["python", "main.py"] diff --git a/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/agent.yaml b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/agent.yaml new file mode 100644 index 000000000..5734170d8 --- /dev/null +++ b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/agent.yaml @@ -0,0 +1,31 @@ +# Unique identifier/name for this agent +name: azure-ai-agents-in-workflow +# Brief description of what this agent does +description: > + A multi-agent workflow featuring a Writer and Reviewer that collaborate + to create and refine content. +metadata: + # Categorization tags for organizing and discovering agents + authors: + - Microsoft Agent Framework Team + tags: + - Azure AI AgentServer + - Microsoft Agent Framework + - Multi-Agent Workflow + - Writer-Reviewer + - Content Creation +template: + name: azure-ai-agents-in-workflow + # The type of agent - "hosted" for HOBO, "container" for COBO + kind: hosted + protocols: + - protocol: responses + environment_variables: + - name: PROJECT_ENDPOINT + value: ${PROJECT_ENDPOINT} + - name: MODEL_DEPLOYMENT_NAME + value: "{{chat}}" +resources: + - kind: model + id: gpt-4.1-mini + name: chat diff --git a/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/main.py b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/main.py new file mode 100644 index 000000000..98d604d45 --- /dev/null +++ b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/main.py @@ -0,0 +1,85 @@ +import asyncio +import os +from contextlib import asynccontextmanager + +from agent_framework import WorkflowBuilder +from agent_framework.azure import AzureAIAgentClient +from azure.ai.agentserver.agentframework import from_agent_framework +from azure.identity.aio import DefaultAzureCredential, ManagedIdentityCredential +from dotenv import load_dotenv + +load_dotenv(override=True) + +# Configure these for your Foundry project +# Read the explicit variables present in the .env file +PROJECT_ENDPOINT = os.getenv( + "PROJECT_ENDPOINT" +) # e.g., "https://.services.ai.azure.com/api/projects/" +MODEL_DEPLOYMENT_NAME = os.getenv( + "MODEL_DEPLOYMENT_NAME", "gpt-4.1-mini" +) # Your model deployment name e.g., "gpt-4.1-mini" + + +def get_credential(): + """Will use Managed Identity when running in Azure, otherwise falls back to DefaultAzureCredential.""" + return ( + ManagedIdentityCredential() + if os.getenv("MSI_ENDPOINT") + else DefaultAzureCredential() + ) + + +@asynccontextmanager +async def create_agents(): + async with ( + get_credential() as credential, + AzureAIAgentClient( + project_endpoint=PROJECT_ENDPOINT, + model_deployment_name=MODEL_DEPLOYMENT_NAME, + credential=credential, + ) as writer_client, + AzureAIAgentClient( + project_endpoint=PROJECT_ENDPOINT, + model_deployment_name=MODEL_DEPLOYMENT_NAME, + credential=credential, + ) as reviewer_client, + ): + writer = writer_client.create_agent( + name="Writer", + instructions="You are an excellent content writer. You create new content and edit contents based on the feedback.", + ) + reviewer = reviewer_client.create_agent( + name="Reviewer", + instructions="You are an excellent content reviewer. Provide actionable feedback to the writer about the provided content in the most concise manner possible.", + ) + yield writer, reviewer + + +def create_workflow(writer, reviewer): + workflow = ( + WorkflowBuilder(name="Writer-Reviewer") + .register_agent(lambda: writer, name="Writer", output_response=True) + .register_agent(lambda: reviewer, name="Reviewer", output_response=True) + .set_start_executor("Writer") + .add_edge("Writer", "Reviewer") + .build() + ) + return workflow.as_agent() + + +async def main() -> None: + """ + The writer and reviewer multi-agent workflow. + + Environment variables required: + - PROJECT_ENDPOINT: Your Microsoft Foundry project endpoint + - MODEL_DEPLOYMENT_NAME: Your Microsoft Foundry model deployment name + """ + + async with create_agents() as (writer, reviewer): + agent = create_workflow(writer, reviewer) + await from_agent_framework(agent).run_async() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/requirements.txt b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/requirements.txt new file mode 100644 index 000000000..3d190f704 --- /dev/null +++ b/samples/python/hosted-agents/agent-framework/azure-ai-agents-in-workflow/requirements.txt @@ -0,0 +1 @@ +azure-ai-agentserver-agentframework==1.0.0b15 \ No newline at end of file