diff --git a/scenarios/Agents/README.md b/scenarios/Agents/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/README.md b/scenarios/Agents/setup/standard-agent-with-threadstorage/README.md new file mode 100644 index 00000000..60c1840a --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/README.md @@ -0,0 +1,60 @@ +--- +description: This set of templates demonstrates how to set up Azure AI Agent Service with the standard setup, meaning with managed identity authentication for project/hub connections and public internet access enabled. Agents use customer-owned, single-tenant search, file storage, and thread storage resources. With this setup, you have full control and visibility over these resources, but you will incur costs based on your usage. +page_type: sample +products: +- azure +- azure-resource-manager +urlFragment: standard-agent +languages: +- bicep +- json +--- +# Standard Agent Setup with thread storage + +> [!IMPORTANT] +> This is the complete Standard Agent Setup with Bring Your Own (BYO) Thread Storage. With this setup, all customer data stored by Agent Service will remain in your own resources, ensuring you have full control over your data. + +![Azure Public Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.azure-ai-agent-service/standard-agent/PublicLastTestDate.svg) +![Azure Public Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.azure-ai-agent-service/standard-agent/PublicDeployment.svg) + +![Azure US Gov Last Test Date](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.azure-ai-agent-service/standard-agent/FairfaxLastTestDate.svg) +![Azure US Gov Last Test Result](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.azure-ai-agent-service/standard-agent/FairfaxDeployment.svg) + +![Best Practice Check](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.azure-ai-agent-service/standard-agent/BestPracticeResult.svg) +![Cred Scan Check](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.azure-ai-agent-service/standard-agent/CredScanResult.svg) + +![Bicep Version](https://azurequickstartsservice.blob.core.windows.net/badges/quickstarts/microsoft.azure-ai-agent-service/standard-agent/BicepVersion.svg) + +[![Deploy To Azure](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/deploytoazure.svg?sanitize=true)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Fazureai-samples%2Fmain%2Fscenarios%2FAgents%2Fsetup%2Fstandard-agent-with-threadstorage%2Fazuredeploy.json) + +[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Fazureai-samples%2Fmain%2Fscenarios%2FAgents%2Fsetup%2Fstandard-agent%2Fazuredeploy.json) + +## Overview + +This template demonstrates how to configure Standard Agent Setup with support for the **new feature BYO thread storage with Azure Cosmos DB.** + +Resources for the hub, project, storage account, key vault, AI Services, Azure AI Search, and Cosmos DB Account will be automatically created for you. The AI Services, AI Search, Cosmos DB Account, and Azure Storage Account will be connected to your project/hub using managed identity for authentication and a gpt-4o model will be deployed in the eastus region. + +## Customization +You can optionally use existing resources for AI Services, AI Search, Cosmos DB, and Azure Blob Storage. Update the following parameters in the parameters file to provide the full ARM resource ID of the existing resources: + +- `aiServiceAccountResourceId` +- `aiSearchServiceResourceId` +- `aiStorageAccountResourceId` +- `cosmosDBResourceId` + +If you want to use an **existing Azure OpenAI resource**, you will need to update the `aiServiceAccountResourceId` and the `aiServiceKind` parameter in the parameters file. Set the `aiServiceKind` parameter to `AzureOpenAI`. + +## Resources + +| Provider and type | Description | +| - | - | +| `Microsoft.Resources/resourceGroups` | The resource group all resources get deployed into | +| `Microsoft.KeyVault/vaults` | An Azure Key Vault instance associated to the Azure Machine Learning workspace | +| `Microsoft.Storage/storageAccounts` | An Azure Storage instance associated to the Azure Machine Learning workspace | +| `Microsoft.MachineLearningServices/workspaces` | An Azure AI hub (Azure Machine Learning RP workspace of kind 'hub') | +| `Microsoft.MachineLearningServices/workspaces` | An Azure AI project (Azure Machine Learning RP workspace of kind 'project') | +| `Microsoft.CognitiveServices/accounts` | An Azure AI Services as the model-as-a-service endpoint provider (allowed kinds: 'AIServices' and 'OpenAI') | +| `Microsoft.CognitiveServices/accounts/deployments` | A gpt-4o model is deployed | +| `Microsoft.Search/searchServices` | An Azure AI Search account | +| `Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview` | An Azure Cosmos DB Account | diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/azuredeploy.json b/scenarios/Agents/setup/standard-agent-with-threadstorage/azuredeploy.json new file mode 100644 index 00000000..e1dbf88e --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/azuredeploy.json @@ -0,0 +1,1699 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "8527508992869683277" + } + }, + "parameters": { + "aiHubName": { + "type": "string", + "defaultValue": "hub-demo", + "minLength": 2, + "maxLength": 12, + "metadata": { + "description": "Name for your Azure AI Hub resource." + } + }, + "aiHubFriendlyName": { + "type": "string", + "defaultValue": "Agents Hub resource", + "metadata": { + "description": "Friendly name for your Hub resource" + } + }, + "aiHubDescription": { + "type": "string", + "defaultValue": "This is an example AI Hub resource for use in Azure AI Studio.", + "metadata": { + "description": "Creating an Azure AI Hub to set up your app environment and Azure resources." + } + }, + "aiProjectName": { + "type": "string", + "defaultValue": "project-demo", + "metadata": { + "description": "Name for the AI project resources." + } + }, + "aiProjectFriendlyName": { + "type": "string", + "defaultValue": "Agents Project resource", + "metadata": { + "description": "Friendly name for your Azure AI resource" + } + }, + "aiProjectDescription": { + "type": "string", + "defaultValue": "This is an example AI Project resource for use in Azure AI Studio.", + "metadata": { + "description": "Creating an Azure AI project under your Hub creates an endpoint for your app to call, and sets up app services to access to resources in your tenant." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Azure region used for the deployment of all resources." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Set of tags to apply to all resources." + } + }, + "aiSearchName": { + "type": "string", + "defaultValue": "agent-ai-search", + "metadata": { + "description": "Name of the Azure AI Search account" + } + }, + "capabilityHostName": { + "type": "string", + "defaultValue": "caphost1", + "metadata": { + "description": "Name for capabilityHost." + } + }, + "storageName": { + "type": "string", + "defaultValue": "agent-storage", + "metadata": { + "description": "Name of the storage account" + } + }, + "aiServicesName": { + "type": "string", + "defaultValue": "agent-ai-services", + "metadata": { + "description": "Name of the Azure AI Services account" + } + }, + "modelName": { + "type": "string", + "defaultValue": "gpt-4o", + "metadata": { + "description": "Model name for deployment" + } + }, + "modelFormat": { + "type": "string", + "defaultValue": "OpenAI", + "metadata": { + "description": "Model format for deployment" + } + }, + "modelVersion": { + "type": "string", + "defaultValue": "2024-08-06", + "metadata": { + "description": "Model version for deployment" + } + }, + "modelSkuName": { + "type": "string", + "defaultValue": "GlobalStandard", + "metadata": { + "description": "Model deployment SKU name" + } + }, + "modelCapacity": { + "type": "int", + "defaultValue": 50, + "metadata": { + "description": "Model deployment capacity" + } + }, + "modelLocation": { + "type": "string", + "defaultValue": "westus", + "metadata": { + "description": "Model deployment location. If you want to deploy an Azure AI resource/model in different location than the rest of the resources created." + } + }, + "aiServiceKind": { + "type": "string", + "defaultValue": "AIServices", + "metadata": { + "description": "AI Service Account kind: either AzureOpenAI or AIServices" + } + }, + "aiServiceAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The AI Service Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." + } + }, + "aiSearchServiceResourceId": { + "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." + } + }, + "aiStorageAccountResourceId": { + "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." + } + }, + "cosmosDBResourceId": { + "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." + } + }, + "cosmosDBName": { + "type": "string", + "defaultValue": "agent-thread-storage" + }, + "deploymentTimestamp": { + "type": "string", + "defaultValue": "[utcNow('yyyyMMddHHmmss')]" + } + }, + "variables": { + "name": "[toLower(format('{0}', parameters('aiHubName')))]", + "projectName": "[toLower(format('{0}', parameters('aiProjectName')))]", + "uniqueSuffix": "[substring(uniqueString(format('{0}-{1}', resourceGroup().id, parameters('deploymentTimestamp'))), 0, 4)]", + "aiServiceExists": "[not(equals(parameters('aiStorageAccountResourceId'), ''))]", + "acsExists": "[not(equals(parameters('aiSearchServiceResourceId'), ''))]", + "cosmosExists": "[not(equals(parameters('cosmosDBResourceId'), ''))]", + "aiServiceParts": "[split(parameters('aiServiceAccountResourceId'), '/')]", + "aiServiceAccountSubscriptionId": "[if(variables('aiServiceExists'), variables('aiServiceParts')[2], subscription().subscriptionId)]", + "aiServiceAccountResourceGroupName": "[if(variables('aiServiceExists'), variables('aiServiceParts')[4], resourceGroup().name)]", + "acsParts": "[split(parameters('aiSearchServiceResourceId'), '/')]", + "aiSearchServiceSubscriptionId": "[if(variables('acsExists'), variables('acsParts')[2], subscription().subscriptionId)]", + "aiSearchServiceResourceGroupName": "[if(variables('acsExists'), variables('acsParts')[4], resourceGroup().name)]", + "cosmosParts": "[split(parameters('cosmosDBResourceId'), '/')]", + "cosmosDBSubscriptionId": "[if(variables('cosmosExists'), variables('cosmosParts')[2], subscription().subscriptionId)]", + "cosmosDBResourceGroupName": "[if(variables('cosmosExists'), variables('cosmosParts')[4], resourceGroup().name)]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('validate-existing-resources-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "aiServiceAccountResourceId": { + "value": "[parameters('aiServiceAccountResourceId')]" + }, + "aiSearchServiceResourceId": { + "value": "[parameters('aiSearchServiceResourceId')]" + }, + "aiStorageAccountResourceId": { + "value": "[parameters('aiStorageAccountResourceId')]" + }, + "cosmosDBResourceId": { + "value": "[parameters('cosmosDBResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.33.93.31351", + "templateHash": "10392990986234847358" + } + }, + "parameters": { + "aiServiceAccountResourceId": { + "type": "string", + "metadata": { + "description": "Resource ID of the AI Service Account. " + } + }, + "aiSearchServiceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Resource ID of the Azure AI Search Service." + } + }, + "aiStorageAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Resource ID of the Azure Storage Account." + } + }, + "cosmosDBResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "ResourceId of Cosmos DB Account" + } + } + }, + "variables": { + "storagePassedIn": "[not(equals(parameters('aiStorageAccountResourceId'), ''))]", + "aiServicesPassedIn": "[not(equals(parameters('aiServiceAccountResourceId'), ''))]", + "searchPassedIn": "[not(equals(parameters('aiSearchServiceResourceId'), ''))]", + "cosmosPassedIn": "[not(equals(parameters('cosmosDBResourceId'), ''))]", + "aiServiceParts": "[split(parameters('aiServiceAccountResourceId'), '/')]", + "aiServiceAccountSubscriptionId": "[if(variables('aiServicesPassedIn'), variables('aiServiceParts')[2], subscription().subscriptionId)]", + "aiServiceAccountResourceGroupName": "[if(variables('aiServicesPassedIn'), variables('aiServiceParts')[4], resourceGroup().name)]", + "acsParts": "[split(parameters('aiSearchServiceResourceId'), '/')]", + "aiSearchServiceSubscriptionId": "[if(variables('searchPassedIn'), variables('acsParts')[2], subscription().subscriptionId)]", + "aiSearchServiceResourceGroupName": "[if(variables('searchPassedIn'), variables('acsParts')[4], resourceGroup().name)]", + "cosmosParts": "[split(parameters('cosmosDBResourceId'), '/')]", + "cosmosDBSubscriptionId": "[if(variables('cosmosPassedIn'), variables('cosmosParts')[2], subscription().subscriptionId)]", + "cosmosDBResourceGroupName": "[if(variables('cosmosPassedIn'), variables('cosmosParts')[4], resourceGroup().name)]", + "storageParts": "[split(parameters('aiStorageAccountResourceId'), '/')]", + "azureStorageSubscriptionId": "[if(variables('storagePassedIn'), variables('storageParts')[2], subscription().subscriptionId)]", + "azureStorageResourceGroupName": "[if(variables('storagePassedIn'), variables('storageParts')[4], resourceGroup().name)]" + }, + "resources": [], + "outputs": { + "aiServiceExists": { + "type": "bool", + "value": "[and(variables('aiServicesPassedIn'), equals(last(split(parameters('aiServiceAccountResourceId'), '/')), variables('aiServiceParts')[8]))]" + }, + "aiSearchExists": { + "type": "bool", + "value": "[and(variables('searchPassedIn'), equals(last(split(parameters('aiSearchServiceResourceId'), '/')), variables('acsParts')[8]))]" + }, + "cosmosDBExists": { + "type": "bool", + "value": "[and(variables('cosmosPassedIn'), equals(last(split(parameters('cosmosDBResourceId'), '/')), variables('cosmosParts')[8]))]" + }, + "aiStorageExists": { + "type": "bool", + "value": "[and(variables('storagePassedIn'), equals(variables('storageParts')[8], 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')]" + }, + "aiServiceAccountSubscriptionId": { + "type": "string", + "value": "[variables('aiServiceAccountSubscriptionId')]" + }, + "aiServiceAccountResourceGroupName": { + "type": "string", + "value": "[variables('aiServiceAccountResourceGroupName')]" + }, + "azureStorageSubscriptionId": { + "type": "string", + "value": "[variables('azureStorageSubscriptionId')]" + }, + "azureStorageResourceGroupName": { + "type": "string", + "value": "[variables('azureStorageResourceGroupName')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "storageName": { + "value": "[format('{0}{1}', parameters('storageName'), variables('uniqueSuffix'))]" + }, + "keyvaultName": { + "value": "[format('kv-{0}-{1}', variables('name'), variables('uniqueSuffix'))]" + }, + "aiServicesName": { + "value": "[format('{0}{1}', parameters('aiServicesName'), variables('uniqueSuffix'))]" + }, + "aiSearchName": { + "value": "[format('{0}-{1}', parameters('aiSearchName'), variables('uniqueSuffix'))]" + }, + "cosmosDBName": { + "value": "[format('{0}-{1}', parameters('cosmosDBName'), variables('uniqueSuffix'))]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "modelName": { + "value": "[parameters('modelName')]" + }, + "modelFormat": { + "value": "[parameters('modelFormat')]" + }, + "modelVersion": { + "value": "[parameters('modelVersion')]" + }, + "modelSkuName": { + "value": "[parameters('modelSkuName')]" + }, + "modelCapacity": { + "value": "[parameters('modelCapacity')]" + }, + "modelLocation": { + "value": "[parameters('modelLocation')]" + }, + "aiServiceAccountResourceId": { + "value": "[parameters('aiServiceAccountResourceId')]" + }, + "aiServiceExists": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiServiceExists.value]" + }, + "aiSearchServiceResourceId": { + "value": "[parameters('aiSearchServiceResourceId')]" + }, + "aiSearchExists": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiSearchExists.value]" + }, + "aiStorageAccountResourceId": { + "value": "[parameters('aiStorageAccountResourceId')]" + }, + "aiStorageExists": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiStorageExists.value]" + }, + "cosmosDBResourceId": { + "value": "[parameters('cosmosDBResourceId')]" + }, + "cosmosDBExists": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-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.33.93.31351", + "templateHash": "9356889094364269195" + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Azure region of the deployment" + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Tags to add to the resources" + } + }, + "aiServicesName": { + "type": "string", + "metadata": { + "description": "AI services name" + } + }, + "keyvaultName": { + "type": "string", + "metadata": { + "description": "The name of the Key Vault" + } + }, + "aiSearchName": { + "type": "string", + "metadata": { + "description": "The name of the AI Search resource" + } + }, + "storageName": { + "type": "string", + "metadata": { + "description": "Name of the storage account" + } + }, + "cosmosDBName": { + "type": "string" + }, + "modelName": { + "type": "string", + "metadata": { + "description": "Model name for deployment" + } + }, + "modelFormat": { + "type": "string", + "metadata": { + "description": "Model format for deployment" + } + }, + "modelVersion": { + "type": "string", + "metadata": { + "description": "Model version for deployment" + } + }, + "modelSkuName": { + "type": "string", + "metadata": { + "description": "Model deployment SKU name" + } + }, + "modelCapacity": { + "type": "int", + "metadata": { + "description": "Model deployment capacity" + } + }, + "modelLocation": { + "type": "string", + "metadata": { + "description": "Model/AI Resource deployment location" + } + }, + "aiServiceAccountResourceId": { + "type": "string", + "metadata": { + "description": "The AI Service Account full ARM Resource ID. This is an optional field, and if not provided, the resource will be created." + } + }, + "aiSearchServiceResourceId": { + "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." + } + }, + "aiStorageAccountResourceId": { + "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." + } + }, + "aiServiceExists": { + "type": "bool" + }, + "aiSearchExists": { + "type": "bool" + }, + "aiStorageExists": { + "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": { + "storageNameCleaned": "[replace(parameters('storageName'), '-', '')]", + "cosmosParts": "[split(parameters('cosmosDBResourceId'), '/')]", + "canaryRegions": [ + "eastus2euap", + "centraluseuap" + ], + "cosmosDbRegion": "[if(contains(variables('canaryRegions'), parameters('location')), 'westus', parameters('location'))]", + "aiServiceParts": "[split(parameters('aiServiceAccountResourceId'), '/')]", + "acsParts": "[split(parameters('aiSearchServiceResourceId'), '/')]", + "aiStorageParts": "[split(parameters('aiStorageAccountResourceId'), '/')]" + }, + "resources": [ + { + "condition": "[not(parameters('cosmosDBExists'))]", + "type": "Microsoft.DocumentDB/databaseAccounts", + "apiVersion": "2024-12-01-preview", + "name": "[parameters('cosmosDBName')]", + "location": "[variables('cosmosDbRegion')]", + "tags": "[parameters('tags')]", + "kind": "GlobalDocumentDB", + "properties": { + "consistencyPolicy": { + "defaultConsistencyLevel": "Session" + }, + "disableLocalAuth": true, + "enableAutomaticFailover": false, + "enableMultipleWriteLocations": false, + "enableFreeTier": false, + "locations": [ + { + "locationName": "[parameters('location')]", + "failoverPriority": 0, + "isZoneRedundant": false + } + ], + "databaseAccountOfferType": "Standard" + } + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyvaultName')]", + "location": "[parameters('location')]", + "tags": "[parameters('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]" + } + }, + { + "condition": "[not(parameters('aiServiceExists'))]", + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2024-10-01", + "name": "[parameters('aiServicesName')]", + "location": "[parameters('modelLocation')]", + "sku": { + "name": "S0" + }, + "kind": "AIServices", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "customSubDomainName": "[toLower(format('{0}', parameters('aiServicesName')))]", + "apiProperties": { + "statisticsEnabled": false + }, + "publicNetworkAccess": "Enabled" + } + }, + { + "condition": "[not(parameters('aiServiceExists'))]", + "type": "Microsoft.CognitiveServices/accounts/deployments", + "apiVersion": "2024-10-01", + "name": "[format('{0}/{1}', parameters('aiServicesName'), 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('aiServicesName'))]" + ] + }, + { + "condition": "[not(parameters('aiSearchExists'))]", + "type": "Microsoft.Search/searchServices", + "apiVersion": "2024-06-01-preview", + "name": "[parameters('aiSearchName')]", + "location": "[parameters('location')]", + "tags": "[parameters('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" + } + }, + { + "condition": "[not(parameters('aiStorageExists'))]", + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-05-01", + "name": "[variables('storageNameCleaned')]", + "location": "[parameters('location')]", + "kind": "StorageV2", + "sku": "[parameters('sku')]", + "properties": { + "minimumTlsVersion": "TLS1_2", + "allowBlobPublicAccess": false, + "publicNetworkAccess": "Enabled", + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "Allow", + "virtualNetworkRules": [] + }, + "allowSharedKeyAccess": false + } + } + ], + "outputs": { + "aiServicesName": { + "type": "string", + "value": "[if(parameters('aiServiceExists'), variables('aiServiceParts')[8], parameters('aiServicesName'))]" + }, + "aiservicesID": { + "type": "string", + "value": "[if(parameters('aiServiceExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('aiServiceParts')[2], variables('aiServiceParts')[4]), 'Microsoft.CognitiveServices/accounts', variables('aiServiceParts')[8]), resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')))]" + }, + "aiservicesTarget": { + "type": "string", + "value": "[if(parameters('aiServiceExists'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('aiServiceParts')[2], variables('aiServiceParts')[4]), 'Microsoft.CognitiveServices/accounts', variables('aiServiceParts')[8]), '2024-10-01').endpoint, reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')), '2024-10-01').endpoint)]" + }, + "aiServiceAccountResourceGroupName": { + "type": "string", + "value": "[if(parameters('aiServiceExists'), variables('aiServiceParts')[4], resourceGroup().name)]" + }, + "aiServiceAccountSubscriptionId": { + "type": "string", + "value": "[if(parameters('aiServiceExists'), variables('aiServiceParts')[2], subscription().subscriptionId)]" + }, + "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)]" + }, + "storageAccountName": { + "type": "string", + "value": "[if(parameters('aiStorageExists'), variables('aiStorageParts')[8], variables('storageNameCleaned'))]" + }, + "storageId": { + "type": "string", + "value": "[if(parameters('aiStorageExists'), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('aiStorageParts')[2], variables('aiStorageParts')[4]), 'Microsoft.Storage/storageAccounts', variables('aiStorageParts')[8]), resourceId('Microsoft.Storage/storageAccounts', variables('storageNameCleaned')))]" + }, + "storageAccountResourceGroupName": { + "type": "string", + "value": "[if(parameters('aiStorageExists'), variables('aiStorageParts')[4], resourceGroup().name)]" + }, + "storageAccountSubscriptionId": { + "type": "string", + "value": "[if(parameters('aiStorageExists'), variables('aiStorageParts')[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)]" + }, + "keyvaultId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults', parameters('keyvaultName'))]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('validate-existing-resources-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "aiHubName": { + "value": "[format('{0}-{1}', variables('name'), variables('uniqueSuffix'))]" + }, + "aiHubFriendlyName": { + "value": "[parameters('aiHubFriendlyName')]" + }, + "aiHubDescription": { + "value": "[parameters('aiHubDescription')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "aiSearchName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiSearchName.value]" + }, + "aiSearchId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aisearchID.value]" + }, + "aiSearchServiceResourceGroupName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiSearchServiceResourceGroupName.value]" + }, + "aiSearchServiceSubscriptionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiSearchServiceSubscriptionId.value]" + }, + "aiServicesName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiServicesName.value]" + }, + "aiServiceKind": { + "value": "[parameters('aiServiceKind')]" + }, + "aiServicesId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiservicesID.value]" + }, + "aiServicesTarget": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiservicesTarget.value]" + }, + "aiServiceAccountResourceGroupName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiServiceAccountResourceGroupName.value]" + }, + "aiServiceAccountSubscriptionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiServiceAccountSubscriptionId.value]" + }, + "keyVaultId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.keyvaultId.value]" + }, + "storageAccountId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.storageId.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.33.93.31351", + "templateHash": "10108960257155829972" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "metadata": { + "description": "Tags to add to the resources" + } + }, + "aiHubName": { + "type": "string", + "metadata": { + "description": "AI hub name" + } + }, + "aiHubFriendlyName": { + "type": "string", + "defaultValue": "[parameters('aiHubName')]", + "metadata": { + "description": "AI hub display name" + } + }, + "aiHubDescription": { + "type": "string", + "metadata": { + "description": "AI hub description" + } + }, + "keyVaultId": { + "type": "string", + "metadata": { + "description": "Resource ID of the key vault resource for storing connection strings" + } + }, + "storageAccountId": { + "type": "string", + "metadata": { + "description": "Resource ID of the storage account resource for storing experimentation outputs" + } + }, + "aiServicesId": { + "type": "string", + "metadata": { + "description": "Resource ID of the AI Services resource" + } + }, + "aiServicesTarget": { + "type": "string", + "metadata": { + "description": "Resource ID of the AI Services endpoint" + } + }, + "aiServicesName": { + "type": "string", + "metadata": { + "description": "Name AI Services resource" + } + }, + "aiServiceAccountResourceGroupName": { + "type": "string", + "metadata": { + "description": "Resource Group name of the AI Services resource" + } + }, + "aiServiceAccountSubscriptionId": { + "type": "string", + "metadata": { + "description": "Subscription ID of the AI Services resource" + } + }, + "aiSearchName": { + "type": "string", + "metadata": { + "description": "Name AI Search resource" + } + }, + "aiSearchId": { + "type": "string", + "metadata": { + "description": "Resource ID of the AI Search resource" + } + }, + "aiSearchServiceResourceGroupName": { + "type": "string", + "metadata": { + "description": "Resource Group name of the AI Search resource" + } + }, + "aiSearchServiceSubscriptionId": { + "type": "string", + "metadata": { + "description": "Subscription ID of the AI Search resource" + } + }, + "aiServiceKind": { + "type": "string", + "metadata": { + "description": "AI Service Account kind: either OpenAI or AIServices" + } + } + }, + "variables": { + "acsConnectionName": "[format('{0}-connection-AISearch', parameters('aiHubName'))]", + "aoaiConnection": "[format('{0}-connection-AIServices_aoai', parameters('aiHubName'))]", + "kindAIServicesExists": "[equals(parameters('aiServiceKind'), 'AIServices')]", + "aiServiceConnectionName": "[if(variables('kindAIServicesExists'), format('{0}-connection-AIServices', parameters('aiHubName')), variables('aoaiConnection'))]" + }, + "resources": [ + { + "type": "Microsoft.MachineLearningServices/workspaces/connections", + "apiVersion": "2024-10-01-preview", + "name": "[format('{0}/{1}', parameters('aiHubName'), variables('aiServiceConnectionName'))]", + "properties": { + "category": "[parameters('aiServiceKind')]", + "target": "[parameters('aiServicesTarget')]", + "authType": "AAD", + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "ResourceId": "[parameters('aiServicesId')]", + "location": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('aiServiceAccountSubscriptionId'), parameters('aiServiceAccountResourceGroupName')), 'Microsoft.CognitiveServices/accounts', parameters('aiServicesName')), '2024-10-01', 'full').location]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('aiHubName'))]" + ] + }, + { + "type": "Microsoft.MachineLearningServices/workspaces/connections", + "apiVersion": "2024-10-01-preview", + "name": "[format('{0}/{1}', parameters('aiHubName'), variables('acsConnectionName'))]", + "properties": { + "category": "CognitiveSearch", + "target": "[format('https://{0}.search.windows.net', parameters('aiSearchName'))]", + "authType": "AAD", + "isSharedToAll": true, + "metadata": { + "ApiType": "Azure", + "ResourceId": "[parameters('aiSearchId')]", + "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.MachineLearningServices/workspaces', parameters('aiHubName'))]" + ] + }, + { + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2024-10-01-preview", + "name": "[parameters('aiHubName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "friendlyName": "[parameters('aiHubFriendlyName')]", + "description": "[parameters('aiHubDescription')]", + "keyVault": "[parameters('keyVaultId')]", + "storageAccount": "[parameters('storageAccountId')]", + "systemDatastoresAuthMode": "identity" + }, + "kind": "hub" + } + ], + "outputs": { + "aiHubID": { + "type": "string", + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('aiHubName'))]" + }, + "aiHubName": { + "type": "string", + "value": "[parameters('aiHubName')]" + }, + "aoaiConnectionName": { + "type": "string", + "value": "[variables('aoaiConnection')]" + }, + "acsConnectionName": { + "type": "string", + "value": "[variables('acsConnectionName')]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "aiProjectName": { + "value": "[format('{0}-{1}', variables('projectName'), variables('uniqueSuffix'))]" + }, + "aiProjectFriendlyName": { + "value": "[parameters('aiProjectFriendlyName')]" + }, + "aiProjectDescription": { + "value": "[parameters('aiProjectDescription')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "aiHubId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiHubID.value]" + }, + "cosmosDBName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.cosmosDBName.value]" + }, + "cosmosDBSubscriptionId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.cosmosDBSubscriptionId.value]" + }, + "cosmosDBResourceGroupName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.cosmosDBResourceGroupName.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.33.93.31351", + "templateHash": "2930065960155969856" + } + }, + "parameters": { + "location": { + "type": "string", + "metadata": { + "description": "Azure region of the deployment" + } + }, + "tags": { + "type": "object", + "metadata": { + "description": "Tags to add to the resources" + } + }, + "aiProjectName": { + "type": "string", + "metadata": { + "description": "AI Project name" + } + }, + "aiProjectFriendlyName": { + "type": "string", + "defaultValue": "[parameters('aiProjectName')]", + "metadata": { + "description": "AI Project display name" + } + }, + "aiProjectDescription": { + "type": "string", + "metadata": { + "description": "AI Project description" + } + }, + "aiHubId": { + "type": "string", + "metadata": { + "description": "Resource ID of the AI Hub resource" + } + }, + "cosmosDBName": { + "type": "string", + "metadata": { + "description": "The name of the CosmosDB account" + } + }, + "cosmosDBSubscriptionId": { + "type": "string", + "metadata": { + "description": "Subscription ID of the Cosmos DB resource" + } + }, + "cosmosDBResourceGroupName": { + "type": "string", + "metadata": { + "description": "Resource Group name of the Cosmos DB resource" + } + } + }, + "variables": { + "subscriptionId": "[subscription().subscriptionId]", + "resourceGroupName": "[resourceGroup().name]", + "projectConnectionString": "[format('{0}.api.azureml.ms;{1};{2};{3}', parameters('location'), variables('subscriptionId'), variables('resourceGroupName'), parameters('aiProjectName'))]", + "cosmosConnectionName": "[format('{0}-connection-CosmosDBAccount', parameters('aiProjectName'))]" + }, + "resources": [ + { + "type": "Microsoft.MachineLearningServices/workspaces", + "apiVersion": "2024-10-01-preview", + "name": "[parameters('aiProjectName')]", + "location": "[parameters('location')]", + "tags": "[union(parameters('tags'), createObject('ProjectConnectionString', variables('projectConnectionString')))]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "friendlyName": "[parameters('aiProjectFriendlyName')]", + "description": "[parameters('aiProjectDescription')]", + "hubResourceId": "[parameters('aiHubId')]" + }, + "kind": "project" + }, + { + "type": "Microsoft.MachineLearningServices/workspaces/connections", + "apiVersion": "2025-01-01-preview", + "name": "[format('{0}/{1}', parameters('aiProjectName'), variables('cosmosConnectionName'))]", + "properties": { + "category": "CosmosDB", + "target": "[format('https://{0}documents.azure.com:443/', parameters('cosmosDBName'))]", + "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.MachineLearningServices/workspaces', parameters('aiProjectName'))]" + ] + } + ], + "outputs": { + "cosmosConnectionName": { + "type": "string", + "value": "[variables('cosmosConnectionName')]" + }, + "aiProjectName": { + "type": "string", + "value": "[parameters('aiProjectName')]" + }, + "aiProjectResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.MachineLearningServices/workspaces', parameters('aiProjectName'))]" + }, + "aiProjectPrincipalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('aiProjectName')), '2024-10-01-preview', 'full').identity.principalId]" + }, + "aiProjectWorkspaceId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('aiProjectName')), '2024-10-01-preview').workspaceId]" + }, + "projectConnectionString": { + "type": "string", + "value": "[reference(resourceId('Microsoft.MachineLearningServices/workspaces', parameters('aiProjectName')), '2024-10-01-preview', 'full').tags.ProjectConnectionString]" + } + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('ai-service-role-assignments-{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))]", + "subscriptionId": "[variables('aiServiceAccountSubscriptionId')]", + "resourceGroup": "[variables('aiServiceAccountResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "aiServicesName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiServicesName.value]" + }, + "aiProjectPrincipalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectPrincipalId.value]" + }, + "aiProjectId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectResourceId.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.33.93.31351", + "templateHash": "8273307596853107787" + } + }, + "parameters": { + "aiServicesName": { + "type": "string", + "metadata": { + "description": "Name of the AI Services resource" + } + }, + "aiProjectPrincipalId": { + "type": "string", + "metadata": { + "description": "Principal ID of the AI project" + } + }, + "aiProjectId": { + "type": "string", + "metadata": { + "description": "Resource ID of the AI project" + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('aiServicesName'))]", + "name": "[guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')), resourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68'), parameters('aiProjectId'))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68')]", + "principalType": "ServicePrincipal" + } + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('aiServicesName'))]", + "name": "[guid(parameters('aiProjectId'), resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'), resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')]", + "principalType": "ServicePrincipal" + } + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('aiServicesName'))]", + "name": "[guid(parameters('aiProjectId'), resourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908'), resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'a97b65f3-24c7-4388-baec-2e87135dc908')]", + "principalType": "ServicePrincipal" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('ai-search-role-assignments-{0}-{1}-deployment', variables('projectName'), 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}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiSearchName.value]" + }, + "aiProjectPrincipalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectPrincipalId.value]" + }, + "aiProjectId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectResourceId.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.33.93.31351", + "templateHash": "13082605048606812458" + } + }, + "parameters": { + "aiSearchName": { + "type": "string", + "metadata": { + "description": "Name of the AI Search resource" + } + }, + "aiProjectPrincipalId": { + "type": "string", + "metadata": { + "description": "Principal ID of the AI project" + } + }, + "aiProjectId": { + "type": "string", + "metadata": { + "description": "Resource 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('aiProjectId'), resourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "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('aiProjectId'), resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0'), resourceId('Microsoft.Search/searchServices', parameters('aiSearchName')))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", + "principalType": "ServicePrincipal" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('cosmos-account-role-assignments-{0}-{1}-deployment', variables('projectName'), 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}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.cosmosDBName.value]" + }, + "aiProjectPrincipalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectPrincipalId.value]" + }, + "projectWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectWorkspaceId.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.33.93.31351", + "templateHash": "17725763585430388696" + } + }, + "parameters": { + "cosmosDBName": { + "type": "string", + "metadata": { + "description": "Name of the AI Search resource" + } + }, + "aiProjectPrincipalId": { + "type": "string", + "metadata": { + "description": "Principal ID of the AI project" + } + }, + "projectWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('cosmosDBName'))]", + "name": "[guid(parameters('projectWorkspaceId'), resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa'), resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('cosmosDBName')))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '230815da-be43-4aae-9cb4-875f7bd000aa')]", + "principalType": "ServicePrincipal" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('capabilityHost-configuration--{0}-deployment', variables('uniqueSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "capabilityHostName": { + "value": "[format('{0}-{1}', variables('uniqueSuffix'), parameters('capabilityHostName'))]" + }, + "aiHubName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiHubName.value]" + }, + "aiProjectName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectName.value]" + }, + "acsConnectionName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.acsConnectionName.value]" + }, + "aoaiConnectionName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.aoaiConnectionName.value]" + }, + "cosmosConnectionName": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.cosmosConnectionName.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.33.93.31351", + "templateHash": "12191138155587807529" + } + }, + "parameters": { + "aiHubName": { + "type": "string", + "metadata": { + "description": "AI hub name" + } + }, + "aiProjectName": { + "type": "string", + "metadata": { + "description": "AI project name" + } + }, + "acsConnectionName": { + "type": "string", + "metadata": { + "description": "Name for ACS connection." + } + }, + "aoaiConnectionName": { + "type": "string", + "metadata": { + "description": "Name for ACS connection." + } + }, + "capabilityHostName": { + "type": "string", + "metadata": { + "description": "Name for capabilityHost." + } + }, + "cosmosConnectionName": { + "type": "string", + "metadata": { + "description": "Name for CosmosDB connection." + } + } + }, + "variables": { + "storageConnections": [ + "[format('{0}/workspaceblobstore', parameters('aiProjectName'))]" + ], + "aiSearchConnection": [ + "[format('{0}', parameters('acsConnectionName'))]" + ], + "aiServiceConnections": [ + "[format('{0}', parameters('aoaiConnectionName'))]" + ], + "cosmosDBConnections": [ + "[format('{0}', parameters('cosmosConnectionName'))]" + ] + }, + "resources": [ + { + "type": "Microsoft.MachineLearningServices/workspaces/capabilityHosts", + "apiVersion": "2025-01-01-preview", + "name": "[format('{0}/{1}', parameters('aiHubName'), format('{0}-{1}', parameters('aiHubName'), parameters('capabilityHostName')))]", + "properties": { + "capabilityHostKind": "Agents" + } + }, + { + "type": "Microsoft.MachineLearningServices/workspaces/capabilityHosts", + "apiVersion": "2025-01-01-preview", + "name": "[format('{0}/{1}', parameters('aiProjectName'), format('{0}-{1}', parameters('aiProjectName'), parameters('capabilityHostName')))]", + "properties": { + "capabilityHostKind": "Agents", + "aiServicesConnections": "[variables('aiServiceConnections')]", + "vectorStoreConnections": "[variables('aiSearchConnection')]", + "storageConnections": "[variables('storageConnections')]", + "threadStorageConnections": "[variables('cosmosDBConnections')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.MachineLearningServices/workspaces/capabilityHosts', parameters('aiHubName'), format('{0}-{1}', parameters('aiHubName'), parameters('capabilityHostName')))]" + ] + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('name'), 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-role-assignments-{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('aiServiceAccountSubscriptionId'), variables('aiServiceAccountResourceGroupName')), 'Microsoft.Resources/deployments', format('ai-service-role-assignments-{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', variables('cosmosDBSubscriptionId'), variables('cosmosDBResourceGroupName')), 'Microsoft.Resources/deployments', format('cosmos-account-role-assignments-{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('cosmos-container-role-assignments-{0}-{1}-deployment', variables('projectName'), 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}-{1}-deployment', variables('name'), variables('uniqueSuffix'))), '2022-09-01').outputs.cosmosDBName.value]" + }, + "aiProjectPrincipalId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectPrincipalId.value]" + }, + "aiProjectId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectResourceId.value]" + }, + "projectWorkspaceId": { + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.aiProjectWorkspaceId.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.33.93.31351", + "templateHash": "4005997312279995865" + } + }, + "parameters": { + "cosmosAccountName": { + "type": "string", + "metadata": { + "description": "Name of the AI Search resource" + } + }, + "aiProjectPrincipalId": { + "type": "string", + "metadata": { + "description": "Principal ID of the AI project" + } + }, + "aiProjectId": { + "type": "string", + "metadata": { + "description": "Resource ID of the AI project" + } + }, + "projectWorkspaceId": { + "type": "string" + } + }, + "variables": { + "userThreadName": "[format('{0}-thread-message-store', parameters('projectWorkspaceId'))]", + "systemThreadName": "[format('{0}-system-thread-message-store', parameters('projectWorkspaceId'))]", + "roleDefinitionId": "[resourceId('Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', parameters('cosmosAccountName'), '00000000-0000-0000-0000-000000000002')]", + "scopeSystemContainer": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/{2}/dbs/enterprise_memory/colls/{3}', subscription().subscriptionId, resourceGroup().name, parameters('cosmosAccountName'), variables('systemThreadName'))]", + "scopeUserContainer": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.DocumentDB/databaseAccounts/{2}/dbs/enterprise_memory/colls/{3}', subscription().subscriptionId, resourceGroup().name, parameters('cosmosAccountName'), variables('userThreadName'))]" + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('cosmosAccountName'), guid(parameters('aiProjectId'), resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('cosmosAccountName'), 'enterprise_memory', variables('userThreadName')), variables('roleDefinitionId')))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "roleDefinitionId": "[variables('roleDefinitionId')]", + "scope": "[variables('scopeUserContainer')]" + } + }, + { + "type": "Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments", + "apiVersion": "2022-05-15", + "name": "[format('{0}/{1}', parameters('cosmosAccountName'), guid(parameters('aiProjectId'), resourceId('Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers', parameters('cosmosAccountName'), 'enterprise_memory', variables('systemThreadName')), variables('roleDefinitionId')))]", + "properties": { + "principalId": "[parameters('aiProjectPrincipalId')]", + "roleDefinitionId": "[variables('roleDefinitionId')]", + "scope": "[variables('scopeSystemContainer')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Resources/deployments', format('capabilityHost-configuration--{0}-deployment', variables('uniqueSuffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('dependencies-{0}-{1}-deployment', variables('name'), variables('uniqueSuffix')))]", + "[resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix')))]" + ] + } + ], + "outputs": { + "PROJECT_CONNECTION_STRING": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-{1}-deployment', variables('projectName'), variables('uniqueSuffix'))), '2022-09-01').outputs.projectConnectionString.value]" + } + } +} \ No newline at end of file diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/azuredeploy.parameters.json b/scenarios/Agents/setup/standard-agent-with-threadstorage/azuredeploy.parameters.json new file mode 100644 index 00000000..f0c25725 --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/azuredeploy.parameters.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "aiHubName": { + "value": "hub-demo" + }, + "aiHubFriendlyName": { + "value": "Agents Hub resource" + }, + "aiHubDescription": { + "value": "This is an example AI resource for use in Azure AI Studio." + }, + "aiProjectName": { + "value": "project-demo" + }, + "aiProjectFriendlyName": { + "value": "Agents Project resource" + }, + "aiProjectDescription": { + "value": "This is an example AI Project resource for use in Azure AI Studio." + }, + "aiSearchName": { + "value": "agent-ai-search" + }, + "capabilityHostName": { + "value": "caphost1" + }, + "storageName": { + "value": "agent-storage" + }, + "aiServicesName": { + "value": "agent-ai-services" + }, + "modelName": { + "value": "gpt-4o" + }, + "modelFormat": { + "value": "OpenAI" + }, + "modelVersion": { + "value": "2024-08-06" + }, + "modelSkuName": { + "value": "GlobalStandard" + }, + "modelCapacity": { + "value": 50 + }, + "modelLocation": { + "value": "eastus" + }, + "aiServiceAccountResourceId": { + "value": "" + }, + "aiSearchServiceResourceId": { + "value": "" + }, + "aiStorageAccountResourceId":{ + "value": "" + }, + "cosmosDBResourceId":{ + "value": "" + }, + "aiServiceKind": { + "value": "AIServices" + } + } + } + \ No newline at end of file diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/main.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/main.bicep new file mode 100644 index 00000000..f0afd270 --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/main.bicep @@ -0,0 +1,259 @@ +// Execute this main file to deploy Enterprise Standard Agent setup resources + +// Parameters +@minLength(2) +@maxLength(12) +@description('Name for your Azure AI Hub resource.') +param aiHubName string = 'hub-demo' + +@description('Friendly name for your Hub resource') +param aiHubFriendlyName string = 'Agents Hub resource' + +@description('Creating an Azure AI Hub to set up your app environment and Azure resources.') +param aiHubDescription string = 'This is an example AI Hub resource for use in Azure AI Studio.' + +@description('Name for the AI project resources.') +param aiProjectName string = 'project-demo' + +@description('Friendly name for your Azure AI resource') +param aiProjectFriendlyName string = 'Agents Project resource' + +@description('Creating an Azure AI project under your Hub creates an endpoint for your app to call, and sets up app services to access to resources in your tenant.') +param aiProjectDescription string = 'This is an example AI Project resource for use in Azure AI Studio.' + +@description('Azure region used for the deployment of all resources.') +param location string = resourceGroup().location + +@description('Set of tags to apply to all resources.') +param tags object = {} + +@description('Name of the Azure AI Search account') +param aiSearchName string = 'agent-ai-search' + +@description('Name for capabilityHost.') +param capabilityHostName string = 'caphost1' + +@description('Name of the storage account') +param storageName string = 'agent-storage' + +@description('Name of the Azure AI Services account') +param aiServicesName string = 'agent-ai-services' + +@description('Model name for deployment') +param modelName string = 'gpt-4o' + +@description('Model format for deployment') +param modelFormat string = 'OpenAI' + +@description('Model version for deployment') +param modelVersion string = '2024-08-06' + +@description('Model deployment SKU name') +param modelSkuName string = 'GlobalStandard' + +@description('Model deployment capacity') +param modelCapacity int = 50 + +@description('Model deployment location. If you want to deploy an Azure AI resource/model in different location than the rest of the resources created.') +param modelLocation string = 'westus' + +@description('AI Service Account kind: either AzureOpenAI or AIServices') +param aiServiceKind string = 'AIServices' + +@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 = '' + +@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 cosmosDBName string = 'agent-thread-storage' + +// Variables +var name = toLower('${aiHubName}') +var projectName = toLower('${aiProjectName}') + +// Create a short, unique suffix, that will be unique to each resource group +// var uniqueSuffix = substring(uniqueString(resourceGroup().id), 0, 4) +param deploymentTimestamp string = utcNow('yyyyMMddHHmmss') +var uniqueSuffix = substring(uniqueString('${resourceGroup().id}-${deploymentTimestamp}'), 0, 4) + +module validateExistingResources 'modules-standard/validate-existing-resources.bicep' = { + name: 'validate-existing-resources-${name}-${uniqueSuffix}-deployment' + params: { + aiServiceAccountResourceId: aiServiceAccountResourceId + aiSearchServiceResourceId: aiSearchServiceResourceId + aiStorageAccountResourceId: aiStorageAccountResourceId + cosmosDBResourceId: cosmosDBResourceId + } +} + +// Already validated existing resources. Either create new resources or use existing ones because the resource IDs have been validated +var aiServiceExists = aiStorageAccountResourceId != '' +var acsExists = aiSearchServiceResourceId != '' +var cosmosExists = cosmosDBResourceId != '' + +var aiServiceParts = split(aiServiceAccountResourceId, '/') +var aiServiceAccountSubscriptionId = aiServiceExists ? aiServiceParts[2] : subscription().subscriptionId +var aiServiceAccountResourceGroupName = aiServiceExists ? aiServiceParts[4] : resourceGroup().name + +var acsParts = split(aiSearchServiceResourceId, '/') +var aiSearchServiceSubscriptionId = acsExists ? acsParts[2] : subscription().subscriptionId +var aiSearchServiceResourceGroupName = acsExists ? acsParts[4] : resourceGroup().name + +var cosmosParts = split(cosmosDBResourceId, '/') +var cosmosDBSubscriptionId = cosmosExists ? cosmosParts[2] : subscription().subscriptionId +var cosmosDBResourceGroupName = cosmosExists ? cosmosParts[4] : resourceGroup().name + +// Dependent resources for the Azure Machine Learning workspace +module aiDependencies 'modules-standard/standard-dependent-resources.bicep' = { + name: 'dependencies-${name}-${uniqueSuffix}-deployment' + params: { + location: location + storageName: '${storageName}${uniqueSuffix}' + keyvaultName: 'kv-${name}-${uniqueSuffix}' + aiServicesName: '${aiServicesName}${uniqueSuffix}' + aiSearchName: '${aiSearchName}-${uniqueSuffix}' + cosmosDBName: '${cosmosDBName}-${uniqueSuffix}' + tags: tags + + // Model deployment parameters + modelName: modelName + modelFormat: modelFormat + modelVersion: modelVersion + modelSkuName: modelSkuName + modelCapacity: modelCapacity + modelLocation: modelLocation + + // AI Services account parameters + aiServiceAccountResourceId: aiServiceAccountResourceId + aiServiceExists: validateExistingResources.outputs.aiServiceExists + + // AI Search Service parameters + aiSearchServiceResourceId: aiSearchServiceResourceId + aiSearchExists: validateExistingResources.outputs.aiSearchExists + + // Storage Account + aiStorageAccountResourceId: aiStorageAccountResourceId + aiStorageExists: validateExistingResources.outputs.aiStorageExists + + // Cosmos DB Account + cosmosDBResourceId: cosmosDBResourceId + cosmosDBExists: validateExistingResources.outputs.cosmosDBExists + } +} + +module aiHub 'modules-standard/standard-ai-hub.bicep' = { + name: '${name}-${uniqueSuffix}-deployment' + params: { + // workspace organization + aiHubName: '${name}-${uniqueSuffix}' + aiHubFriendlyName: aiHubFriendlyName + aiHubDescription: aiHubDescription + location: location + tags: tags + + + aiSearchName: aiDependencies.outputs.aiSearchName + aiSearchId: aiDependencies.outputs.aisearchID + aiSearchServiceResourceGroupName: aiDependencies.outputs.aiSearchServiceResourceGroupName + aiSearchServiceSubscriptionId: aiDependencies.outputs.aiSearchServiceSubscriptionId + + aiServicesName: aiDependencies.outputs.aiServicesName + aiServiceKind: aiServiceKind + aiServicesId: aiDependencies.outputs.aiservicesID + aiServicesTarget: aiDependencies.outputs.aiservicesTarget + aiServiceAccountResourceGroupName:aiDependencies.outputs.aiServiceAccountResourceGroupName + aiServiceAccountSubscriptionId:aiDependencies.outputs.aiServiceAccountSubscriptionId + + keyVaultId: aiDependencies.outputs.keyvaultId + storageAccountId: aiDependencies.outputs.storageId + } +} + + +module aiProject 'modules-standard/standard-ai-project.bicep' = { + name: '${projectName}-${uniqueSuffix}-deployment' + params: { + // workspace organization + aiProjectName: '${projectName}-${uniqueSuffix}' + aiProjectFriendlyName: aiProjectFriendlyName + aiProjectDescription: aiProjectDescription + location: location + tags: tags + aiHubId: aiHub.outputs.aiHubID + + cosmosDBName: aiDependencies.outputs.cosmosDBName + cosmosDBSubscriptionId: aiDependencies.outputs.cosmosDBSubscriptionId + cosmosDBResourceGroupName: aiDependencies.outputs.cosmosDBResourceGroupName + } +} + +module aiServiceRoleAssignments 'modules-standard/ai-service-role-assignments.bicep' = { + name: 'ai-service-role-assignments-${projectName}-${uniqueSuffix}-deployment' + scope: resourceGroup(aiServiceAccountSubscriptionId, aiServiceAccountResourceGroupName) + params: { + aiServicesName: aiDependencies.outputs.aiServicesName + aiProjectPrincipalId: aiProject.outputs.aiProjectPrincipalId + aiProjectId: aiProject.outputs.aiProjectResourceId + } +} + +module aiSearchRoleAssignments 'modules-standard/ai-search-role-assignments.bicep' = { + name: 'ai-search-role-assignments-${projectName}-${uniqueSuffix}-deployment' + scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) + params: { + aiSearchName: aiDependencies.outputs.aiSearchName + aiProjectPrincipalId: aiProject.outputs.aiProjectPrincipalId + aiProjectId: aiProject.outputs.aiProjectResourceId + } +} + +module cosmosAccountRoleAssignments 'modules-standard/cosmos-account-role-assignments.bicep' = { + name: 'cosmos-account-role-assignments-${projectName}-${uniqueSuffix}-deployment' + scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) + params: { + cosmosDBName: aiDependencies.outputs.cosmosDBName + aiProjectPrincipalId: aiProject.outputs.aiProjectPrincipalId + projectWorkspaceId: aiProject.outputs.aiProjectWorkspaceId + + } +} + +module addCapabilityHost 'modules-standard/add-capability-host.bicep' = { + name: 'capabilityHost-configuration--${uniqueSuffix}-deployment' + params: { + capabilityHostName: '${uniqueSuffix}-${capabilityHostName}' + aiHubName: aiHub.outputs.aiHubName + aiProjectName: aiProject.outputs.aiProjectName + acsConnectionName: aiHub.outputs.acsConnectionName + aoaiConnectionName: aiHub.outputs.aoaiConnectionName + cosmosConnectionName: aiProject.outputs.cosmosConnectionName + } + dependsOn: [ + aiSearchRoleAssignments,aiServiceRoleAssignments, cosmosAccountRoleAssignments + ] +} + + +module cosmosContainerRoleAssignments 'modules-standard/cosmos-container-role-assignment.bicep' = { + name: 'cosmos-container-role-assignments-${projectName}-${uniqueSuffix}-deployment' + scope: resourceGroup(cosmosDBSubscriptionId, cosmosDBResourceGroupName) + params: { + cosmosAccountName: aiDependencies.outputs.cosmosDBName + aiProjectPrincipalId: aiProject.outputs.aiProjectPrincipalId + aiProjectId: aiProject.outputs.aiProjectResourceId + projectWorkspaceId: aiProject.outputs.aiProjectWorkspaceId + +} +dependsOn: [ + addCapabilityHost + ] +} + +output PROJECT_CONNECTION_STRING string = aiProject.outputs.projectConnectionString diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/metadata.json b/scenarios/Agents/setup/standard-agent-with-threadstorage/metadata.json new file mode 100644 index 00000000..47319a5c --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/metadata.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://aka.ms/azure-quickstart-templates-metadata-schema#", + "type": "QuickStart", + "itemDisplayName": "Standard Agent Setup", + "description": "This set of templates demonstrates how to set up Azure AI Agent Service with the standard setup, meaning with managed identity authentication for project/hub connections and public internet access enabled. Agents use customer-owned, single-tenant search and storage resources. With this setup, you have full control and visibility over these resources, but you will incur costs based on your usage.", + "summary": "This set of templates demonstrates how to use Azure AI Agent Service with the standard setup.", + "githubUsername": "fosteramanda", + "dateUpdated": "2025-02-11", + "environments": [ + "AzureCloud" + ] + } \ No newline at end of file diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/add-capability-host.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/add-capability-host.bicep new file mode 100644 index 00000000..2396bb29 --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/add-capability-host.bicep @@ -0,0 +1,57 @@ +@description('AI hub name') +param aiHubName string + +@description('AI project name') +param aiProjectName string + +@description('Name for ACS connection.') +param acsConnectionName string + +@description('Name for ACS connection.') +param aoaiConnectionName string + +@description('Name for capabilityHost.') +param capabilityHostName string + +@description('Name for CosmosDB connection.') +param cosmosConnectionName string + +var storageConnections = ['${aiProjectName}/workspaceblobstore'] +var aiSearchConnection = ['${acsConnectionName}'] +var aiServiceConnections = ['${aoaiConnectionName}'] +var cosmosDBConnections = ['${cosmosConnectionName}'] + +#disable-next-line BCP081 +resource aiHub 'Microsoft.MachineLearningServices/workspaces@2024-10-01-preview' existing = { + name: aiHubName +} + +#disable-next-line BCP081 +resource aiProject 'Microsoft.MachineLearningServices/workspaces@2024-10-01-preview' existing = { + name: aiProjectName +} + +#disable-next-line BCP081 +resource hubCapabilityHost 'Microsoft.MachineLearningServices/workspaces/capabilityHosts@2025-01-01-preview' = { + name: '${aiHubName}-${capabilityHostName}' + parent: aiHub + properties: { + capabilityHostKind: 'Agents' + } +} + +#disable-next-line BCP081 +resource projectCapabilityHost 'Microsoft.MachineLearningServices/workspaces/capabilityHosts@2025-01-01-preview' = { + name: '${aiProjectName}-${capabilityHostName}' + parent: aiProject + properties: { + capabilityHostKind: 'Agents' + aiServicesConnections: aiServiceConnections + vectorStoreConnections: aiSearchConnection + storageConnections: storageConnections + threadStorageConnections: cosmosDBConnections + } + dependsOn: [ + hubCapabilityHost + ] +} diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/ai-search-role-assignments.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/ai-search-role-assignments.bicep new file mode 100644 index 00000000..eb8e8b5e --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/ai-search-role-assignments.bicep @@ -0,0 +1,46 @@ +// 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 aiProjectPrincipalId string + +@description('Resource ID of the AI project') +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/setup/standard-agent-with-threadstorage/modules-standard/ai-service-role-assignments.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/ai-service-role-assignments.bicep new file mode 100644 index 00000000..d4ad1dd1 --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/ai-service-role-assignments.bicep @@ -0,0 +1,60 @@ +// Assigns the necessary roles to the AI project + +@description('Name of the AI Services resource') +param aiServicesName string + +@description('Principal ID of the AI project') +param aiProjectPrincipalId string + +@description('Resource ID of the AI project') +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/setup/standard-agent-with-threadstorage/modules-standard/cosmos-account-role-assignments.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/cosmos-account-role-assignments.bicep new file mode 100644 index 00000000..e9b3c1d2 --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/cosmos-account-role-assignments.bicep @@ -0,0 +1,29 @@ +// Assigns the necessary roles to the AI project + +@description('Name of the AI Search resource') +param cosmosDBName string + +@description('Principal ID of the AI project') +param aiProjectPrincipalId string + +param projectWorkspaceId 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(projectWorkspaceId, cosmosDBOperatorRole.id, cosmosDBAccount.id) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: cosmosDBOperatorRole.id + principalType: 'ServicePrincipal' + } +} diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/cosmos-container-role-assignment.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/cosmos-container-role-assignment.bicep new file mode 100644 index 00000000..eedefafb --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/cosmos-container-role-assignment.bicep @@ -0,0 +1,67 @@ +// Assigns the necessary roles to the AI project + +@description('Name of the AI Search resource') +param cosmosAccountName string + +@description('Principal ID of the AI project') +param aiProjectPrincipalId string + +@description('Resource ID of the AI project') +param aiProjectId string + +param projectWorkspaceId string + +var userThreadName = '${projectWorkspaceId}-thread-message-store' +var systemThreadName = '${projectWorkspaceId}-system-thread-message-store' + + +resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { + name: cosmosAccountName + scope: resourceGroup() +} + +// Reference existing database +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2024-12-01-preview' existing = { + parent: cosmosAccount + name: 'enterprise_memory' +} + +resource containerUserMessageStore 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-12-01-preview' existing = { + parent: database + name: userThreadName +} + +resource containerSystemMessageStore 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2024-12-01-preview' existing = { + parent: database + name: systemThreadName +} + + +var roleDefinitionId = resourceId( + 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions', + cosmosAccountName, + '00000000-0000-0000-0000-000000000002' +) + +var scopeSystemContainer = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccountName}/dbs/enterprise_memory/colls/${systemThreadName}' +var scopeUserContainer = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccountName}/dbs/enterprise_memory/colls/${userThreadName}' + +resource containerRoleAssignmentUserContainer 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmosAccount + name: guid(aiProjectId, containerUserMessageStore.id, roleDefinitionId) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: roleDefinitionId + scope: scopeUserContainer + } +} + +resource containerRoleAssignmentSystemContainer 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmosAccount + name: guid(aiProjectId, containerSystemMessageStore.id, roleDefinitionId) + properties: { + principalId: aiProjectPrincipalId + roleDefinitionId: roleDefinitionId + scope: scopeSystemContainer + } +} diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-ai-hub.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-ai-hub.bicep new file mode 100644 index 00000000..3286bbbf --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-ai-hub.bicep @@ -0,0 +1,127 @@ +// 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 + +@description('Resource Group name of the AI Search resource') +param aiSearchServiceResourceGroupName string + +@description('Subscription ID of the AI Search resource') +param aiSearchServiceSubscriptionId string + +@description('AI Service Account kind: either OpenAI or AIServices') +param aiServiceKind string + +var acsConnectionName = '${aiHubName}-connection-AISearch' + +var aoaiConnection = '${aiHubName}-connection-AIServices_aoai' + +var kindAIServicesExists = aiServiceKind == 'AIServices' + +var aiServiceConnectionName = kindAIServicesExists ? '${aiHubName}-connection-AIServices' : aoaiConnection + +resource aiServices 'Microsoft.CognitiveServices/accounts@2024-10-01' existing = { + name: aiServicesName + scope: resourceGroup(aiServiceAccountSubscriptionId, aiServiceAccountResourceGroupName) +} + +resource searchService 'Microsoft.Search/searchServices@2024-06-01-preview' existing = { + name: aiSearchName + scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) +} + +resource aiHub 'Microsoft.MachineLearningServices/workspaces@2024-10-01-preview' = { + name: aiHubName + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + // organization + friendlyName: aiHubFriendlyName + description: aiHubDescription + + // dependent resources + keyVault: keyVaultId + storageAccount: storageAccountId + systemDatastoresAuthMode: 'identity' + } + kind: 'hub' + + resource aiServicesConnection 'connections@2024-10-01-preview' = { + name: aiServiceConnectionName + properties: { + category: aiServiceKind // either AIServices or AzureOpenAI + target: aiServicesTarget + authType: 'AAD' + isSharedToAll: true + metadata: { + ApiType: 'Azure' + ResourceId: aiServicesId + location: aiServices.location + } + } + } + + resource hub_connection_azureai_search 'connections@2024-10-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 + } + } + } + +} + +output aiHubID string = aiHub.id +output aiHubName string = aiHub.name +output aoaiConnectionName string = aoaiConnection +output acsConnectionName string = acsConnectionName diff --git a/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-ai-project.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-ai-project.bicep new file mode 100644 index 00000000..7a4446dc --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-ai-project.bicep @@ -0,0 +1,86 @@ +// 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('The name of the CosmosDB account') +param cosmosDBName string + +@description('Subscription ID of the Cosmos DB resource') +param cosmosDBSubscriptionId string + +@description('Resource Group name of the Cosmos DB resource') +param cosmosDBResourceGroupName string + +//for constructing endpoint +var subscriptionId = subscription().subscriptionId +var resourceGroupName = resourceGroup().name + +var projectConnectionString = '${location}.api.azureml.ms;${subscriptionId};${resourceGroupName};${aiProjectName}' + +var cosmosConnectionName = '${aiProjectName}-connection-CosmosDBAccount' + +resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { + name: cosmosDBName + scope: resourceGroup(cosmosDBSubscriptionId,cosmosDBResourceGroupName) +} + +#disable-next-line BCP081 +resource aiProject 'Microsoft.MachineLearningServices/workspaces@2024-10-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' +} + +#disable-next-line BCP081 +resource project_connection_cosmosdb 'Microsoft.MachineLearningServices/workspaces/connections@2025-01-01-preview' = { + name: cosmosConnectionName + parent: aiProject + properties: { + category: 'CosmosDB' + target: 'https://${cosmosDBName}documents.azure.com:443/' + authType: 'AAD' + metadata: { + ApiType: 'Azure' + ResourceId: cosmosDBAccount.id + location: cosmosDBAccount.location + } + } +} + +output cosmosConnectionName string = project_connection_cosmosdb.name +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/setup/standard-agent-with-threadstorage/modules-standard/standard-dependent-resources.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-dependent-resources.bicep new file mode 100644 index 00000000..d1b3f323 --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/standard-dependent-resources.bicep @@ -0,0 +1,243 @@ +// Creates Azure dependent resources for Azure AI Agent Service standard agent setup + +@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 + +param cosmosDBName 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 + +@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 aiStorageExists 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]) +} + +var canaryRegions = ['eastus2euap', 'centraluseuap'] +var cosmosDbRegion = contains(canaryRegions, location) ? 'westus' : location + +resource cosmosDB 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' = if(!cosmosDBExists) { + name: cosmosDBName + location: cosmosDbRegion + tags: tags + kind: 'GlobalDocumentDB' + properties: { + consistencyPolicy: { + defaultConsistencyLevel: 'Session' + } + disableLocalAuth: true + enableAutomaticFailover: false + enableMultipleWriteLocations: false + enableFreeTier: false + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + } +} + + +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@2024-10-01' existing = if (aiServiceExists) { + name: aiServiceParts[8] + scope: resourceGroup(aiServiceParts[2], aiServiceParts[4]) +} + +resource aiServices 'Microsoft.CognitiveServices/accounts@2024-10-01' = 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-10-01'= 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@2024-06-01-preview' existing = if (aiSearchExists) { + name: acsParts[8] + scope: resourceGroup(acsParts[2], acsParts[4]) +} +resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = if(!aiSearchExists) { + 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@2023-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 = 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 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 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/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/validate-existing-resources.bicep b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/validate-existing-resources.bicep new file mode 100644 index 00000000..5c4a8d1a --- /dev/null +++ b/scenarios/Agents/setup/standard-agent-with-threadstorage/modules-standard/validate-existing-resources.bicep @@ -0,0 +1,74 @@ +@description('Resource ID of the AI Service Account. ') +param aiServiceAccountResourceId string + +@description('Resource ID of the Azure AI Search Service.') +param aiSearchServiceResourceId string = '' + +@description('Resource ID of the Azure Storage Account.') +param aiStorageAccountResourceId string = '' + +@description('ResourceId of Cosmos DB Account') +param cosmosDBResourceId string = '' + +var storagePassedIn = aiStorageAccountResourceId != '' +var aiServicesPassedIn = aiServiceAccountResourceId != '' +var searchPassedIn = aiSearchServiceResourceId != '' +var cosmosPassedIn = cosmosDBResourceId != '' + + +var aiServiceParts = split(aiServiceAccountResourceId, '/') +var aiServiceAccountSubscriptionId = aiServicesPassedIn ? aiServiceParts[2] : subscription().subscriptionId +var aiServiceAccountResourceGroupName = aiServicesPassedIn ? aiServiceParts[4] : resourceGroup().name + +var acsParts = split(aiSearchServiceResourceId, '/') +var aiSearchServiceSubscriptionId = searchPassedIn ? acsParts[2] : subscription().subscriptionId +var aiSearchServiceResourceGroupName = searchPassedIn ? acsParts[4] : resourceGroup().name + +var cosmosParts = split(cosmosDBResourceId, '/') +var cosmosDBSubscriptionId = cosmosPassedIn ? cosmosParts[2] : subscription().subscriptionId +var cosmosDBResourceGroupName = cosmosPassedIn ? cosmosParts[4] : resourceGroup().name + +var storageParts = split(aiStorageAccountResourceId, '/') +var azureStorageSubscriptionId = storagePassedIn ? storageParts[2] : subscription().subscriptionId +var azureStorageResourceGroupName = storagePassedIn ? storageParts[4] : resourceGroup().name + +resource aiServiceAccount 'Microsoft.CognitiveServices/accounts@2023-10-01-preview' existing = if (aiServicesPassedIn) { + name: last(split(aiServiceAccountResourceId, '/')) + scope: resourceGroup(aiServiceAccountSubscriptionId, aiServiceAccountResourceGroupName) +} + +// Validate Azure AI Search +resource azureAISearch 'Microsoft.Search/searchServices@2024-06-01-preview' existing = if (searchPassedIn) { + name: last(split(aiSearchServiceResourceId, '/')) + scope: resourceGroup(aiSearchServiceSubscriptionId, aiSearchServiceResourceGroupName) +} + + +// Validate Cosmos DB Account +resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = if (cosmosPassedIn) { + name: last(split(cosmosDBResourceId, '/')) + scope: resourceGroup(cosmosDBSubscriptionId,cosmosDBResourceGroupName) +} + +// Validate Storage Account +resource azureStorageAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = if (storagePassedIn) { + name: storageParts[8] + scope: resourceGroup(azureStorageSubscriptionId,azureStorageResourceGroupName) +} + +output aiServiceExists bool = aiServicesPassedIn && (aiServiceAccount.name == aiServiceParts[8]) +output aiSearchExists bool = searchPassedIn && (azureAISearch.name == acsParts[8]) +output cosmosDBExists bool = cosmosPassedIn && (cosmosDBAccount.name == cosmosParts[8]) +output aiStorageExists bool = storagePassedIn && (azureStorageAccount.name == storageParts[8]) + +output aiSearchServiceSubscriptionId string = aiSearchServiceSubscriptionId +output aiSearchServiceResourceGroupName string = aiSearchServiceResourceGroupName + +output cosmosDBSubscriptionId string = cosmosDBSubscriptionId +output cosmosDBResourceGroupName string = cosmosDBResourceGroupName + +output aiServiceAccountSubscriptionId string = aiServiceAccountSubscriptionId +output aiServiceAccountResourceGroupName string = aiServiceAccountResourceGroupName + +output azureStorageSubscriptionId string = azureStorageSubscriptionId +output azureStorageResourceGroupName string = azureStorageResourceGroupName