Skip to content

Commit 2cdea6b

Browse files
feat: Reusing existing AI foundry changes
2 parents aa89358 + e0eaf40 commit 2cdea6b

11 files changed

+556
-114
lines changed

docs/CustomizingAzdParameters.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ By default this template will use the environment name as the prefix to prevent
2121
| `AZURE_ENV_IMAGETAG` | string | `latest` | Set the Image tag Like (allowed values: latest, dev, hotfix) |
2222
| `AZURE_ENV_EMBEDDING_MODEL_CAPACITY` | integer | `80` | Sets the capacity for the embedding model deployment. |
2323
| `AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID` | string | Guide to get your [Existing Workspace ID](/docs/re-use-log-analytics.md) | Reuses an existing Log Analytics Workspace instead of creating a new one. |
24-
24+
| `AZURE_EXISTING_AI_PROJECT_RESOURCE_ID` | string | Guid to get your existing AI Foundry Project resource ID | Reuses an existing AIFoundry and AIFoundryProject instead of creating a new one. |
2525

2626

2727
## How to Set a Parameter

docs/DeploymentGuide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ Once you've opened the project in [Codespaces](#github-codespaces), [Dev Contain
175175
```
176176
If you don't have azd env then you need to pass parameters along with the command. Then the command will look like the following:
177177
```shell
178-
bash ./infra/scripts/process_sample_data.sh <Storage-Account-name> <Storage-Account-container-name> <Key-Vault-name> <CosmosDB-Account-name> <Resource-Group-name> <aiFoundryResourceName> <aiSearchResourceName>
178+
bash ./infra/scripts/process_sample_data.sh <Storage-Account-name> <Storage-Account-container-name> <Key-Vault-name> <CosmosDB-Account-name> <Resource-Group-name> <AI-Foundry-Name> <AI-Foundry-Resource-Group-Name> <Search-Service-Name>
179179
```
180180

181181
6. Open the [Azure Portal](https://portal.azure.com/), go to the deployed resource group, find the App Service and get the app URL from `Default domain`.

infra/deploy_ai_foundry.bicep

Lines changed: 114 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ param embeddingModel string
1111
param embeddingDeploymentCapacity int
1212
param managedIdentityObjectId string
1313
param existingLogAnalyticsWorkspaceId string = ''
14+
param azureExistingAIProjectResourceId string = ''
1415

1516
var abbrs = loadJsonContent('./abbreviations.json')
1617

@@ -23,13 +24,38 @@ var aiProjectFriendlyName = aiProjectName
2324
var aiProjectDescription = 'AI Foundry Project'
2425
var aiSearchName = '${abbrs.ai.aiSearch}${solutionName}'
2526
var workspaceName = '${abbrs.managementGovernance.logAnalyticsWorkspace}${solutionName}'
26-
var aiSearchConnectionName = 'myVectorStoreProjectConnectionName-${solutionName}'
27+
// var aiSearchConnectionName = 'myVectorStoreProjectConnectionName-${solutionName}'
2728

2829
var useExisting = !empty(existingLogAnalyticsWorkspaceId)
2930
var existingLawSubscription = useExisting ? split(existingLogAnalyticsWorkspaceId, '/')[2] : ''
3031
var existingLawResourceGroup = useExisting ? split(existingLogAnalyticsWorkspaceId, '/')[4] : ''
3132
var existingLawName = useExisting ? split(existingLogAnalyticsWorkspaceId, '/')[8] : ''
3233

34+
var existingOpenAIEndpoint = !empty(azureExistingAIProjectResourceId)
35+
? format('https://{0}.openai.azure.com/', split(azureExistingAIProjectResourceId, '/')[8])
36+
: ''
37+
var existingProjEndpoint = !empty(azureExistingAIProjectResourceId)
38+
? format(
39+
'https://{0}.services.ai.azure.com/api/projects/{1}',
40+
split(azureExistingAIProjectResourceId, '/')[8],
41+
split(azureExistingAIProjectResourceId, '/')[10]
42+
)
43+
: ''
44+
var existingAIFoundryName = !empty(azureExistingAIProjectResourceId)
45+
? split(azureExistingAIProjectResourceId, '/')[8]
46+
: ''
47+
var existingAIProjectName = !empty(azureExistingAIProjectResourceId)
48+
? split(azureExistingAIProjectResourceId, '/')[10]
49+
: ''
50+
var existingAIServiceSubscription = !empty(azureExistingAIProjectResourceId)
51+
? split(azureExistingAIProjectResourceId, '/')[2]
52+
: ''
53+
var existingAIServiceResourceGroup = !empty(azureExistingAIProjectResourceId)
54+
? split(azureExistingAIProjectResourceId, '/')[4]
55+
: ''
56+
var aiSearchConnectionName = 'foundry-search-connection-${solutionName}'
57+
var aiAppInsightConnectionName = 'foundry-app-insights-connection-${solutionName}'
58+
3359
var aiModelDeployments = [
3460
{
3561
name: gptModelName
@@ -86,7 +112,12 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
86112
}
87113
}
88114

89-
resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = {
115+
resource existingAiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = if (!empty(azureExistingAIProjectResourceId)) {
116+
name: existingAIFoundryName
117+
scope: resourceGroup(existingAIServiceSubscription, existingAIServiceResourceGroup)
118+
}
119+
120+
resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = if (empty(azureExistingAIProjectResourceId)) {
90121
name: aiFoundryName
91122
location: location
92123
sku: {
@@ -109,7 +140,7 @@ resource aiFoundry 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' = {
109140
}
110141
}
111142

112-
resource aiFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = {
143+
resource aiFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04-01-preview' = if (empty(azureExistingAIProjectResourceId)) {
113144
parent: aiFoundry
114145
name: aiProjectName
115146
location: location
@@ -124,7 +155,7 @@ resource aiFoundryProject 'Microsoft.CognitiveServices/accounts/projects@2025-04
124155

125156
@batchSize(1)
126157
resource aiFModelDeployments 'Microsoft.CognitiveServices/accounts/deployments@2025-04-01-preview' = [
127-
for aiModeldeployment in aiModelDeployments: {
158+
for aiModeldeployment in aiModelDeployments: if (empty(azureExistingAIProjectResourceId)) {
128159
parent: aiFoundry
129160
name: aiModeldeployment.name
130161
properties: {
@@ -166,7 +197,7 @@ resource aiSearch 'Microsoft.Search/searchServices@2024-06-01-preview' = {
166197
}
167198
}
168199

169-
resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = {
200+
resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = if (empty(azureExistingAIProjectResourceId)) {
170201
name: aiSearchConnectionName
171202
parent: aiFoundryProject
172203
properties: {
@@ -182,48 +213,87 @@ resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/project
182213
}
183214
}
184215

216+
module existing_AIProject_SearchConnectionModule 'deploy_aifp_aisearch_connection.bicep' = if (!empty(azureExistingAIProjectResourceId)) {
217+
name: 'aiProjectSearchConnectionDeployment'
218+
scope: resourceGroup(existingAIServiceSubscription, existingAIServiceResourceGroup)
219+
params: {
220+
existingAIProjectName: existingAIProjectName
221+
existingAIFoundryName: existingAIFoundryName
222+
aiSearchName: aiSearchName
223+
aiSearchResourceId: aiSearch.id
224+
aiSearchLocation: aiSearch.location
225+
aiSearchConnectionName: aiSearchConnectionName
226+
}
227+
}
228+
185229
resource cognitiveServicesOpenAIUser 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
186230
name: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
187231
}
188232

189-
resource assignCognitiveRoleToAISearch 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
190-
name: guid(resourceGroup().id, aiFoundryProject.id, cognitiveServicesOpenAIUser.id)
191-
scope: aiFoundry
192-
properties: {
233+
module assignOpenAIRoleToAISearch 'deploy_foundry_role_assignment.bicep' = {
234+
name: 'assignOpenAIRoleToAISearch'
235+
scope: resourceGroup(existingAIServiceSubscription, existingAIServiceResourceGroup)
236+
params: {
193237
roleDefinitionId: cognitiveServicesOpenAIUser.id
238+
roleAssignmentName: guid(resourceGroup().id, aiSearch.id, cognitiveServicesOpenAIUser.id, 'openai-foundry')
239+
aiFoundryName: !empty(azureExistingAIProjectResourceId) ? existingAIFoundryName : aiFoundryName
240+
aiProjectName: !empty(azureExistingAIProjectResourceId) ? existingAIProjectName : aiProjectName
194241
principalId: aiSearch.identity.principalId
195-
principalType: 'ServicePrincipal'
196242
}
197243
}
198244

199-
resource searchIndexDataReader 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
245+
@description('This is the built-in Search Index Data Reader role.')
246+
resource searchIndexDataReaderRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
247+
scope: aiSearch
200248
name: '1407120a-92aa-4202-b7e9-c0e197c71c8f'
201249
}
202250

203-
resource assignSearchIndexDataReaderToAiProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
204-
name: guid(resourceGroup().id, aiFoundryProject.id, searchIndexDataReader.id)
251+
resource searchIndexDataReaderRoleAssignmentToAIFP 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (empty(azureExistingAIProjectResourceId)) {
252+
name: guid(aiSearch.id, aiFoundryProject.id, searchIndexDataReaderRoleDefinition.id)
205253
scope: aiSearch
206254
properties: {
255+
roleDefinitionId: searchIndexDataReaderRoleDefinition.id
207256
principalId: aiFoundryProject.identity.principalId
208-
roleDefinitionId: searchIndexDataReader.id
257+
principalType: 'ServicePrincipal'
258+
}
259+
}
260+
resource assignSearchIndexDataReaderToExistingAiProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(azureExistingAIProjectResourceId)) {
261+
name: guid(resourceGroup().id, existingAIProjectName, searchIndexDataReaderRoleDefinition.id, 'Existing')
262+
scope: aiSearch
263+
properties: {
264+
roleDefinitionId: searchIndexDataReaderRoleDefinition.id
265+
principalId: assignOpenAIRoleToAISearch.outputs.aiProjectPrincipalId
209266
principalType: 'ServicePrincipal'
210267
}
211268
}
212269

213-
resource searchServiceContributor 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
270+
@description('This is the built-in Search Service Contributor role.')
271+
resource searchServiceContributorRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
272+
scope: aiSearch
214273
name: '7ca78c08-252a-4471-8644-bb5ff32d4ba0'
215274
}
216275

217-
resource assignSearchServiceContributorToAiProject 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
218-
name: guid(resourceGroup().id, aiFoundryProject.id, searchServiceContributor.id)
276+
resource searchServiceContributorRoleAssignmentToAIFP 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (empty(azureExistingAIProjectResourceId)) {
277+
name: guid(aiSearch.id, aiFoundryProject.id, searchServiceContributorRoleDefinition.id)
219278
scope: aiSearch
220279
properties: {
280+
roleDefinitionId: searchServiceContributorRoleDefinition.id
221281
principalId: aiFoundryProject.identity.principalId
222-
roleDefinitionId: searchServiceContributor.id
223282
principalType: 'ServicePrincipal'
224283
}
225284
}
226285

286+
resource searchServiceContributorRoleAssignmentExisting 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (!empty(azureExistingAIProjectResourceId)) {
287+
name: guid(resourceGroup().id, existingAIProjectName, searchServiceContributorRoleDefinition.id, 'Existing')
288+
scope: aiSearch
289+
properties: {
290+
roleDefinitionId: searchServiceContributorRoleDefinition.id
291+
principalId: assignOpenAIRoleToAISearch.outputs.aiProjectPrincipalId
292+
principalType: 'ServicePrincipal'
293+
}
294+
}
295+
296+
227297
resource tenantIdEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
228298
parent: keyVault
229299
name: 'TENANT-ID'
@@ -232,6 +302,7 @@ resource tenantIdEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' =
232302
}
233303
}
234304

305+
235306
resource azureOpenAIDeploymentModel 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
236307
parent: keyVault
237308
name: 'AZURE-OPEN-AI-DEPLOYMENT-MODEL'
@@ -252,9 +323,11 @@ resource azureOpenAIEndpointEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-
252323
parent: keyVault
253324
name: 'AZURE-OPENAI-ENDPOINT'
254325
properties: {
255-
value: aiFoundry.properties.endpoints['OpenAI Language Model Instance API'] //aiServices_m.properties.endpoint
326+
value: !empty(existingOpenAIEndpoint)
327+
? existingOpenAIEndpoint
328+
: aiFoundry.properties.endpoints['OpenAI Language Model Instance API']
329+
}
256330
}
257-
}
258331

259332
resource azureOpenAIEmbeddingDeploymentModel 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
260333
parent: keyVault
@@ -292,23 +365,27 @@ resource cogServiceEndpointEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-p
292365
parent: keyVault
293366
name: 'COG-SERVICES-ENDPOINT'
294367
properties: {
295-
value: aiFoundry.properties.endpoint
368+
value: !empty(existingOpenAIEndpoint)
369+
? existingOpenAIEndpoint
370+
: aiFoundry.properties.endpoint
296371
}
297372
}
298373

299374
resource cogServiceKeyEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
300375
parent: keyVault
301376
name: 'COG-SERVICES-KEY'
302377
properties: {
303-
value: aiFoundry.listKeys().key1
378+
value: !empty(existingOpenAIEndpoint)
379+
? existingAiFoundry.listKeys().key1
380+
: aiFoundry.listKeys().key1
304381
}
305382
}
306383

307384
resource cogServiceNameEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
308385
parent: keyVault
309386
name: 'COG-SERVICES-NAME'
310387
properties: {
311-
value: aiFoundryName
388+
value: !empty(existingAIFoundryName) ? existingAIFoundryName : aiFoundryName
312389
}
313390
}
314391

@@ -339,19 +416,26 @@ resource azureLocatioEntry 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview
339416
output keyvaultName string = keyvaultName
340417
output keyvaultId string = keyVault.id
341418

342-
output aiServicesTarget string = aiFoundry.properties.endpoint //aiServices_m.properties.endpoint
343-
output aiServicesName string = aiFoundryName //aiServicesName_m
344-
output aiServicesId string = aiFoundry.id //aiServices_m.id
419+
// output aiServicesTarget string = aiFoundry.properties.endpoint //aiServices_m.properties.endpoint
420+
// output aiServicesName string = aiFoundryName //aiServicesName_m
421+
// output aiServicesId string = aiFoundry.id //aiServices_m.id
345422

346423
output aiSearchName string = aiSearchName
347424
output aiSearchId string = aiSearch.id
348425
output aiSearchTarget string = 'https://${aiSearch.name}.search.windows.net'
349426
output aiSearchService string = aiSearch.name
350427
output aiSearchConnectionName string = aiSearchConnectionName
351-
output aiFoundryProjectName string = aiFoundryProject.name
352-
output aiFoundryProjectEndpoint string = aiFoundryProject.properties.endpoints['AI Foundry API']
353-
output aoaiEndpoint string = aiFoundry.properties.endpoints['OpenAI Language Model Instance API']
354-
output aiFoundryName string = aiFoundryName
428+
output aiFoundryProjectName string = !empty(existingAIProjectName) ? existingAIProjectName : aiFoundryProject.name
429+
// output aiFoundryProjectEndpoint string = aiFoundryProject.properties.endpoints['AI Foundry API']
430+
output aiFoundryProjectEndpoint string = !empty(existingProjEndpoint)
431+
? existingProjEndpoint
432+
: aiFoundryProject.properties.endpoints['AI Foundry API']
433+
// output aoaiEndpoint string = aiFoundry.properties.endpoints['OpenAI Language Model Instance API']
434+
output aoaiEndpoint string = !empty(existingOpenAIEndpoint)
435+
? existingOpenAIEndpoint
436+
: aiFoundry.properties.endpoints['OpenAI Language Model Instance API']
437+
output aiFoundryName string = !empty(existingAIFoundryName) ? existingAIFoundryName : aiFoundryName
438+
output aiFoundryRgName string = !empty(existingAIServiceResourceGroup) ? existingAIServiceResourceGroup : resourceGroup().name
355439

356440
output applicationInsightsId string = applicationInsights.id
357441
output logAnalyticsWorkspaceResourceName string = useExisting ? existingLogAnalyticsWorkspace.name : logAnalytics.name
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
param existingAIProjectName string
2+
param existingAIFoundryName string
3+
param aiSearchName string
4+
param aiSearchResourceId string
5+
param aiSearchLocation string
6+
param aiSearchConnectionName string
7+
8+
resource projectAISearchConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = {
9+
name: '${existingAIFoundryName}/${existingAIProjectName}/${aiSearchConnectionName}'
10+
properties: {
11+
category: 'CognitiveSearch'
12+
target: 'https://${aiSearchName}.search.windows.net'
13+
authType: 'AAD'
14+
isSharedToAll: true
15+
metadata: {
16+
ApiType: 'Azure'
17+
ResourceId: aiSearchResourceId
18+
location: aiSearchLocation
19+
}
20+
}
21+
}

0 commit comments

Comments
 (0)