Skip to content

Commit 0a22b20

Browse files
Merge pull request #54 from microsoft/AIFoundry-FDPUpdate
AI foundry fdp update
2 parents 896f3de + 2093adc commit 0a22b20

18 files changed

Lines changed: 3500 additions & 18447 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [1.2] - 2025-05-13
6+
### Added
7+
- Add new project module leveraging the new cognitive services/projects type
8+
- Add BYO service connections for search, storage and CosmosDB to project (based on feature flag selection)
9+
- new infrastructure drawing
10+
11+
### Changed
12+
- Revise Cognitive Services module to leverage new preview api to leverage new FDP updates
13+
- Update AI Search CMK enforcement value to 'disabled'
14+
- Update and add private endpoints for cognitive services project subtype
15+
- Update and add required roles and scopes to cognitive services and ai search modules
16+
- Update md to show changes
17+
18+
### Deprecated
19+
- Remove the modules deploying AML hub and project.
20+
521

622
## [1.1] - 2025-04-30
723
### Added

README.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,24 @@
44

55
## Overview
66

7-
This is a foundational deployment solution for deploying an AI hub and project into an isolated environment (vNet) within Azure. The deployed features follow Microsoft's Well-Architected Framework [WAF](https://learn.microsoft.com/en-us/azure/well-architected/) to establish an isolated infrastructure for AI Foundry, intended to assist in moving from a Proof of Concept state to a production-ready application.
7+
<span style="font-size: 3em;">🚀</span> **New: Updated deployment to match Foundry release at Build 2025!**
8+
This new update has been tested in the EastUS2 region successfully.
9+
This is a foundational solution for deploying an AI Foundry account ([Cognitive Services accountKind = 'AIServices'](https://review.learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/2025-04-01-preview/accounts?branch=main&pivots=deployment-language-bicep)) and project ([cognitiveServices/projects](https://review.learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/2025-04-01-preview/accounts/projects?branch=main&pivots=deployment-language-bicep)) into an isolated environment (vNet) within Azure. The deployed features follow Microsoft's Well-Architected Framework [WAF](https://learn.microsoft.com/en-us/azure/well-architected/) to establish an isolated infrastructure for AI Foundry, intended to assist in moving from a Proof of Concept state to a production-ready application.
810

9-
This template leverages Azure Verified Modules (AVM) and the Azure Developer CLI (AZD) to provision a WAF-aligned infrastructure for AI application development. This infrastructure includes AI Foundry elements, a virtual network (VNET), private endpoints, Key Vault, a storage account, and additional, optional WAF-aligned resources (such as Cosmos DB and SQL Server) that can be leveraged with Foundry developed projects.
11+
This template leverages Azure Verified Modules (AVM) and the Azure Developer CLI (AZD) to provision a WAF-aligned infrastructure for AI application development. This infrastructure includes AI Foundry elements, a virtual network (VNET), private endpoints, Key Vault, a storage account, and additional, optional WAF-aligned resources (such as AI Search, Cosmos DB and SQL Server) that can be leveraged with Foundry developed projects.
1012

1113
The following deployment automates our recommended configuration to protect your data and resources; using Microsoft Entra ID role-based access control, a managed network, and private endpoints. We recommend disabling public network access for Azure OpenAI resources, Azure AI Search resources, and storage accounts (which will occur when deploying those optional services within this workflow). Using selected networks with IP rules isn't supported because the services' IP addresses are dynamic.
1214

13-
AI Foundry has two network isolation aspects, this repository will automate:
14-
1. Configuring the network isolation of the Azure AI Foundry hub and project managed compute (compute instance, serverless compute, managed online endpoint) [Configure Managed Network](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/configure-managed-network)
15-
2. Configuring the virtual network, private end points and private link services to isolate resources to connect to the hub and project in a secure way. [Secure Data Playground](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/secure-data-playground)
15+
This repository will automate:
16+
1. Configuring the virtual network, private end points and private link services to isolate resources connecting to the account and project in a secure way. [Secure Data Playground](https://learn.microsoft.com/en-us/azure/ai-foundry/how-to/secure-data-playground)
17+
2. Deploying and configuring the network isolation of the Azure AI Foundry account and project sub-resource within the virtual network, and with all services configured behind private end points.
18+
1619

1720

1821
## Architecture
1922
The diagram below illustrates the capabilities included in the template.
2023

21-
![Network Isolation Infrastructure](./img/Architecture/Deploy-AI-App-in-Prod-Architecture_final.png)
24+
![Network Isolation Infrastructure](./img/Architecture/FDParch.png)
2225

2326
| Diagram Step | Description |
2427
| ------------- | ------------- |
@@ -31,12 +34,12 @@ The diagram below illustrates the capabilities included in the template.
3134
## Features
3235

3336
### What solutions does this enable?
34-
- Deploys AI hub and AI project into a virtual network with all dependent services connected via private end points.
37+
- Deploys an AI Foundry account and project leveraging the latest AI Foundry updates announced at Build 2025, into a virtual network with all dependent services connected via private end points.
3538

3639
- Configures AI Foundry, adhering to the best practices outlined in the Well Architected Framework.
3740

3841
- Provides the ability to [add additional Azure services during deployment](docs/add_additional_services.md), configured to connect via isolation to enrich your AI project.
39-
(API Management, CosmosDB, Azure SQL DB)
42+
(AI Search, API Management, CosmosDB, Azure SQL DB)
4043

4144
- <span style="font-size: 3em;">🚀</span> **New**:
4245
Offers ability to [start with an existing Azure AI Project](docs/transfer_project_connections.md) which will provision dependent Azure resources based on the Project's established connections within AI Foundry.
@@ -49,8 +52,7 @@ Offers ability to [start with an existing Azure AI Project](docs/transfer_projec
4952
3. The solution ensures secure access to the private VNET through a jump-box VM with Azure Bastion. By default, Bastion does not require an inbound NSG rule for network traffic. However, if your environment enforces specific policy rules, you can resolve access issues by entering your machine's IP address in the `allowedIpAddress` parameter when prompted during deployment. If not specified, all IP addresses are allowed to connect to Azure Bastion.
5053
4. If deploying from your [local environment](docs/local_environment_steps.md), install the [Azure CLI (AZ)](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) and the [Azure Developer CLI (AZD)](https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows%2Cbrew-mac%2Cscript-linux&pivots=os-windows).
5154
5. If deploying via [GitHub Codespaces](docs/github_code_spaces_steps.md) - requires the user to be on a GitHub Team or Enterprise Cloud plan.
52-
6. If leveraging [One-click deployment](#quick-deploy).
53-
7. If leveraging [GitHub Actions](docs/github_actions_steps.md).
55+
6. If leveraging [GitHub Actions](docs/github_actions_steps.md).
5456

5557
### Check Azure OpenAI Quota Availability
5658

@@ -110,7 +112,7 @@ This template has [Managed Identity](https://learn.microsoft.com/entra/identity/
110112

111113
## Resources
112114

113-
- [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-studio/)
115+
- [Azure AI Foundry documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/)
114116
- [Azure Well Architecture Framework documentation](https://learn.microsoft.com/en-us/azure/well-architected/)
115117
- [Azure OpenAI Service - Documentation, quickstarts, API reference - Azure AI services | Microsoft Learn](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/use-your-data)
116118
- [Azure AI Content Understanding documentation](https://learn.microsoft.com/en-us/azure/ai-services/content-understanding/)

docs/Required_roles_scopes_resources.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ Be sure these resource providers are registered in your Azure subscription. To r
1717
|Azure Log Analytics|Microsoft.OperationalInsights|/workspaces|An Azure Log Analytics workspace used to collect diagnostics|
1818
|Azure Key Vault|Microsoft.KeyVault|/vaults|An Azure Key Vault instance associated with the Azure AI Foundry Hub|
1919
|Azure Storage Account|Microsoft.Storage|/storageAccounts|An Azure Storage instance associated with the Azure AI Foundry Hub|
20-
|Azure Container Registry|Microsoft.ContainerRegistry|/registries|An Azure Container Registry instance associated with the Azure AI Foundry Hub|
21-
|Azure AI Hub / Project|Microsoft.MachineLearningServices|/workspaces|An Azure AI Studio Hub and Project (Azure ML Workspace of kind ‘hub’ and ‘project’)|
20+
|Azure Container Registry|Microsoft.ContainerRegistry|/registries|An Azure Container Registry instance associated with the Azure AI Foundry Account|
2221
|Azure AI Services|Microsoft.CognitiveServices|/accounts|An Azure AI Services as the model-as-a-service endpoint provider including GPT-4o and ADA Text Embeddings model deployments|
2322
|Azure Virtual Network|Microsoft.Network|/virtualNetworks|A bring-your-own (BYO) virtual network hosting a virtual machine to connect to Azure AI Foundry which will be behind a private endpoint when in network isolation mode. |
2423
|Bastion Host|Microsoft.Network||A Bastion Host defined in the BYO virtual network that provides RDP connectivity to the jumpbox virtual machine|

img/Architecture/Architecture.png

-352 KB
Binary file not shown.

img/Architecture/FDParch.png

127 KB
Loading

infra/main.bicep

Lines changed: 40 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ param name string
88
@description('Specifies the location for all the Azure resources. Defaults to the location of the resource group.')
99
param location string
1010

11-
@description('Optional. Specifies the connections to be created for the Azure AI Hub workspace. The connections are used to connect to other Azure resources and services.')
12-
param connections connectionType[] = []
13-
1411
@description('Optional. Specifies the OpenAI deployments to create.')
1512
param aiModelDeployments deploymentsType[] = []
1613

@@ -82,6 +79,21 @@ param translatorEnabled bool = false
8279
@description('Whether to include Azure Document Intelligence in the deployment.')
8380
param documentIntelligenceEnabled bool = false
8481

82+
@description('Optional. A collection of rules governing the accessibility from specific network locations.')
83+
param networkAcls object ={
84+
defaultAction: 'Deny'
85+
bypass: 'AzureServices' // ✅ Allows trusted Microsoft services
86+
// virtualNetworkRules: [
87+
// {
88+
// id: networkIsolation ? network.outputs.vmSubnetName : ''
89+
// ignoreMissingVnetServiceEndpoint: true
90+
// }
91+
// ]
92+
}
93+
94+
@description('Name of the first project')
95+
param projectName string = '${take(name, 8)}proj'
96+
8597
var defaultTags = {
8698
'azd-env-name': name
8799
}
@@ -170,7 +182,7 @@ module containerRegistry 'modules/containerRegistry.bicep' = if (acrEnabled) {
170182
module storageAccount 'modules/storageAccount.bicep' = {
171183
name: take('${name}-storage-account-deployment', 64)
172184
params: {
173-
name: 'st${name}${resourceToken}'
185+
storageName: 'st${name}${resourceToken}'
174186
location: location
175187
networkIsolation: networkIsolation
176188
virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : ''
@@ -190,7 +202,7 @@ module storageAccount 'modules/storageAccount.bicep' = {
190202
}
191203
], searchEnabled ? [
192204
{
193-
principalId: aiSearch.outputs.systemAssignedMIPrincipalId
205+
principalId: searchEnabled ? aiSearch.outputs.systemAssignedMIPrincipalId : ''
194206
principalType: 'ServicePrincipal'
195207
roleDefinitionIdOrName: 'Storage Blob Data Contributor'
196208
}
@@ -206,6 +218,7 @@ module cognitiveServices 'modules/cognitive-services/main.bicep' = {
206218
resourceToken: resourceToken
207219
location: location
208220
networkIsolation: networkIsolation
221+
networkAcls: networkAcls
209222
virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : ''
210223
virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : ''
211224
logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
@@ -221,6 +234,21 @@ module cognitiveServices 'modules/cognitive-services/main.bicep' = {
221234
}
222235
}
223236

237+
// // Add the new FDP cognitive services module
238+
module project 'modules/ai-foundry-project/main.bicep' = {
239+
name: '${name}prj'
240+
params: {
241+
cosmosDBname: cosmosDbEnabled? cosmosDb.outputs.cosmosDBname : ''
242+
cosmosDbEnabled: cosmosDbEnabled
243+
searchEnabled: searchEnabled
244+
name: projectName
245+
location: location
246+
storageName: storageAccount.outputs.storageName
247+
aiServicesName: cognitiveServices.outputs.aiServicesName
248+
nameFormatted: searchEnabled ? aiSearch.outputs.name : ''
249+
}
250+
}
251+
224252
module aiSearch 'modules/aisearch.bicep' = if (searchEnabled) {
225253
name: take('${name}-ai-search-deployment', 64)
226254
params: {
@@ -230,6 +258,7 @@ module aiSearch 'modules/aisearch.bicep' = if (searchEnabled) {
230258
virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : ''
231259
virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : ''
232260
logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
261+
userObjectId: userObjectId
233262
roleAssignments: union(empty(userObjectId) ? [] : [
234263
{
235264
principalId: userObjectId
@@ -259,7 +288,7 @@ module virtualMachine './modules/virtualMachine.bicep' = if (networkIsolation)
259288
vmNicName: toLower('nic-vm-${name}-jump')
260289
vmSize: vmSize
261290
vmSubnetId: network.outputs.vmSubnetId
262-
storageAccountName: storageAccount.outputs.name
291+
storageAccountName: storageAccount.outputs.storageName
263292
storageAccountResourceGroup: resourceGroup().name
264293
imagePublisher: 'MicrosoftWindowsDesktop'
265294
imageOffer: 'Windows-11'
@@ -282,72 +311,6 @@ module virtualMachine './modules/virtualMachine.bicep' = if (networkIsolation)
282311
dependsOn: networkIsolation ? [storageAccount] : []
283312
}
284313

285-
module aiHub 'modules/ai-foundry/hub.bicep' = {
286-
name: take('${name}-ai-hub-deployment', 64)
287-
params: {
288-
name: 'hub-${name}'
289-
location: location
290-
networkIsolation: networkIsolation
291-
virtualNetworkResourceId: networkIsolation ? network.outputs.virtualNetworkId : ''
292-
virtualNetworkSubnetResourceId: networkIsolation ? network.outputs.vmSubnetId : ''
293-
logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
294-
appInsightsResourceId: applicationInsights.outputs.resourceId
295-
containerRegistryResourceId: acrEnabled ? containerRegistry.outputs.resourceId : null
296-
keyVaultResourceId: keyvault.outputs.resourceId
297-
storageAccountResourceId: storageAccount.outputs.resourceId
298-
roleAssignments:empty(userObjectId) ? [] : [
299-
{
300-
roleDefinitionIdOrName: 'f6c7c914-8db3-469d-8ca1-694a8f32e121' // ML Data Scientist Role
301-
principalId: userObjectId
302-
principalType: 'User'
303-
}
304-
]
305-
connections: concat(
306-
cognitiveServices.outputs.connections,
307-
connections,
308-
searchEnabled ? [
309-
{
310-
name: aiSearch.outputs.name
311-
value: null
312-
category: 'CognitiveSearch'
313-
target: 'https://${aiSearch.outputs.name}.search.windows.net/'
314-
connectionProperties: {
315-
authType: 'AAD'
316-
}
317-
isSharedToAll: true
318-
metadata: {
319-
ApiType: 'Azure'
320-
ResourceId: aiSearch.outputs.resourceId
321-
}
322-
}] : [])
323-
tags: allTags
324-
}
325-
}
326-
327-
module aiProject 'modules/ai-foundry/project.bicep' = {
328-
name: take('${name}-ai-project-deployment', 64)
329-
params: {
330-
name: 'proj-${name}'
331-
location: location
332-
hubResourceId: aiHub.outputs.resourceId
333-
networkIsolation: networkIsolation
334-
logAnalyticsWorkspaceResourceId: logAnalyticsWorkspace.outputs.resourceId
335-
roleAssignments: union(empty(userObjectId) ? [] : [
336-
{
337-
roleDefinitionIdOrName: 'f6c7c914-8db3-469d-8ca1-694a8f32e121' // ML Data Scientist Role
338-
principalId: userObjectId
339-
principalType: 'User'
340-
}
341-
], [
342-
{
343-
roleDefinitionIdOrName: 'f6c7c914-8db3-469d-8ca1-694a8f32e121' // ML Data Scientist Role
344-
principalId: cognitiveServices.outputs.aiServicesSystemAssignedMIPrincipalId
345-
principalType: 'ServicePrincipal'
346-
}
347-
])
348-
tags: allTags
349-
}
350-
}
351314

352315
module apim 'modules/apim.bicep' = if (apiManagementEnabled) {
353316
name: take('${name}-apim-deployment', 64)
@@ -395,23 +358,23 @@ module sqlServer 'modules/sqlServer.bicep' = if (sqlServerEnabled) {
395358
}
396359

397360
import { sqlDatabaseType, databasePropertyType, deploymentsType } from 'modules/customTypes.bicep'
398-
import { connectionType } from 'br/public:avm/res/machine-learning-services/workspace:0.10.1'
361+
399362

400363
output AZURE_KEY_VAULT_NAME string = keyvault.outputs.name
401364
output AZURE_AI_SERVICES_NAME string = cognitiveServices.outputs.aiServicesName
402365
output AZURE_AI_SEARCH_NAME string = searchEnabled ? aiSearch.outputs.name : ''
403-
output AZURE_AI_HUB_NAME string = aiHub.outputs.name
404-
output AZURE_AI_PROJECT_NAME string = aiHub.outputs.name
366+
output AZURE_AI_HUB_NAME string = cognitiveServices.outputs.aiServicesName
367+
output AZURE_AI_PROJECT_NAME string = project.outputs.projectName
405368
output AZURE_BASTION_NAME string = networkIsolation ? network.outputs.bastionName : ''
406369
output AZURE_VM_RESOURCE_ID string = networkIsolation ? virtualMachine.outputs.id : ''
407370
output AZURE_VM_USERNAME string = servicesUsername
408371
output AZURE_APP_INSIGHTS_NAME string = applicationInsights.outputs.name
409372
output AZURE_CONTAINER_REGISTRY_NAME string = acrEnabled ? containerRegistry.outputs.name : ''
410373
output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = logAnalyticsWorkspace.outputs.name
411-
output AZURE_STORAGE_ACCOUNT_NAME string = storageAccount.outputs.name
374+
output AZURE_STORAGE_ACCOUNT_NAME string = storageAccount.outputs.storageName
412375
output AZURE_API_MANAGEMENT_NAME string = apiManagementEnabled ? apim.outputs.name : ''
413376
output AZURE_VIRTUAL_NETWORK_NAME string = networkIsolation ? network.outputs.virtualNetworkName : ''
414377
output AZURE_VIRTUAL_NETWORK_SUBNET_NAME string =networkIsolation ? network.outputs.vmSubnetName : ''
415378
output AZURE_SQL_SERVER_NAME string = sqlServerEnabled ? sqlServer.outputs.name : ''
416379
output AZURE_SQL_SERVER_USERNAME string = sqlServerEnabled ? servicesUsername : ''
417-
output AZURE_COSMOS_ACCOUNT_NAME string = cosmosDbEnabled ? cosmosDb.outputs.name : ''
380+
output AZURE_COSMOS_ACCOUNT_NAME string = cosmosDbEnabled ? cosmosDb.outputs.cosmosDBname : ''

0 commit comments

Comments
 (0)