From b03b4e9d3ef449ace2ea7249c7665df23847686c Mon Sep 17 00:00:00 2001 From: "Gonam Lakshmi Vasanthi (Centific Technologies Inc)" Date: Thu, 20 Feb 2025 15:47:43 +0530 Subject: [PATCH] Added missing dependent files in the modules used in bicep files --- .../basic-ai-hub-keys.bicep | 68 ++++ .../basic-ai-project-keys.bicep | 42 +++ .../basic-dependent-resources-keys.bicep | 134 +++++++ .../ai-search-role-assignments.bicep | 41 ++ .../ai-service-role-assignments.bicep | 56 +++ .../network-secured-ai-hub.bicep | 139 +++++++ .../network-secured-ai-project.bicep | 89 +++++ .../network-secured-dependent-resources.bicep | 357 ++++++++++++++++++ .../network-secured-identity.bicep | 18 + .../private-endpoint-and-dns.bicep | 187 +++++++++ .../ai-search-role-assignments.bicep | 41 ++ .../ai-service-role-assignments.bicep | 56 +++ .../modules-standard/standard-ai-hub.bicep | 119 ++++++ .../standard-ai-project.bicep | 78 ++++ .../standard-dependent-resources.bicep | 196 ++++++++++ 15 files changed, 1621 insertions(+) create mode 100644 scenarios/Agents/modules-basic-keys/basic-ai-hub-keys.bicep create mode 100644 scenarios/Agents/modules-basic-keys/basic-ai-project-keys.bicep create mode 100644 scenarios/Agents/modules-basic-keys/basic-dependent-resources-keys.bicep create mode 100644 scenarios/Agents/modules-network-secured/ai-search-role-assignments.bicep create mode 100644 scenarios/Agents/modules-network-secured/ai-service-role-assignments.bicep create mode 100644 scenarios/Agents/modules-network-secured/network-secured-ai-hub.bicep create mode 100644 scenarios/Agents/modules-network-secured/network-secured-ai-project.bicep create mode 100644 scenarios/Agents/modules-network-secured/network-secured-dependent-resources.bicep create mode 100644 scenarios/Agents/modules-network-secured/network-secured-identity.bicep create mode 100644 scenarios/Agents/modules-network-secured/private-endpoint-and-dns.bicep create mode 100644 scenarios/Agents/modules-standard/ai-search-role-assignments.bicep create mode 100644 scenarios/Agents/modules-standard/ai-service-role-assignments.bicep create mode 100644 scenarios/Agents/modules-standard/standard-ai-hub.bicep create mode 100644 scenarios/Agents/modules-standard/standard-ai-project.bicep create mode 100644 scenarios/Agents/modules-standard/standard-dependent-resources.bicep diff --git a/scenarios/Agents/modules-basic-keys/basic-ai-hub-keys.bicep b/scenarios/Agents/modules-basic-keys/basic-ai-hub-keys.bicep new file mode 100644 index 00000000..d848c457 --- /dev/null +++ b/scenarios/Agents/modules-basic-keys/basic-ai-hub-keys.bicep @@ -0,0 +1,68 @@ +// Creates an Azure AI resource with proxied endpoints for the Azure AI services provider + +@description('Azure region of the deployment') +param location string + +@description('Tags to add to the resources') +param tags object + +@description('AI hub name') +param aiHubName string + +@description('AI hub display name') +param aiHubFriendlyName string = aiHubName + +@description('AI hub description') +param aiHubDescription string + +@description('Resource ID of the storage account resource for storing experimentation outputs') +param storageAccountId string + +@description('Resource ID of the AI Services resource') +param aiServicesId string + +@description('Resource ID of the AI Services endpoint') +param aiServicesTarget string + +@description('Model/AI Resource deployment location') +param modelLocation string + +resource aiHub 'Microsoft.MachineLearningServices/workspaces@2024-07-01-preview' = { + name: aiHubName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + // organization + friendlyName: aiHubFriendlyName + description: aiHubDescription + + // dependent resources + storageAccount: storageAccountId + } + kind: 'hub' + + resource aiServicesConnection 'connections@2024-07-01-preview' = { + name: '${aiHubName}-connection-AIServices' + properties: { + category: 'AIServices' + target: aiServicesTarget + // useWorkspaceManagedIdentity: true + authType: 'ApiKey' + isSharedToAll: true + credentials: { + key: '${listKeys(aiServicesId, '2022-10-01').key1}' + } + metadata: { + ApiType: 'Azure' + ResourceId: aiServicesId + Location: modelLocation + } + } + } +} + + +output aiHubID string = aiHub.id diff --git a/scenarios/Agents/modules-basic-keys/basic-ai-project-keys.bicep b/scenarios/Agents/modules-basic-keys/basic-ai-project-keys.bicep new file mode 100644 index 00000000..764ca201 --- /dev/null +++ b/scenarios/Agents/modules-basic-keys/basic-ai-project-keys.bicep @@ -0,0 +1,42 @@ +// Creates an Azure AI resource with proxied endpoints for the Azure AI services provider + +@description('Azure region of the deployment') +param location string + +@description('Tags to add to the resources') +param tags object + +@description('AI Project name') +param aiProjectName string + +@description('AI Project display name') +param aiProjectFriendlyName string = aiProjectName + +@description('AI Project description') +param aiProjectDescription string + +@description('Resource ID of the AI Hub resource') +param aiHubId string + + + +resource aiProject 'Microsoft.MachineLearningServices/workspaces@2023-08-01-preview' = { + name: aiProjectName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + // organization + friendlyName: aiProjectFriendlyName + description: aiProjectDescription + + // dependent resources + hubResourceId: aiHubId + + } + kind: 'project' +} + +output aiProjectID string = aiProject.id diff --git a/scenarios/Agents/modules-basic-keys/basic-dependent-resources-keys.bicep b/scenarios/Agents/modules-basic-keys/basic-dependent-resources-keys.bicep new file mode 100644 index 00000000..e048f685 --- /dev/null +++ b/scenarios/Agents/modules-basic-keys/basic-dependent-resources-keys.bicep @@ -0,0 +1,134 @@ +@description('Azure region of the deployment') +param location string = resourceGroup().location + +@description('Tags to add to the resources') +param tags object = {} + +@description('AI services name') +param aiServicesName string + +@description('Model name for deployment') +param modelName string + +@description('Model format for deployment') +param modelFormat string + +@description('Model version for deployment') +param modelVersion string + +@description('Model deployment SKU name') +param modelSkuName string + +@description('Model deployment capacity') +param modelCapacity int + +@description('Model/AI Resource deployment location') +param modelLocation string + +resource aiServices 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' = { + name: aiServicesName + location: modelLocation + sku: { + name: 'S0' + } + kind: 'AIServices' // or 'OpenAI' + identity: { + type: 'SystemAssigned' + } + properties: { + customSubDomainName: toLower('${toLower(aiServicesName)}') + apiProperties: { + statisticsEnabled: false + } + publicNetworkAccess: 'Enabled' + } +} + +resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-06-01-preview'= { + parent: aiServices + name: modelName + sku : { + capacity: modelCapacity + name: modelSkuName + } + properties: { + model:{ + name: modelName + format: modelFormat + version: modelVersion + } + } +} + +@description('Name of the storage account') +param storageName string + +@allowed([ + 'Standard_LRS' + 'Standard_ZRS' + 'Standard_GRS' + 'Standard_GZRS' + 'Standard_RAGRS' + 'Standard_RAGZRS' + 'Premium_LRS' + 'Premium_ZRS' +]) + +@description('Storage SKU') +param storageSkuName string = 'Standard_LRS' + +var storageNameCleaned = replace(storageName, '-', '') + +resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = { + name: storageNameCleaned + location: location + tags: tags + sku: { + name: storageSkuName + } + kind: 'StorageV2' + properties: { + accessTier: 'Hot' + allowBlobPublicAccess: false + allowCrossTenantReplication: false + allowSharedKeyAccess: true + encryption: { + keySource: 'Microsoft.Storage' + requireInfrastructureEncryption: false + services: { + blob: { + enabled: true + keyType: 'Account' + } + file: { + enabled: true + keyType: 'Account' + } + queue: { + enabled: true + keyType: 'Service' + } + table: { + enabled: true + keyType: 'Service' + } + } + } + isHnsEnabled: false + isNfsV3Enabled: false + keyPolicy: { + keyExpirationPeriodInDays: 7 + } + largeFileSharesState: 'Disabled' + minimumTlsVersion: 'TLS1_2' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } + supportsHttpsTrafficOnly: true + } +} + +output aiservicesID string = aiServices.id +output aiservicesTarget string = aiServices.properties.endpoint +output storageId string = storage.id diff --git a/scenarios/Agents/modules-network-secured/ai-search-role-assignments.bicep b/scenarios/Agents/modules-network-secured/ai-search-role-assignments.bicep new file mode 100644 index 00000000..326d5fee --- /dev/null +++ b/scenarios/Agents/modules-network-secured/ai-search-role-assignments.bicep @@ -0,0 +1,41 @@ +// Assigns the necessary roles to the AI project + +param aiSearchName string +param aiProjectPrincipalId string +param aiProjectId 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(aiProjectId, searchIndexDataContributorRole.id, searchService.id) + properties: { + principalId: aiProjectPrincipalId + 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(aiProjectId, searchServiceContributorRole.id, searchService.id) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: searchServiceContributorRole.id + principalType: 'ServicePrincipal' + } +} diff --git a/scenarios/Agents/modules-network-secured/ai-service-role-assignments.bicep b/scenarios/Agents/modules-network-secured/ai-service-role-assignments.bicep new file mode 100644 index 00000000..f22ad548 --- /dev/null +++ b/scenarios/Agents/modules-network-secured/ai-service-role-assignments.bicep @@ -0,0 +1,56 @@ +// Assigns the necessary roles to the AI project + +param aiServicesName string +param aiProjectPrincipalId string +param aiProjectId string + +resource aiServices 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' existing = { + name: aiServicesName + scope: resourceGroup() +} + +resource cognitiveServicesContributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + scope: resourceGroup() + +} + +resource cognitiveServicesContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01'= { + scope: aiServices + name: guid(aiServices.id, cognitiveServicesContributorRole.id, aiProjectId) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: cognitiveServicesContributorRole.id + principalType: 'ServicePrincipal' + } +} + + +resource cognitiveServicesOpenAIUserRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + scope: resourceGroup() +} +resource cognitiveServicesOpenAIUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aiServices + name: guid(aiProjectId, cognitiveServicesOpenAIUserRole.id, aiServices.id) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: cognitiveServicesOpenAIUserRole.id + principalType: 'ServicePrincipal' + } +} + +resource cognitiveServicesUserRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: 'a97b65f3-24c7-4388-baec-2e87135dc908' + scope: resourceGroup() +} + +resource cognitiveServicesUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aiServices + name: guid(aiProjectId, cognitiveServicesUserRole.id, aiServices.id) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: cognitiveServicesUserRole.id + principalType: 'ServicePrincipal' + } +} diff --git a/scenarios/Agents/modules-network-secured/network-secured-ai-hub.bicep b/scenarios/Agents/modules-network-secured/network-secured-ai-hub.bicep new file mode 100644 index 00000000..e34085c5 --- /dev/null +++ b/scenarios/Agents/modules-network-secured/network-secured-ai-hub.bicep @@ -0,0 +1,139 @@ +// Creates an Azure AI resource with proxied endpoints for the Azure AI services provider + +@description('Azure region of the deployment') +param location string + +@description('Tags to add to the resources') +param tags object + +@description('AI hub name') +param aiHubName string + +@description('AI hub display name') +param aiHubFriendlyName string = aiHubName + +@description('AI hub description') +param aiHubDescription string + +@description('Resource ID of the key vault resource for storing connection strings') +param keyVaultId string + +@description('Resource ID of the storage account resource for storing experimentation outputs') +param storageAccountId string + +@description('Resource ID of the AI Services resource') +param aiServicesId string +@description('Resource ID of the AI Services endpoint') +param aiServicesTarget string +@description('Name AI Services resource') +param aiServicesName string +@description('Resource Group name of the AI Services resource') +param aiServiceAccountResourceGroupName string +@description('Subscription ID of the AI Services resource') +param aiServiceAccountSubscriptionId string + +@description('Name AI Search resource') +param aiSearchName string +@description('Resource ID of the AI Search resource') +param aiSearchId string +param aiSearchServiceResourceGroupName string +param aiSearchServiceSubscriptionId string +@description('Name for capabilityHost.') +param capabilityHostName string = 'caphost1' + +param uaiName string + +param subnetId string + +param aiHubExists bool = false + +var acsConnectionName = '${aiHubName}-connection-AISearch' + +var aoaiConnection = '${aiHubName}-connection-AIServices_aoai' +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = { + name: uaiName +} +var userAssignedIdentities = json('{\'${userAssignedIdentity.id}\': {}}') + +resource aiServices 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: aiServicesName + scope: resourceGroup(aiServiceAccountSubscriptionId, aiServiceAccountResourceGroupName) +} +resource searchService 'Microsoft.Search/searchServices@2023-11-01' existing = { + name: aiSearchName + scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) +} + + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces?tabs=bicep +resource aiHub 'Microsoft.MachineLearningServices/workspaces@2024-10-01-preview' = if(!aiHubExists) { + name: aiHubName + location: location + kind: 'hub' + tags: tags + identity: { + type: 'UserAssigned' + userAssignedIdentities: userAssignedIdentities + } + properties: { + // organization + friendlyName: aiHubFriendlyName + description: aiHubDescription + primaryUserAssignedIdentity: userAssignedIdentity.id + + // dependent resources + //allowPublicAccessWhenBehindVnet: false + keyVault: keyVaultId + storageAccount: storageAccountId + } + + + // Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces/connections?tabs=bicep + resource aiServicesConnection 'connections@2024-07-01-preview' = { + name: '${aiHubName}-connection-AIServices' + properties: { + category: 'AIServices' + target: aiServicesTarget + authType: 'AAD' + isSharedToAll: true + metadata: { + ApiType: 'Azure' + ResourceId: aiServicesId + location: aiServices.location + } + } + } + resource hub_connection_azureai_search 'connections@2024-07-01-preview' = { + name: acsConnectionName + properties: { + category: 'CognitiveSearch' + target: 'https://${aiSearchName}.search.windows.net' + authType: 'AAD' + isSharedToAll: true + metadata: { + ApiType: 'Azure' + ResourceId: aiSearchId + location: searchService.location + } + } + } + + // Resource definition for the capability host + // Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces/capabilityhosts?tabs=bicep + resource capabilityHost 'capabilityHosts@2024-10-01-preview' = { + name: capabilityHostName + properties: { + customerSubnet: subnetId + capabilityHostKind: 'Agents' + } + } + dependsOn: [ + userAssignedIdentity + aiServices + //searchService + ] +} + +output aiHubID string = aiHub.id +output aoaiConnectionName string = aoaiConnection +output acsConnectionName string = acsConnectionName diff --git a/scenarios/Agents/modules-network-secured/network-secured-ai-project.bicep b/scenarios/Agents/modules-network-secured/network-secured-ai-project.bicep new file mode 100644 index 00000000..c62453ac --- /dev/null +++ b/scenarios/Agents/modules-network-secured/network-secured-ai-project.bicep @@ -0,0 +1,89 @@ +// Creates an Azure AI resource with proxied endpoints for the Azure AI services provider + +@description('Azure region of the deployment') +param location string + +@description('Tags to add to the resources') +param tags object + +@description('AI Project name') +param aiProjectName string + +@description('AI Project display name') +param aiProjectFriendlyName string = aiProjectName + +@description('AI Project description') +param aiProjectDescription string + +@description('Resource ID of the AI Hub resource') +param aiHubId string + +@description('Name for capabilityHost.') +param capabilityHostName string = 'caphost1' + +@description('Name for ACS connection.') +param acsConnectionName string + +@description('Name for ACS connection.') +param aoaiConnectionName string + +param uaiName string + +//for constructing endpoint +var subscriptionId = subscription().subscriptionId +var resourceGroupName = resourceGroup().name + +var projectConnectionString = '${location}.api.azureml.ms;${subscriptionId};${resourceGroupName};${aiProjectName}' + + +var storageConnections = ['${aiProjectName}/workspaceblobstore'] +var aiSearchConnection = ['${acsConnectionName}'] +var aiServiceConnections = ['${aoaiConnectionName}'] + +resource uai 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' existing = { + name: uaiName + scope: resourceGroup() +} + + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces?tabs=bicep +resource aiProject 'Microsoft.MachineLearningServices/workspaces@2023-08-01-preview' = { + name: aiProjectName + location: location + tags: union(tags, { + ProjectConnectionString: projectConnectionString + }) + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${uai.id}': {} + } + } + properties: { + // organization + friendlyName: aiProjectFriendlyName + description: aiProjectDescription + primaryUserAssignedIdentity: uai.id + + // dependent resources + hubResourceId: aiHubId + } + kind: 'project' + + // Resource definition for the capability host + // Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.machinelearningservices/workspaces/capabilityhosts?tabs=bicep + resource capabilityHost 'capabilityHosts@2024-10-01-preview' = { + name: '${aiProjectName}-${capabilityHostName}' + properties: { + capabilityHostKind: 'Agents' + aiServicesConnections: aiServiceConnections + vectorStoreConnections: aiSearchConnection + storageConnections: storageConnections + } + } +} + +output aiProjectName string = aiProject.name +output aiProjectResourceId string = aiProject.id +output aiProjectWorkspaceId string = aiProject.properties.workspaceId +output projectConnectionString string = aiProject.tags.ProjectConnectionString diff --git a/scenarios/Agents/modules-network-secured/network-secured-dependent-resources.bicep b/scenarios/Agents/modules-network-secured/network-secured-dependent-resources.bicep new file mode 100644 index 00000000..e6cc2501 --- /dev/null +++ b/scenarios/Agents/modules-network-secured/network-secured-dependent-resources.bicep @@ -0,0 +1,357 @@ +// Creates Azure dependent resources for Azure AI studio + +param storageExists bool = false +param keyvaultExists bool = false +param aiServicesExists bool = false +param aiSearchExists bool = false + +@description('Azure region of the deployment') +param location string = resourceGroup().location + +@description('Tags to add to the resources') +param tags object = {} + +@description('Tags to add to the resources') +param suffix string + +@description('AI services name') +param aiServicesName 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 storageName string + +@description('Model name for deployment') +param modelName string + +@description('Model format for deployment') +param modelFormat string + +@description('Model version for deployment') +param modelVersion string + +@description('Model deployment SKU name') +param modelSkuName string + +@description('Model deployment capacity') +param modelCapacity int + +@description('Model/AI Resource deployment location') +param modelLocation string + +@description('The name of the virtual network') +param vnetName string = 'agents-vnet-${suffix}' + +@description('The name of Agents Subnet') +param agentsSubnetName string = 'agents-subnet-${suffix}' + +@description('The name of Customer Hub subnet') +param cxSubnetName string = 'hub-subnet-${suffix}' + +param userAssignedIdentityName string + +var cxSubnetRef = resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, cxSubnetName) +var agentSubnetRef = resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, agentsSubnetName) + +resource uai 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = { + location: location + name: userAssignedIdentityName +} + +/* -------------------------------------------- Create VNet Resources -------------------------------------------- */ + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.network/virtualnetworks?pivots=deployment-language-bicep +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-05-01' = { + name: vnetName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + '172.16.0.0/16' + ] + } + subnets: [ + { + name: cxSubnetName + properties: { + addressPrefix: '172.16.0.0/24' + serviceEndpoints: [ + { + service: 'Microsoft.KeyVault' + locations: [ + location + ] + } + { + service: 'Microsoft.Storage' + locations: [ + location + ] + } + { + service: 'Microsoft.CognitiveServices' + locations: [ + modelLocation + ] + } + ] + } + } + { + name: agentsSubnetName + properties: { + addressPrefix: '172.16.101.0/24' + delegations: [ + { + name: 'Microsoft.app/environments' + properties: { + serviceName: 'Microsoft.app/environments' + } + } + ] + } + } + ] + } + dependsOn: [ + uai + ] +} + +/* -------------------------------------------- Fetch Existing Resources And Connect to Vnet -------------------------------------------- */ +resource existingStorage 'Microsoft.Storage/storageAccounts@2022-05-01' existing = if(storageExists) { + name: storageName + scope: resourceGroup() +} + +resource existingKeyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if(keyvaultExists) { + name: keyvaultName + scope: resourceGroup() +} +resource existingAiServices 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' existing = if(aiServicesExists) { + name: aiServicesName + scope: resourceGroup() +} + +resource existingAiSearch 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' existing = if(aiSearchExists) { + name: aiSearchName + scope: resourceGroup() +} + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.keyvault/vaults?pivots=deployment-language-bicep +resource defaultKeyVault 'Microsoft.KeyVault/vaults@2022-07-01' = if(!keyvaultExists) { + name: keyvaultName + location: location + tags: tags + properties: { + createMode: 'default' + enabledForDeployment: true + enabledForDiskEncryption: false + enabledForTemplateDeployment: true + enableSoftDelete: true + enableRbacAuthorization: true + enablePurgeProtection: true + publicNetworkAccess: 'Disabled' + accessPolicies: [ + { + tenantId: subscription().tenantId + objectId: uai.properties.principalId + permissions: { secrets: [ 'set', 'get', 'list', 'delete', 'purge' ] } + } + ] + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules:[ + { + id: virtualNetwork.properties.subnets[0].id + } + ] + } + sku: { + family: 'A' + name: 'standard' + } + softDeleteRetentionInDays: 7 + tenantId: subscription().tenantId + } +} + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/accounts?pivots=deployment-language-bicep +resource defaultAiServices 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' = if(!aiServicesExists) { + name: aiServicesName + location: modelLocation + sku: { + name: 'S0' + } + kind: 'AIServices' // or 'OpenAI' + identity: { + type: 'SystemAssigned' + } + properties: { + customSubDomainName: toLower('${(aiServicesName)}') + apiProperties: { + statisticsEnabled: false + } + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules:[ + { + id: virtualNetwork.properties.subnets[0].id + } + ] + } + publicNetworkAccess: 'Disabled' + } +} + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/accounts/deployments?pivots=deployment-language-bicep +resource defaultModelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-06-01-preview'= if(!aiServicesExists){ + parent: defaultAiServices + name: modelName + sku : { + capacity: modelCapacity + name: modelSkuName + } + properties: { + model:{ + name: modelName + format: modelFormat + version: modelVersion + } + } +} + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.search/searchservices?pivots=deployment-language-bicep +resource defaultAiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = if(!aiSearchExists) { + name: aiSearchName + location: location + tags: tags + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${uai.id}': {} + } + } + properties: { + disableLocalAuth: false + authOptions: { aadOrApiKey: { aadAuthFailureMode: 'http401WithBearerChallenge'}} + encryptionWithCmk: { + enforcement: 'Unspecified' + } + hostingMode: 'default' + partitionCount: 1 + publicNetworkAccess: 'Disabled' + replicaCount: 1 + semanticSearch: 'disabled' + } + sku: { + name: 'standard' + } +} + +// 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' } +var storageNameCleaned = storageExists ? existingStorage.name : replace(storageName, '-', '') + +// Documentation: https://learn.microsoft.com/en-us/azure/templates/microsoft.storage/storageaccounts?pivots=deployment-language-bicep +resource defaultStorage 'Microsoft.Storage/storageAccounts@2022-05-01' = if(!storageExists){ + name: storageNameCleaned + location: location + kind: 'StorageV2' + sku: sku + properties: { + minimumTlsVersion: 'TLS1_2' + allowBlobPublicAccess: false + publicNetworkAccess: 'Disabled' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules: [ + { + id: cxSubnetRef + } + ] + } + allowSharedKeyAccess: false + } + dependsOn: [ + virtualNetwork + ] +} + +module storageAccessAssignment './storage-role-assignments.bicep' = if(!storageExists){ + name: 'dependencies-${suffix}-storage-rbac' + params: { + suffix: suffix + storageName: storageNameCleaned + UAIPrincipalId: uai.properties.principalId + } + dependsOn: [ defaultStorage ] +} + +module keyVaultAccessAssignment './keyvault-role-assignments.bicep' = if(!keyvaultExists){ + name: 'dependencies-${suffix}-keyvault-rbac' + params: { + suffix: suffix + keyvaultName: keyvaultName + UAIPrincipalId: uai.properties.principalId + } + dependsOn: [ defaultKeyVault ] +} + +module cognitiveServicesAccessAssignment './cognitive-services-role-assignments.bicep' = if(!aiServicesExists){ + name: 'dependencies-${suffix}-cogsvc-rbac' + params: { + suffix: suffix + UAIPrincipalId: uai.properties.principalId + } + dependsOn: [ defaultAiServices ] +} + +module aiSearchAccessAssignment 'ai-search-role-assignments.bicep' = if(!aiSearchExists){ + name: 'dependencies-${suffix}-aisearch-rbac' + params: { + aiProjectId: aiSearchName + aiProjectPrincipalId: uai.properties.principalId + aiSearchName: aiSearchName + } + dependsOn: [ defaultAiSearch ] +} + + +var aiServiceParts = aiServicesExists ? split(existingAiServices.id, '/') : split(defaultAiServices.id, '/') +var acsParts = aiSearchExists ? split(existingAiSearch.id, '/') : split(defaultAiSearch.id, '/') +var storageParts = storageExists ? split(existingStorage.id, '/') : split(defaultStorage.id, '/') + +output aiServicesName string = aiServicesExists ? existingAiServices.name : defaultAiServices.name +output aiservicesID string = aiServicesExists ? existingAiServices.id : defaultAiServices.id +output aiservicesTarget string = aiServicesExists ? existingAiServices.properties.endpoint : defaultAiServices.properties.endpoint +output aiServiceAccountResourceGroupName string = aiServiceParts[4] +output aiServiceAccountSubscriptionId string = aiServiceParts[2] + +output aiSearchName string = aiSearchExists ? existingAiSearch.name : defaultAiSearch.name +output aisearchID string = aiSearchExists ? existingAiSearch.id : defaultAiSearch.id +output aiSearchServiceResourceGroupName string = acsParts[4] +output aiSearchServiceSubscriptionId string = acsParts[2] + +output storageAccountName string = storageExists ? existingStorage.name : storageName +output storageId string = storageExists ? existingStorage.id : defaultStorage.id +output storageAccountResourceGroupName string = storageParts[4] +output storageAccountSubscriptionId string = storageParts[2] + +output virtualNetworkName string = virtualNetwork.name +output virtualNetworkId string = virtualNetwork.id +output cxSubnetName string = cxSubnetName +output agentSubnetName string = agentsSubnetName +output cxSubnetId string = cxSubnetRef +output agentSubnetId string = agentSubnetRef + +output keyvaultId string = defaultKeyVault.id diff --git a/scenarios/Agents/modules-network-secured/network-secured-identity.bicep b/scenarios/Agents/modules-network-secured/network-secured-identity.bicep new file mode 100644 index 00000000..ff163293 --- /dev/null +++ b/scenarios/Agents/modules-network-secured/network-secured-identity.bicep @@ -0,0 +1,18 @@ +param location string +param userAssignedIdentityName string +param uaiExists bool = false + +resource existingUAI 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' existing = if(uaiExists) { + name: userAssignedIdentityName + scope: resourceGroup() +} + +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = { + location: location + name: userAssignedIdentityName +} + +output uaiName string = uaiExists ? existingUAI.name : userAssignedIdentity.name +output uaiId string = uaiExists ? existingUAI.id : userAssignedIdentity.id +output uaiPrincipalId string = uaiExists ? existingUAI.properties.principalId : userAssignedIdentity.properties.principalId +output uaiClientId string = uaiExists ? existingUAI.properties.clientId : userAssignedIdentity.properties.clientId diff --git a/scenarios/Agents/modules-network-secured/private-endpoint-and-dns.bicep b/scenarios/Agents/modules-network-secured/private-endpoint-and-dns.bicep new file mode 100644 index 00000000..2d53f6fe --- /dev/null +++ b/scenarios/Agents/modules-network-secured/private-endpoint-and-dns.bicep @@ -0,0 +1,187 @@ +param aiServicesName string +param aiSearchName string +param storageName string +param vnetName string +param cxSubnetName string +param suffix string +param aiStorageId string + +resource aiServices 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: aiServicesName + scope: resourceGroup() +} + +resource aiSearch 'Microsoft.Search/searchServices@2023-11-01' existing = { + name: aiSearchName + scope: resourceGroup() +} + +resource vnet 'Microsoft.Network/virtualNetworks@2024-05-01' existing = { + name: vnetName + scope: resourceGroup() +} + +resource cxSubnet 'Microsoft.Network/virtualNetworks/subnets@2024-05-01' existing = { + parent: vnet + name: cxSubnetName +} + +resource aiServicesPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { + name: '${aiServicesName}-private-endpoint' + location: resourceGroup().location + properties: { + subnet: { + id: cxSubnet.id + } + privateLinkServiceConnections: [ + { + name: '${aiServicesName}-private-link-service-connection' + properties: { + privateLinkServiceId: aiServices.id + groupIds: [ + 'account' + ] + } + } + ] + } +} + +resource aiSearchPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { + name: '${aiSearchName}-private-endpoint' + location: resourceGroup().location + properties: { + subnet: { + id: cxSubnet.id + } + privateLinkServiceConnections: [ + { + name: '${aiSearchName}-private-link-service-connection' + properties: { + privateLinkServiceId: aiSearch.id + groupIds: [ + 'searchService' + ] + } + } + ] + } +} + +resource storagePrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { + name: '${storageName}-private-endpoint' + location: resourceGroup().location + properties: { + subnet: { + id: cxSubnet.id + } + privateLinkServiceConnections: [ + { + name: '${storageName}-private-link-service-connection' + properties: { + privateLinkServiceId: aiStorageId + groupIds: [ + 'blob' + ] + } + } + ] + } +} + +resource aiServicesPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.azureml.ms' + location: 'global' +} + +resource aiServicesLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = { + parent: aiServicesPrivateDnsZone + location: 'global' + name: 'aiServices-${suffix}-link' + properties: { + virtualNetwork: { + id: vnet.id + } + registrationEnabled: false + } +} + +resource aiServicesDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { + parent: aiServicesPrivateEndpoint + name: '${aiServicesName}-dns-group' + properties: { + privateDnsZoneConfigs: [ + { + name: '${aiServicesName}-dns-config' + properties: { + privateDnsZoneId: aiServicesPrivateDnsZone.id + } + } + ] + } +} + +resource aiSearchPrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.search.windows.net' + location: 'global' +} + +resource aiSearchLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = { + parent: aiSearchPrivateDnsZone + location: 'global' + name: 'aiSearch-${suffix}-link' + properties: { + virtualNetwork: { + id: vnet.id + } + registrationEnabled: false + } +} + +resource aiSearchDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { + parent: aiSearchPrivateEndpoint + name: '${aiSearchName}-dns-group' + properties: { + privateDnsZoneConfigs: [ + { + name: '${aiSearchName}-dns-config' + properties: { + privateDnsZoneId: aiSearchPrivateDnsZone.id + } + } + ] + } +} + +resource storagePrivateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.blob.core.windows.net' + location: 'global' +} + +resource storageLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2024-06-01' = { + parent: storagePrivateDnsZone + location: 'global' + name: 'storage-${suffix}-link' + properties: { + virtualNetwork: { + id: vnet.id + } + registrationEnabled: false + } +} + + +resource storageDnsGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { + parent: storagePrivateEndpoint + name: '${storageName}-dns-group' + properties: { + privateDnsZoneConfigs: [ + { + name: '${storageName}-dns-config' + properties: { + privateDnsZoneId: storagePrivateDnsZone.id + } + } + ] + } +} diff --git a/scenarios/Agents/modules-standard/ai-search-role-assignments.bicep b/scenarios/Agents/modules-standard/ai-search-role-assignments.bicep new file mode 100644 index 00000000..326d5fee --- /dev/null +++ b/scenarios/Agents/modules-standard/ai-search-role-assignments.bicep @@ -0,0 +1,41 @@ +// Assigns the necessary roles to the AI project + +param aiSearchName string +param aiProjectPrincipalId string +param aiProjectId 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(aiProjectId, searchIndexDataContributorRole.id, searchService.id) + properties: { + principalId: aiProjectPrincipalId + 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(aiProjectId, searchServiceContributorRole.id, searchService.id) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: searchServiceContributorRole.id + principalType: 'ServicePrincipal' + } +} diff --git a/scenarios/Agents/modules-standard/ai-service-role-assignments.bicep b/scenarios/Agents/modules-standard/ai-service-role-assignments.bicep new file mode 100644 index 00000000..f22ad548 --- /dev/null +++ b/scenarios/Agents/modules-standard/ai-service-role-assignments.bicep @@ -0,0 +1,56 @@ +// Assigns the necessary roles to the AI project + +param aiServicesName string +param aiProjectPrincipalId string +param aiProjectId string + +resource aiServices 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' existing = { + name: aiServicesName + scope: resourceGroup() +} + +resource cognitiveServicesContributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68' + scope: resourceGroup() + +} + +resource cognitiveServicesContributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01'= { + scope: aiServices + name: guid(aiServices.id, cognitiveServicesContributorRole.id, aiProjectId) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: cognitiveServicesContributorRole.id + principalType: 'ServicePrincipal' + } +} + + +resource cognitiveServicesOpenAIUserRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd' + scope: resourceGroup() +} +resource cognitiveServicesOpenAIUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aiServices + name: guid(aiProjectId, cognitiveServicesOpenAIUserRole.id, aiServices.id) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: cognitiveServicesOpenAIUserRole.id + principalType: 'ServicePrincipal' + } +} + +resource cognitiveServicesUserRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: 'a97b65f3-24c7-4388-baec-2e87135dc908' + scope: resourceGroup() +} + +resource cognitiveServicesUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: aiServices + name: guid(aiProjectId, cognitiveServicesUserRole.id, aiServices.id) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: cognitiveServicesUserRole.id + principalType: 'ServicePrincipal' + } +} diff --git a/scenarios/Agents/modules-standard/standard-ai-hub.bicep b/scenarios/Agents/modules-standard/standard-ai-hub.bicep new file mode 100644 index 00000000..9cfa850a --- /dev/null +++ b/scenarios/Agents/modules-standard/standard-ai-hub.bicep @@ -0,0 +1,119 @@ +// Creates an Azure AI resource with proxied endpoints for the Azure AI services provider + +@description('Azure region of the deployment') +param location string + +@description('Tags to add to the resources') +param tags object + +@description('AI hub name') +param aiHubName string + +@description('AI hub display name') +param aiHubFriendlyName string = aiHubName + +@description('AI hub description') +param aiHubDescription string + +@description('Resource ID of the key vault resource for storing connection strings') +param keyVaultId string + +@description('Resource ID of the storage account resource for storing experimentation outputs') +param storageAccountId string + +@description('Resource ID of the AI Services resource') +param aiServicesId string +@description('Resource ID of the AI Services endpoint') +param aiServicesTarget string +@description('Name AI Services resource') +param aiServicesName string +@description('Resource Group name of the AI Services resource') +param aiServiceAccountResourceGroupName string +@description('Subscription ID of the AI Services resource') +param aiServiceAccountSubscriptionId string + +@description('Name AI Search resource') +param aiSearchName string +@description('Resource ID of the AI Search resource') +param aiSearchId string +param aiSearchServiceResourceGroupName string +param aiSearchServiceSubscriptionId string +@description('Name for capabilityHost.') +param capabilityHostName string = 'caphost1' + +var acsConnectionName = '${aiHubName}-connection-AISearch' + +var aoaiConnection = '${aiHubName}-connection-AIServices_aoai' + + +resource aiServices 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: aiServicesName + scope: resourceGroup(aiServiceAccountSubscriptionId, aiServiceAccountResourceGroupName) +} + +resource searchService 'Microsoft.Search/searchServices@2023-11-01' existing = { + name: aiSearchName + scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) +} + +resource aiHub 'Microsoft.MachineLearningServices/workspaces@2024-07-01-preview' = { + name: aiHubName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + // organization + friendlyName: aiHubFriendlyName + description: aiHubDescription + + // dependent resources + keyVault: keyVaultId + storageAccount: storageAccountId + } + kind: 'hub' + + resource aiServicesConnection 'connections@2024-07-01-preview' = { + name: '${aiHubName}-connection-AIServices' + properties: { + category: 'AIServices' + target: aiServicesTarget + authType: 'AAD' + isSharedToAll: true + metadata: { + ApiType: 'Azure' + ResourceId: aiServicesId + location: aiServices.location + } + } + } + resource hub_connection_azureai_search 'connections@2024-07-01-preview' = { + name: acsConnectionName + properties: { + category: 'CognitiveSearch' + target: 'https://${aiSearchName}.search.windows.net' + authType: 'AAD' + //useWorkspaceManagedIdentity: false + isSharedToAll: true + metadata: { + ApiType: 'Azure' + ResourceId: aiSearchId + location: searchService.location + } + } + } + + // Resource definition for the capability host + resource capabilityHost 'capabilityHosts@2024-10-01-preview' = { + name: '${aiHubName}-${capabilityHostName}' + properties: { + capabilityHostKind: 'Agents' + } + } + +} + +output aiHubID string = aiHub.id +output aoaiConnectionName string = aoaiConnection +output acsConnectionName string = acsConnectionName diff --git a/scenarios/Agents/modules-standard/standard-ai-project.bicep b/scenarios/Agents/modules-standard/standard-ai-project.bicep new file mode 100644 index 00000000..48796ed1 --- /dev/null +++ b/scenarios/Agents/modules-standard/standard-ai-project.bicep @@ -0,0 +1,78 @@ +// Creates an Azure AI resource with proxied endpoints for the Azure AI services provider + +@description('Azure region of the deployment') +param location string + +@description('Tags to add to the resources') +param tags object + +@description('AI Project name') +param aiProjectName string + +@description('AI Project display name') +param aiProjectFriendlyName string = aiProjectName + +@description('AI Project description') +param aiProjectDescription string + +@description('Resource ID of the AI Hub resource') +param aiHubId string + +@description('Name for capabilityHost.') +param capabilityHostName string = 'caphost1' + +@description('Name for ACS connection.') +param acsConnectionName string + +@description('Name for ACS connection.') +param aoaiConnectionName string + +//for constructing endpoint +var subscriptionId = subscription().subscriptionId +var resourceGroupName = resourceGroup().name + +var projectConnectionString = '${location}.api.azureml.ms;${subscriptionId};${resourceGroupName};${aiProjectName}' + + +var storageConnections = ['${aiProjectName}/workspaceblobstore'] +var aiSearchConnection = ['${acsConnectionName}'] +var aiServiceConnections = ['${aoaiConnectionName}'] + + +resource aiProject 'Microsoft.MachineLearningServices/workspaces@2023-08-01-preview' = { + name: aiProjectName + location: location + tags: union(tags, { + ProjectConnectionString: projectConnectionString + }) + identity: { + type: 'SystemAssigned' + } + properties: { + // organization + friendlyName: aiProjectFriendlyName + description: aiProjectDescription + + // dependent resources + hubResourceId: aiHubId + + } + kind: 'project' + + // Resource definition for the capability host + resource capabilityHost 'capabilityHosts@2024-10-01-preview' = { + name: '${aiProjectName}-${capabilityHostName}' + properties: { + capabilityHostKind: 'Agents' + aiServicesConnections: aiServiceConnections + vectorStoreConnections: aiSearchConnection + storageConnections: storageConnections + } + } +} + +output aiProjectName string = aiProject.name +output aiProjectResourceId string = aiProject.id +output aiProjectPrincipalId string = aiProject.identity.principalId +output aiProjectWorkspaceId string = aiProject.properties.workspaceId +output projectConnectionString string = aiProject.tags.ProjectConnectionString diff --git a/scenarios/Agents/modules-standard/standard-dependent-resources.bicep b/scenarios/Agents/modules-standard/standard-dependent-resources.bicep new file mode 100644 index 00000000..abf81f59 --- /dev/null +++ b/scenarios/Agents/modules-standard/standard-dependent-resources.bicep @@ -0,0 +1,196 @@ +// Creates Azure dependent resources for Azure AI studio + +@description('Azure region of the deployment') +param location string = resourceGroup().location + +@description('Tags to add to the resources') +param tags object = {} + +@description('AI services name') +param aiServicesName 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 storageName string + +var storageNameCleaned = replace(storageName, '-', '') + +@description('Model name for deployment') +param modelName string + +@description('Model format for deployment') +param modelFormat string + +@description('Model version for deployment') +param modelVersion string + +@description('Model deployment SKU name') +param modelSkuName string + +@description('Model deployment capacity') +param modelCapacity int + +@description('Model/AI Resource deployment location') +param modelLocation string + +@description('The AI Service Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created.') +param aiServiceAccountResourceId 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 aiSearchServiceResourceId 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 aiStorageAccountResourceId string + +var aiServiceExists = aiServiceAccountResourceId != '' +var acsExists = aiSearchServiceResourceId != '' +var aiStorageExists = aiStorageAccountResourceId != '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: keyvaultName + location: location + tags: tags + properties: { + createMode: 'default' + enabledForDeployment: false + enabledForDiskEncryption: false + enabledForTemplateDeployment: false + enableSoftDelete: true + enableRbacAuthorization: true + enablePurgeProtection: true + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } + sku: { + family: 'A' + name: 'standard' + } + softDeleteRetentionInDays: 7 + tenantId: subscription().tenantId + } +} + + +var aiServiceParts = split(aiServiceAccountResourceId, '/') + +resource existingAIServiceAccount 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = if (aiServiceExists) { + name: aiServiceParts[8] + scope: resourceGroup(aiServiceParts[2], aiServiceParts[4]) +} + +resource aiServices 'Microsoft.CognitiveServices/accounts@2024-06-01-preview' = if(!aiServiceExists) { + name: aiServicesName + location: modelLocation + sku: { + name: 'S0' + } + kind: 'AIServices' // or 'OpenAI' + identity: { + type: 'SystemAssigned' + } + properties: { + customSubDomainName: toLower('${(aiServicesName)}') + apiProperties: { + statisticsEnabled: false + } + publicNetworkAccess: 'Enabled' + } +} +resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-06-01-preview'= if(!aiServiceExists){ + parent: aiServices + name: modelName + sku : { + capacity: modelCapacity + name: modelSkuName + } + properties: { + model:{ + name: modelName + format: modelFormat + version: modelVersion + } + } +} + +var acsParts = split(aiSearchServiceResourceId, '/') + +resource existingSearchService 'Microsoft.Search/searchServices@2023-11-01' existing = if (acsExists) { + name: acsParts[8] + scope: resourceGroup(acsParts[2], acsParts[4]) +} +resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = if(!acsExists) { + name: aiSearchName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + disableLocalAuth: false + authOptions: { aadOrApiKey: { aadAuthFailureMode: 'http401WithBearerChallenge'}} + encryptionWithCmk: { + enforcement: 'Unspecified' + } + hostingMode: 'default' + partitionCount: 1 + publicNetworkAccess: 'enabled' + replicaCount: 1 + semanticSearch: 'disabled' + } + sku: { + name: 'standard' + } +} + +var aiStorageParts = split(aiStorageAccountResourceId, '/') + +resource existingAIStorageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = if (aiStorageExists) { + name: aiStorageParts[8] + scope: resourceGroup(aiStorageParts[2], aiStorageParts[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' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = if(!aiStorageExists) { + name: storageNameCleaned + location: location + kind: 'StorageV2' + sku: sku + properties: { + minimumTlsVersion: 'TLS1_2' + allowBlobPublicAccess: false + publicNetworkAccess: 'Enabled' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Allow' + virtualNetworkRules: [] + } + allowSharedKeyAccess: false + } +} + +output aiServicesName string = aiServiceExists ? existingAIServiceAccount.name : aiServicesName +output aiservicesID string = aiServiceExists ? existingAIServiceAccount.id : aiServices.id +output aiservicesTarget string = aiServiceExists ? existingAIServiceAccount.properties.endpoint : aiServices.properties.endpoint +output aiServiceAccountResourceGroupName string = aiServiceExists ? aiServiceParts[4] : resourceGroup().name +output aiServiceAccountSubscriptionId string = aiServiceExists ? aiServiceParts[2] : subscription().subscriptionId + +output aiSearchName string = acsExists ? existingSearchService.name : aiSearch.name +output aisearchID string = acsExists ? existingSearchService.id : aiSearch.id +output aiSearchServiceResourceGroupName string = acsExists ? acsParts[4] : resourceGroup().name +output aiSearchServiceSubscriptionId string = acsExists ? acsParts[2] : subscription().subscriptionId + +output storageAccountName string = aiStorageExists ? existingAIStorageAccount.name : storage.name +output storageId string = aiStorageExists ? existingAIStorageAccount.id : storage.id +output storageAccountResourceGroupName string = aiStorageExists ? aiStorageParts[4] : resourceGroup().name +output storageAccountSubscriptionId string = aiStorageExists ? aiStorageParts[2] : subscription().subscriptionId + +output keyvaultId string = keyVault.id