From e7c996b9381b398cbd5ed258536956b2f2515080 Mon Sep 17 00:00:00 2001 From: Dennis Eikelenboom Date: Tue, 29 Apr 2025 22:45:50 -0700 Subject: [PATCH 1/2] template examples --- .../00-basic/azuredeploy.json | 76 -------- .../00-basic/main.bicep | 51 ++++- .../01-connections/ai-search.bicep | 50 +++++ .../10-private-network/deploy.json | 45 +++++ .../20-user-assigned-identity/main.bicep | 41 ++-- .../30-customer-managed-keys/azuredeploy.json | 144 -------------- .../azuredeploy.parameters.json | 39 ---- .../30-customer-managed-keys/main.bicep | 71 +++---- .../step1-provision-resources.bicep | 184 ++++++++++++++++++ .../step2-grant-role-assignments.bicep | 75 +++++++ .../step3-create-capability-host.bicep | 57 ++++++ 11 files changed, 512 insertions(+), 321 deletions(-) delete mode 100644 use-cases/infrastructure-as-code/00-basic/azuredeploy.json create mode 100644 use-cases/infrastructure-as-code/01-connections/ai-search.bicep create mode 100644 use-cases/infrastructure-as-code/10-private-network/deploy.json delete mode 100644 use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.json delete mode 100644 use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.parameters.json create mode 100644 use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep create mode 100644 use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep create mode 100644 use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep diff --git a/use-cases/infrastructure-as-code/00-basic/azuredeploy.json b/use-cases/infrastructure-as-code/00-basic/azuredeploy.json deleted file mode 100644 index d74297770..000000000 --- a/use-cases/infrastructure-as-code/00-basic/azuredeploy.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "aiServicesName": { - "type": "string", - "defaultValue": "AIServices-viaARMtemplate", - "metadata": { - "description": "This is our AIServices resource template." - } - }, - "location": { - "type": "string", - "defaultValue": "East US 2", - "metadata": { - "description": "Location for all resources." - } - }, - "sku": { - "type": "string", - "defaultValue": "S0", - "allowedValues": [ - "S0" - ] - }, - "aiProjectName": { - "type": "string", - "defaultValue": "AIProject-viaARMtemplate", - "metadata": { - "description": "This will be the Project's name." - } - }, - "PNAflag": { - "type": "string", - "defaultValue": "Enabled", - "metadata": { - "description": "Public Network Access flag default is Enabled." - } - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('aiServicesName')]", - "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, - "sku": { - "name": "[parameters('sku')]" - }, - "kind": "AIServices", - "properties": { - "publicNetworkAccess": "Enabled", - "allowProjectManagement": true - } - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('aiProjectName'))]", - "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, - "dependsOn": [ - "[resourceID('Microsoft.CognitiveServices/accounts', parameters('aiServicesName'))]" - ], - "properties": { - "publicNetworkAccess": "Enabled", - "allowProjectManagement": true - } - } - ] - } \ No newline at end of file diff --git a/use-cases/infrastructure-as-code/00-basic/main.bicep b/use-cases/infrastructure-as-code/00-basic/main.bicep index 52a33e315..fad087fb2 100644 --- a/use-cases/infrastructure-as-code/00-basic/main.bicep +++ b/use-cases/infrastructure-as-code/00-basic/main.bicep @@ -1,28 +1,61 @@ -param aiServicesName string = 'myaiservices123' -param location string = 'East US 2' -param sku string = 'S0' -param aiProjectName string = 'myaiservices123-proj' +param aiFoundryName string = 'resourcename' +param aiProjectName string = '${aiFoundryName}-proj' +param location string = 'westus' -resource aiService 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { - name: aiServicesName +/* + An AI Foundry resources is a variant of a CognitiveServices/account resource type +*/ +resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { + name: aiFoundryName location: location identity: { type: 'SystemAssigned' } sku: { - name: sku + name: 'S0' } kind: 'AIServices' properties: { - allowProjectManagement: true + allowProjectManagement: true // required to work in AI Foundry + + // Defines developer API endpoint subdomain + customSubDomainName: aiFoundryName } } +/* + Developer APIs are exposed via a project, which groups in- and outputs that relate to one use case, including files. + Its advisable to create one project right away, so development teams can directly get started. + Projects may be granted individual RBAC permissions and identities on top of what account provides. +*/ resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { name: aiProjectName - parent: aiService + parent: aiFoundry location: location identity: { type: 'SystemAssigned' } + properties: { + displayName: 'test' + description: 'test2' + isDefault: true + } +} + +/* + Optionally deploy a model to use in playground, agents and other tools. +*/ +resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01'= { + parent: aiFoundry + name: 'gpt-4o' + sku : { + capacity: 1 + name: 'GlobalStandard' + } + properties: { + model:{ + name: 'gpt-4o' + format: 'OpenAI' + } + } } diff --git a/use-cases/infrastructure-as-code/01-connections/ai-search.bicep b/use-cases/infrastructure-as-code/01-connections/ai-search.bicep new file mode 100644 index 000000000..42c8ed959 --- /dev/null +++ b/use-cases/infrastructure-as-code/01-connections/ai-search.bicep @@ -0,0 +1,50 @@ +/* +This example demonstrates how to add an Azure AI Search connection. +*/ +param aiFoundryName string = 'your-account' +param aiSearchName string = 'ais-${aiFoundryName}' + +// whether ai Search is existing or new +@allowed([ + 'new' + 'existing' +]) +param newOrExisting string = 'new' + +#disable-next-line BCP081 +resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: aiFoundryName + scope: resourceGroup() +} + +resource existingSearchService 'Microsoft.Search/searchServices@2025-02-01-preview' existing = if (newOrExisting == 'existing') { + name: aiSearchName +} + +resource newSearchService 'Microsoft.Search/searchServices@2025-02-01-preview' = if (newOrExisting == 'new') { + name: aiSearchName + location: 'westus' + sku: { + name: 'basic' + } + properties: {} +} + +resource project_connection_azureai_search 'Microsoft.CognitiveServices/accounts/connections@2025-04-01-preview' = { + name: aiSearchName + parent: aiFoundry + properties: { + category: 'CognitiveSearch' + target: ((newOrExisting == 'new') ? newSearchService.properties.endpoint : existingSearchService.properties.endpoint) + authType: 'ApiKey' + isSharedToAll: true + credentials: { + key: ((newOrExisting == 'new') ? listKeys(newSearchService.id, '2020-06-10').key1 : listKeys(existingSearchService.id, '2020-06-10').key1) + } + metadata: { + ApiType: 'Azure' + ResourceId: ((newOrExisting == 'new') ? newSearchService.id : existingSearchService.id) + location: ((newOrExisting == 'new') ? newSearchService.location : existingSearchService.location) + } + } +} diff --git a/use-cases/infrastructure-as-code/10-private-network/deploy.json b/use-cases/infrastructure-as-code/10-private-network/deploy.json new file mode 100644 index 000000000..bcfcafb32 --- /dev/null +++ b/use-cases/infrastructure-as-code/10-private-network/deploy.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "accounts_sansinhtest19871_name": { + "defaultValue": "sansinhtest199919", + "type": "String" + } + }, + "variables": {}, + "resources": [ + { + "type": "Microsoft.CognitiveServices/accounts", + "apiVersion": "2024-10-01", + "name": "[parameters('accounts_sansinhtest19871_name')]", + "location": "eastus", + "sku": { + "name": "S0" + }, + + "kind": "AIServices", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "allowProjectManagement": true, + "customSubDomainName": "[parameters('accounts_sansinhtest19871_name')]", + "networkAcls": { + "defaultAction": "Allow", + "virtualNetworkRules": [], + "ipRules": [] + }, + "publicNetworkAccess": "Enabled", + "networkInjections":[ + { + "scenario": "agent", + "subnetArmId" : "/subscriptions/a9216f37-b90e-4db2-b844-b171e5394fc1/resourceGroups/sansinhtest/providers/Microsoft.Network/virtualNetworks/sansinheert/subnets/default", + "useMicrosoftManagedNetwork": false + } + ] + } + + } + ] +} \ No newline at end of file diff --git a/use-cases/infrastructure-as-code/20-user-assigned-identity/main.bicep b/use-cases/infrastructure-as-code/20-user-assigned-identity/main.bicep index 0453f55ee..f6350b07f 100644 --- a/use-cases/infrastructure-as-code/20-user-assigned-identity/main.bicep +++ b/use-cases/infrastructure-as-code/20-user-assigned-identity/main.bicep @@ -2,40 +2,41 @@ AI Foundry account and project - with your User-Assigned managed identity. Description: - - Create an AI Foundry (previously known as Azure AI Services) account and project with UAI. - - Create a gpt-4o model deployment - - When creating a project, the Identity is not updateable. Please select 'SystemAssigned', 'UserAssigned' or 'SystemAssigned,UserAssigned' during creation as this cannot be updated. . - - Creating your first project is needed to support more capabilities and is the default reoute for APIs if no paramter is provided. + - Creates an AI Foundry (previously known as Azure AI Services) account and project with UAI. + - Creates a gpt-4o model deployment + + Known limitations: + - When creating a project, managed identity cannot be updated. Please select 'SystemAssigned', 'UserAssigned' or 'SystemAssigned,UserAssigned' during creation. */ @description('That name is the name of our application. It has to be unique. Type a name followed by your resource group name. (-)') -param aiServicesName string = 'aiServices-${uniqueString(resourceGroup().id)}' +param aiFoundryName string = 'your-resource' @description('Location for all resources.') -param location string = resourceGroup().location +param location string = 'eastus' @description('Name of the first project') -param defaultProjectName string = '${aiServicesName}-proj' +param defaultProjectName string = '${aiFoundryName}-proj' param defaultProjectDisplayName string = 'Project' param defaultProjectDescription string = 'Describe what your project is about.' /* Step 1: Get your existing/previously created Managed Identity -*/ -@description('User Assigned Identity Name') -param userAssignedIdentityName string +*/ @description('User Assigned Identity Resource Group Name') param userIdentityResourceGroupName string = resourceGroup().name +@description('User Assigned Identity Name') +param userAssignedIdentityName string = 'aifoundry-test-uai' + var userAssignedIdentityId = extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, '${userIdentityResourceGroupName}'), 'Microsoft.ManagedIdentity/userAssignedIdentities', '${userAssignedIdentityName}') /* Step 2: Create a Cognitive Services Account - */ resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { - name: aiServicesName + name: aiFoundryName location: location identity: { type: 'SystemAssigned,UserAssigned' // Select 'UserAssigned' or 'SystemAssigned,UserAssigned' during creation as this cannot be updated. @@ -51,19 +52,20 @@ resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { // Networking publicNetworkAccess: 'Enabled' - // Specifies wheether this resource support project management as child resources, used as containers for access management, data isolation, and cost in AI Foundry. + // Specifies whether this resource support project management as child resources, used as containers for access management, data isolation, and cost in AI Foundry. allowProjectManagement: true + // Defines developer API endpoint subdomain + customSubDomainName: aiFoundryName + // Auth - disableLocalAuth: true + disableLocalAuth: false } } /* Step 3: Deploy gpt-4o model - */ - resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01'= { parent: account name: 'gpt-4o' @@ -82,7 +84,6 @@ resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024- /* Step 4: Create a Project - */ resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { name: defaultProjectName @@ -92,7 +93,7 @@ resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-previ identity: { type: 'SystemAssigned,UserAssigned' // Select 'UserAssigned' or 'SystemAssigned,UserAssigned' during creation as this cannot be updated. userAssignedIdentities: { - '${userAssignedIdentityId}': {} + '${userAssignedIdentityId}': {} } } @@ -103,6 +104,10 @@ resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-previ } } +/* Step 5: + Grant managed identity 'Azure AI Administrator' role on account +*/ + output accountId string = account.id output accountName string = account.name output project string = project.name diff --git a/use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.json b/use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.json deleted file mode 100644 index 48f9e689d..000000000 --- a/use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "aiServicesName": { - "defaultValue": "[format('aiServices-{0}', uniqueString(resourceGroup().id))]", - "type": "string", - "metadata": { - "description": "That name is the name of our application. It has to be unique.Type a name followed by your resource group name. (-)" - } - }, - "location": { - "defaultValue": "[resourceGroup().location]", - "type": "string", - "metadata": { - "description": "Location for all resources." - } - }, - "defaultProjectName": { - "defaultValue": "[format('{0}-proj', parameters('aiServicesName'))]", - "type": "string", - "metadata": { - "description": "Name of the first project" - } - }, - "defaultProjectDisplayName": { - "defaultValue": "Project", - "type": "string" - }, - "defaultProjectDescription": { - "defaultValue": "Describe what your project is about.", - "type": "string" - }, - "azureKeyVaultName": { - "type": "string", - "metadata": { - "description": "Name of the customers existing Azure Key Vault resource" - } - }, - "azureKeyVaultTarget": { - "defaultValue": "[format('https://{0}.vault.azure.net/', parameters('azureKeyVaultName'))]", - "type": "string", - "metadata": { - "description": "Name of the Azure Key Vault target" - } - }, - "azureKeyVaultResourceGroupName": { - "defaultValue": "[resourceGroup().name]", - "type": "string", - "metadata": { - "description": "Resource Group name of the Azure Key Vault resource" - } - }, - "azureKeyVaultSubscriptionId": { - "defaultValue": "[subscription().subscriptionId]", - "type": "string", - "metadata": { - "description": "Subscription ID of the Azure Key Vault resource" - } - }, - "azureKeyName": { - "type": "string", - "metadata": { - "description": "Name of the Azure Key Vault key" - } - }, - "azureKeyVersion": { - "type": "string", - "metadata": { - "description": "Version of the Azure Key Vault key" - } - } - }, - "resources": [ - { - "type": "Microsoft.CognitiveServices/accounts", - "apiVersion": "2025-04-01-preview", - "name": "[parameters('aiServicesName')]", - "location": "[parameters('location')]", - "sku": { - "name": "S0" - }, - "kind": "AIServices", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "publicNetworkAccess": "Disabled", - "allowProjectManagement": true, - "disableLocalAuth": false - } - }, - { - "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2024-10-01", - "name": "[format('{0}/{1}', parameters('aiServicesName'), 'gpt-4o')]", - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName'))]" - ], - "sku": { - "capacity": 1, - "name": "GlobalStandard" - }, - "properties": { - "model": { - "name": "gpt-4o", - "format": "OpenAI", - "version": "2024-08-06" - } - } - }, - { - "type": "Microsoft.CognitiveServices/accounts/projects", - "apiVersion": "2025-04-01-preview", - "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('defaultProjectName'))]", - "location": "[parameters('location')]", - "dependsOn": [ - "[resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName'))]" - ], - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "displayName": "[parameters('defaultProjectDisplayName')]", - "description": "[parameters('defaultProjectDescription')]", - "isDefault": true - } - } - ], - "outputs": { - "accountId": { - "type": "string", - "value": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName'))]" - }, - "accountName": { - "type": "string", - "value": "[parameters('aiServicesName')]" - }, - "project": { - "type": "string", - "value": "[parameters('defaultProjectName')]" - } - } -} \ No newline at end of file diff --git a/use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.parameters.json b/use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.parameters.json deleted file mode 100644 index 3d8a74f5a..000000000 --- a/use-cases/infrastructure-as-code/30-customer-managed-keys/azuredeploy.parameters.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "aiServicesName": { - "value": "aiServices" - }, - "location": { - "value": "eastus" - }, - "defaultProjectName": { - "value": "aiServices" - }, - "defaultProjectDisplayName": { - "value": "Project" - }, - "defaultProjectDescription": { - "value": "Describe what your project is about." - }, - "azureKeyVaultName": { - "value": "" - }, - "azureKeyVaultTarget": { - "value": "" - }, - "azureKeyVaultResourceGroupName": { - "value": "" - }, - "azureKeyVaultSubscriptionId": { - "value": "" - }, - "azureKeyName": { - "value": "key" - }, - "azureKeyVersion": { - "value": "" - } - } - } \ No newline at end of file diff --git a/use-cases/infrastructure-as-code/30-customer-managed-keys/main.bicep b/use-cases/infrastructure-as-code/30-customer-managed-keys/main.bicep index bc5f8a2ea..a75a2bee6 100644 --- a/use-cases/infrastructure-as-code/30-customer-managed-keys/main.bicep +++ b/use-cases/infrastructure-as-code/30-customer-managed-keys/main.bicep @@ -1,10 +1,12 @@ /* - Azure AI Fouondry accoutn and project - with Customer Managed Key (CMK) + AI Foundry using Customer Managed Keys (CMK) for data encryption Description: - - Create an Azure AI Foundry account and project with CMK + - Create an Azure AI Foundry account + - Create a project - Create a model deployment - + + Important: Agent APIs do not support customer-managed key encryption in basic setup. This requires 'standard' setup, where you bring your own storage resources. Refer to standard Agent setup examples. */ @description('That name is the name of our application. It has to be unique.Type a name followed by your resource group name. (-)') param aiServicesName string = 'aiServices-${uniqueString(resourceGroup().id)}' @@ -17,10 +19,11 @@ param defaultProjectName string = '${aiServicesName}-proj' param defaultProjectDisplayName string = 'Project' param defaultProjectDescription string = 'Describe what your project is about.' -// Azure Key Vault -// These parameters are used under the encryption section of the Cognitive Services Account resource +/* + Reference your encryption key from an Azure Key Vault resource +*/ @description('Name of the customers existing Azure Key Vault resource') -param azureKeyVaultName string +param azureKeyVaultName string = 'es2euapdeeik' @description('Name of the Azure Key Vault target') param azureKeyVaultTarget string = 'https://${azureKeyVaultName}.vault.azure.net/' @description('Resource Group name of the Azure Key Vault resource') @@ -28,13 +31,12 @@ param azureKeyVaultResourceGroupName string = resourceGroup().name @description('Subscription ID of the Azure Key Vault resource') param azureKeyVaultSubscriptionId string = subscription().subscriptionId @description('Name of the Azure Key Vault key') -param azureKeyName string +param azureKeyName string = 'es2euapdeeik' @description('Version of the Azure Key Vault key') -param azureKeyVersion string +param azureKeyVersion string = 'a1f7ef03275b48ad8612d279350607d7' /* - Step 2: Create a Cognitive Services Account - + An AI Foundry resources is a variant of a CognitiveServices/account resource type */ resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { name: aiServicesName @@ -50,8 +52,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { // Networking publicNetworkAccess: 'Disabled' - // Encryption - /* + // Encryption properties may only be set at update, after creation, in case of system-assigned managed identity since the identity must be created first. encryption: { keySource: 'Microsoft.KeyVault' keyVaultProperties: { @@ -60,21 +61,40 @@ resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { keyVersion: azureKeyVersion } } - */ // When set, we provision hub virtual workspace on existing Account // Below property cannot be reversed once set allowProjectManagement: true + // temporarily needed + customSubDomainName: aiServicesName + // auth disableLocalAuth: false } } /* - Step 3: Deploy gpt-4o model - - - Agents will use the build-in model deployments + Developer APIs are exposed via a project, which groups in- and outputs that relate to one use case, including files. + Its advisable to create one project right away, so development teams can directly get started. + Projects may be granted individual RBAC permissions and identities on top of what account provides. +*/ +resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { + name: defaultProjectName + parent: account + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + displayName: defaultProjectDisplayName + description: defaultProjectDescription + isDefault: true //can't be updated after creation; can only be set by one project in the account + } +} + +/* + Optionally deploy a model to use in playground, agents and other tools. */ resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01'= { parent: account @@ -92,25 +112,6 @@ resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024- } } -/* - Step 5: Create a Project. This resource maps to virtual Azure ML project - -*/ -resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { - name: defaultProjectName - parent: account - location: location - identity: { - type: 'SystemAssigned' - } - properties: { - displayName: defaultProjectDisplayName - description: defaultProjectDescription - - isDefault: true //can't be updated after creation; can only be set by one project in the account - } -} - output accountId string = account.id output accountName string = account.name output project string = project.name diff --git a/use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep b/use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep new file mode 100644 index 000000000..60fc5b6ec --- /dev/null +++ b/use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep @@ -0,0 +1,184 @@ +/* + Azure AI Foundry standard account, project, dependent resources, connections and capability Hosts + + Description: + - Create an Azure AI Foundry account and project with standard setup + - Create dependent resources required for full agent scenario suite + - Create connector to attach dependent resources to AI Foundry account and project + - Create agent capability host settings + - [TODO] Account networking setting injection + +*/ + +@description('Name of foundry account. It has to be unique. Type a name followed by your resource group name. (-)') +param foundryAccountName string + +@description('Location for all resources.') +param location string = 'westus2' + +@description('Name of the project') +param defaultProjectName string = '${foundryAccountName}proj' +param defaultProjectDisplayName string = 'Project' +param defaultProjectDescription string = 'Describe what your project is about.' + +// Azure CosmosDB Account +@description('Name of the BYO CosmosDB Resource') +param cosmosDBAccountName string = '${foundryAccountName}db' +@description('Resource Group name of the BYO CosmosDB resource') +param cosmosDBAccountResourceGroupName string = resourceGroup().name +@description('Subscription ID of the BYO CosmosDB resource') +param cosmosDBAccountSubscriptionId string = subscription().subscriptionId + +// Azure Storage Account +@description('Name of the BYO Storage Account') +param storageAccountName string = '${foundryAccountName}sa' +@description('Resource Group name of the BYO Azure Storage Account') +param azureStorageAccountResourceGroupName string = resourceGroup().name +@description('Subscription ID of the BYO Azure Storage Account') +param azureStorageAccountSubscriptionId string = subscription().subscriptionId + +// Azure AI Search +@description('Name AI Search resource') +param aiSearchName string = '${foundryAccountName}search' +@description('Resource Group name of the AI Search resource') +param aiSearchServiceResourceGroupName string = resourceGroup().name +@description('Subscription ID of the AI Search resource') +param aiSearchServiceSubscriptionId string = subscription().subscriptionId + +// User new or existing dependent resources +@allowed([ + 'new' + 'existing' +]) +param newOrExisting string = 'new' + +/* + Step 1: Create a Foundry Account +*/ +resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { + name: foundryAccountName + location: location + identity: { + type: 'SystemAssigned' + } + kind: 'AIServices' + sku: { + name: 'S0' + } + properties: { + allowProjectManagement: true + customSubDomainName: foundryAccountName + disableLocalAuth: false + } +} + +/* + Step 2: Create a Foundry Project +*/ + +resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { + name: defaultProjectName + parent: account + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + displayName: defaultProjectDisplayName + description: defaultProjectDescription + isDefault: true + } +} + +/* + Step 3: Create dependent resources +*/ +resource newSearchService 'Microsoft.Search/searchServices@2025-02-01-preview' = if (newOrExisting == 'new') { + name: aiSearchName + location: location + sku: { + name: 'basic' + } + properties: {} +} + +resource existingSearchService 'Microsoft.Search/searchServices@2025-02-01-preview' existing = if (newOrExisting == 'existing') { + name: aiSearchName +} + +resource newCosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' = { + name: cosmosDBAccountName + location: location + kind: 'GlobalDocumentDB' + properties: {databaseAccountOfferType: 'Standard'} +} + +resource existingCosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = if (newOrExisting == 'existing') { + name: cosmosDBAccountName +} + +resource newStorageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = if (newOrExisting == 'new') { + name: storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: {} +} + +resource existingStorageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = if (newOrExisting == 'existing') { + name: storageAccountName +} + +/* + Step 4: Create Connections +*/ +resource project_connection_cosmosdb 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { + name: cosmosDBAccountName + parent: project + properties: { + category: 'CosmosDB' + target: ((newOrExisting == 'new') ? newCosmosDBAccount.properties.documentEndpoint : existingCosmosDBAccount.properties.documentEndpoint) + authType: 'AAD' + metadata: { + ApiType: 'Azure' + ResourceId: ((newOrExisting == 'new') ? newCosmosDBAccount.id : existingCosmosDBAccount.id) + location: ((newOrExisting == 'new') ? newCosmosDBAccount.location : existingCosmosDBAccount.location) + } + } +} + +resource project_connection_azure_storage 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { + name: storageAccountName + parent: project + properties: { + category: 'AzureStorageAccount' + target: ((newOrExisting == 'new') ? newStorageAccount.properties.primaryEndpoints.blob : existingStorageAccount.properties.primaryEndpoints.blob) + authType: 'AAD' + metadata: { + ApiType: 'Azure' + ResourceId: ((newOrExisting == 'new') ? newStorageAccount.id : existingStorageAccount.id) + location: ((newOrExisting == 'new') ? newStorageAccount.location : existingStorageAccount.location) + } + } +} + +resource project_connection_azureai_search 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { + name: aiSearchName + parent: project + properties: { + category: 'CognitiveSearch' + target: ((newOrExisting == 'new') ? newSearchService.properties.endpoint : existingSearchService.properties.endpoint) + authType: 'AAD' + isSharedToAll: true + metadata: { + ApiType: 'Azure' + ResourceId: ((newOrExisting == 'new') ? newSearchService.id : existingSearchService.id) + location: ((newOrExisting == 'new') ? newSearchService.location : existingSearchService.location) + } + } +} + +output accountId string = account.id +output accountName string = account.name diff --git a/use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep b/use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep new file mode 100644 index 000000000..b59085047 --- /dev/null +++ b/use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep @@ -0,0 +1,75 @@ +param storageAccountName string +param searchAccountName string +param cosmosdbName string +param accountName string +param projectName string + +var role = { + StorageBlobOwner: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' + CosmosdbDataContributor: '/subscriptions/${subscription().subscriptionId}/resourceGroups/$(resourceGroup().resourceGroupName)/providers/Microsoft.DocumentDB/databaseAccounts/myaiservice042604db/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002' + SearchContributor: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' + CosmosdbContributor: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' +} + +// Get a reference to the storage account +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = { + name: storageAccountName +} + +resource searchService 'Microsoft.Search/searchServices@2025-02-01-preview' existing = { + name: searchAccountName +} + +resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { + name: cosmosdbName +} + +resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: accountName +} + +resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { + name: projectName + parent: account +} + +// Grant permissions to the storage account +resource storageAccountAppRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { + name: guid(storageAccount.id, role['StorageBlobOwner'], accountName, projectName) + scope: storageAccount + properties: { + roleDefinitionId: role['StorageBlobOwner'] + principalId: project.identity.principalId + } +} + +// Grant permissions to the search account +resource searchAccountAppRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { + name: guid(searchService.id, role['SearchContributor'], accountName, projectName) + scope: searchService + properties: { + roleDefinitionId: role['StorageBlobOwner'] + principalId: project.identity.principalId + } +} + +// Grant permissions to the cosmosdb account +resource cosmosdbAccountAppRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { + name: guid(cosmosDBAccount.id, role['CosmosdbContributor'], accountName, projectName) + scope: cosmosDBAccount + properties: { + roleDefinitionId: role['CosmosdbContributor'] + principalId: project.identity.principalId + } +} + +// Grant dataplane permissions to the cosmosdb account +resource assignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2024-05-15' = { + name: guid(cosmosDBAccount.id, role['CosmosdbDataContributor'], accountName, projectName) + parent: cosmosDBAccount + properties: { + principalId: project.identity.principalId + roleDefinitionId: role['CosmosdbDataContributor'] + scope: cosmosDBAccount.id + } +} diff --git a/use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep b/use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep new file mode 100644 index 000000000..30ca8e9c3 --- /dev/null +++ b/use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep @@ -0,0 +1,57 @@ +param accountName string +param projectName string +param searchConnectionName string +param storageConnectionName string +param cosmosdbConnectionName string + +resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { + name: accountName +} + +resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { + name: projectName + parent: account +} + +resource accountCapabilityHost 'Microsoft.CognitiveServices/accounts/capabilityHosts@2025-04-01-preview' = { + name: '${accountName}-accountCapHost' + parent: account + properties: { + capabilityHostKind: 'Agents' + } +} + +resource projectCapabilityHost 'Microsoft.CognitiveServices/accounts/projects/capabilityHosts@2025-04-01-preview' = { + name: '${projectName}-projectCapHost' + parent: project + dependsOn: [ + accountCapabilityHost + ] + properties: { + capabilityHostKind: 'Agents' + vectorStoreConnections: [searchConnectionName] + storageConnections: [storageConnectionName] + threadStorageConnections : [cosmosdbConnectionName] + } +} + +/* + Optional Step: Deploy gpt-4o model + - Subscription may not enable or have sufficient quota for gpt-4o model. Please adjust model accordingly to execute + - Agents will use the build-in model deployments +*/ +resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01'= { + parent: account + name: 'gpt-4o' + sku : { + capacity: 1 + name: 'GlobalStandard' + } + properties: { + model:{ + name: 'gpt-4o' + format: 'OpenAI' + version: '2024-08-06' + } + } +} From 578cdde696feae3dde9d66bddcfa9ddf4ed6c3b6 Mon Sep 17 00:00:00 2001 From: Dennis Eikelenboom Date: Tue, 29 Apr 2025 22:46:15 -0700 Subject: [PATCH 2/2] de-dup agent standard --- .../step1-provision-resources.bicep | 184 ------------------ .../step2-grant-role-assignments.bicep | 75 ------- .../step3-create-capability-host.bicep | 57 ------ 3 files changed, 316 deletions(-) delete mode 100644 use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep delete mode 100644 use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep delete mode 100644 use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep diff --git a/use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep b/use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep deleted file mode 100644 index 60fc5b6ec..000000000 --- a/use-cases/infrastructure-as-code/40-agent-standard-setup/step1-provision-resources.bicep +++ /dev/null @@ -1,184 +0,0 @@ -/* - Azure AI Foundry standard account, project, dependent resources, connections and capability Hosts - - Description: - - Create an Azure AI Foundry account and project with standard setup - - Create dependent resources required for full agent scenario suite - - Create connector to attach dependent resources to AI Foundry account and project - - Create agent capability host settings - - [TODO] Account networking setting injection - -*/ - -@description('Name of foundry account. It has to be unique. Type a name followed by your resource group name. (-)') -param foundryAccountName string - -@description('Location for all resources.') -param location string = 'westus2' - -@description('Name of the project') -param defaultProjectName string = '${foundryAccountName}proj' -param defaultProjectDisplayName string = 'Project' -param defaultProjectDescription string = 'Describe what your project is about.' - -// Azure CosmosDB Account -@description('Name of the BYO CosmosDB Resource') -param cosmosDBAccountName string = '${foundryAccountName}db' -@description('Resource Group name of the BYO CosmosDB resource') -param cosmosDBAccountResourceGroupName string = resourceGroup().name -@description('Subscription ID of the BYO CosmosDB resource') -param cosmosDBAccountSubscriptionId string = subscription().subscriptionId - -// Azure Storage Account -@description('Name of the BYO Storage Account') -param storageAccountName string = '${foundryAccountName}sa' -@description('Resource Group name of the BYO Azure Storage Account') -param azureStorageAccountResourceGroupName string = resourceGroup().name -@description('Subscription ID of the BYO Azure Storage Account') -param azureStorageAccountSubscriptionId string = subscription().subscriptionId - -// Azure AI Search -@description('Name AI Search resource') -param aiSearchName string = '${foundryAccountName}search' -@description('Resource Group name of the AI Search resource') -param aiSearchServiceResourceGroupName string = resourceGroup().name -@description('Subscription ID of the AI Search resource') -param aiSearchServiceSubscriptionId string = subscription().subscriptionId - -// User new or existing dependent resources -@allowed([ - 'new' - 'existing' -]) -param newOrExisting string = 'new' - -/* - Step 1: Create a Foundry Account -*/ -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = { - name: foundryAccountName - location: location - identity: { - type: 'SystemAssigned' - } - kind: 'AIServices' - sku: { - name: 'S0' - } - properties: { - allowProjectManagement: true - customSubDomainName: foundryAccountName - disableLocalAuth: false - } -} - -/* - Step 2: Create a Foundry Project -*/ - -resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = { - name: defaultProjectName - parent: account - location: location - identity: { - type: 'SystemAssigned' - } - properties: { - displayName: defaultProjectDisplayName - description: defaultProjectDescription - isDefault: true - } -} - -/* - Step 3: Create dependent resources -*/ -resource newSearchService 'Microsoft.Search/searchServices@2025-02-01-preview' = if (newOrExisting == 'new') { - name: aiSearchName - location: location - sku: { - name: 'basic' - } - properties: {} -} - -resource existingSearchService 'Microsoft.Search/searchServices@2025-02-01-preview' existing = if (newOrExisting == 'existing') { - name: aiSearchName -} - -resource newCosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' = { - name: cosmosDBAccountName - location: location - kind: 'GlobalDocumentDB' - properties: {databaseAccountOfferType: 'Standard'} -} - -resource existingCosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = if (newOrExisting == 'existing') { - name: cosmosDBAccountName -} - -resource newStorageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = if (newOrExisting == 'new') { - name: storageAccountName - location: location - sku: { - name: 'Standard_LRS' - } - kind: 'StorageV2' - properties: {} -} - -resource existingStorageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = if (newOrExisting == 'existing') { - name: storageAccountName -} - -/* - Step 4: Create Connections -*/ -resource project_connection_cosmosdb 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { - name: cosmosDBAccountName - parent: project - properties: { - category: 'CosmosDB' - target: ((newOrExisting == 'new') ? newCosmosDBAccount.properties.documentEndpoint : existingCosmosDBAccount.properties.documentEndpoint) - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: ((newOrExisting == 'new') ? newCosmosDBAccount.id : existingCosmosDBAccount.id) - location: ((newOrExisting == 'new') ? newCosmosDBAccount.location : existingCosmosDBAccount.location) - } - } -} - -resource project_connection_azure_storage 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { - name: storageAccountName - parent: project - properties: { - category: 'AzureStorageAccount' - target: ((newOrExisting == 'new') ? newStorageAccount.properties.primaryEndpoints.blob : existingStorageAccount.properties.primaryEndpoints.blob) - authType: 'AAD' - metadata: { - ApiType: 'Azure' - ResourceId: ((newOrExisting == 'new') ? newStorageAccount.id : existingStorageAccount.id) - location: ((newOrExisting == 'new') ? newStorageAccount.location : existingStorageAccount.location) - } - } -} - -resource project_connection_azureai_search 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = { - name: aiSearchName - parent: project - properties: { - category: 'CognitiveSearch' - target: ((newOrExisting == 'new') ? newSearchService.properties.endpoint : existingSearchService.properties.endpoint) - authType: 'AAD' - isSharedToAll: true - metadata: { - ApiType: 'Azure' - ResourceId: ((newOrExisting == 'new') ? newSearchService.id : existingSearchService.id) - location: ((newOrExisting == 'new') ? newSearchService.location : existingSearchService.location) - } - } -} - -output accountId string = account.id -output accountName string = account.name diff --git a/use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep b/use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep deleted file mode 100644 index b59085047..000000000 --- a/use-cases/infrastructure-as-code/40-agent-standard-setup/step2-grant-role-assignments.bicep +++ /dev/null @@ -1,75 +0,0 @@ -param storageAccountName string -param searchAccountName string -param cosmosdbName string -param accountName string -param projectName string - -var role = { - StorageBlobOwner: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b7e6dc6d-f1e8-4753-8033-0f276bb0955b' - CosmosdbDataContributor: '/subscriptions/${subscription().subscriptionId}/resourceGroups/$(resourceGroup().resourceGroupName)/providers/Microsoft.DocumentDB/databaseAccounts/myaiservice042604db/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002' - SearchContributor: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/7ca78c08-252a-4471-8644-bb5ff32d4ba0' - CosmosdbContributor: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/5bd9cd88-fe45-4216-938b-f97437e15450' -} - -// Get a reference to the storage account -resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = { - name: storageAccountName -} - -resource searchService 'Microsoft.Search/searchServices@2025-02-01-preview' existing = { - name: searchAccountName -} - -resource cosmosDBAccount 'Microsoft.DocumentDB/databaseAccounts@2024-12-01-preview' existing = { - name: cosmosdbName -} - -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { - name: accountName -} - -resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { - name: projectName - parent: account -} - -// Grant permissions to the storage account -resource storageAccountAppRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { - name: guid(storageAccount.id, role['StorageBlobOwner'], accountName, projectName) - scope: storageAccount - properties: { - roleDefinitionId: role['StorageBlobOwner'] - principalId: project.identity.principalId - } -} - -// Grant permissions to the search account -resource searchAccountAppRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { - name: guid(searchService.id, role['SearchContributor'], accountName, projectName) - scope: searchService - properties: { - roleDefinitionId: role['StorageBlobOwner'] - principalId: project.identity.principalId - } -} - -// Grant permissions to the cosmosdb account -resource cosmosdbAccountAppRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { - name: guid(cosmosDBAccount.id, role['CosmosdbContributor'], accountName, projectName) - scope: cosmosDBAccount - properties: { - roleDefinitionId: role['CosmosdbContributor'] - principalId: project.identity.principalId - } -} - -// Grant dataplane permissions to the cosmosdb account -resource assignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2024-05-15' = { - name: guid(cosmosDBAccount.id, role['CosmosdbDataContributor'], accountName, projectName) - parent: cosmosDBAccount - properties: { - principalId: project.identity.principalId - roleDefinitionId: role['CosmosdbDataContributor'] - scope: cosmosDBAccount.id - } -} diff --git a/use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep b/use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep deleted file mode 100644 index 30ca8e9c3..000000000 --- a/use-cases/infrastructure-as-code/40-agent-standard-setup/step3-create-capability-host.bicep +++ /dev/null @@ -1,57 +0,0 @@ -param accountName string -param projectName string -param searchConnectionName string -param storageConnectionName string -param cosmosdbConnectionName string - -resource account 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { - name: accountName -} - -resource project 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' existing = { - name: projectName - parent: account -} - -resource accountCapabilityHost 'Microsoft.CognitiveServices/accounts/capabilityHosts@2025-04-01-preview' = { - name: '${accountName}-accountCapHost' - parent: account - properties: { - capabilityHostKind: 'Agents' - } -} - -resource projectCapabilityHost 'Microsoft.CognitiveServices/accounts/projects/capabilityHosts@2025-04-01-preview' = { - name: '${projectName}-projectCapHost' - parent: project - dependsOn: [ - accountCapabilityHost - ] - properties: { - capabilityHostKind: 'Agents' - vectorStoreConnections: [searchConnectionName] - storageConnections: [storageConnectionName] - threadStorageConnections : [cosmosdbConnectionName] - } -} - -/* - Optional Step: Deploy gpt-4o model - - Subscription may not enable or have sufficient quota for gpt-4o model. Please adjust model accordingly to execute - - Agents will use the build-in model deployments -*/ -resource modelDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01'= { - parent: account - name: 'gpt-4o' - sku : { - capacity: 1 - name: 'GlobalStandard' - } - properties: { - model:{ - name: 'gpt-4o' - format: 'OpenAI' - version: '2024-08-06' - } - } -}