Skip to content

Commit 8a977bb

Browse files
feat: Reuse existing Azure AI Foundry changes, dependabot changes
2 parents aa89358 + b59f124 commit 8a977bb

22 files changed

+591
-149
lines changed

.github/workflows/azure-dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919

2020
# Step 2: Validate the Azure template using microsoft/template-validation-action
2121
- name: Validate Azure Template
22-
uses: microsoft/template-validation-action@v0.3.5
22+
uses: microsoft/template-validation-action@v0.4.3
2323
id: validation
2424
env:
2525
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
WEBAPP_URL: ${{ steps.get_output.outputs.WEBAPP_URL }}
2727
steps:
2828
- name: Checkout Code
29-
uses: actions/checkout@v3
29+
uses: actions/checkout@v4
3030

3131
- name: Setup Azure CLI
3232
run: |

.github/workflows/docker-build-and-push.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ jobs:
1919

2020
steps:
2121
- name: Checkout repository
22-
uses: actions/checkout@v2
22+
uses: actions/checkout@v4
2323

2424
- name: Set up Docker Buildx
25-
uses: docker/setup-buildx-action@v1
25+
uses: docker/setup-buildx-action@v3
2626

2727
- name: Log in to Azure Container Registry - External Registry
2828
if: ${{ github.ref_name == 'main' }}

.github/workflows/node.js.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ jobs:
2222
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
2323

2424
steps:
25-
- uses: actions/checkout@v3
25+
- uses: actions/checkout@v4
2626
- name: Use Node.js ${{ matrix.node-version }}
27-
uses: actions/setup-node@v3
27+
uses: actions/setup-node@v4
2828
with:
2929
node-version: ${{ matrix.node-version }}
3030
cache: 'npm'

.github/workflows/pylint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616

1717
# Step 2: Set up Python environment
1818
- name: Set up Python ${{ matrix.python-version }}
19-
uses: actions/setup-python@v3
19+
uses: actions/setup-python@v5
2020
with:
2121
python-version: ${{ matrix.python-version }}
2222

.github/workflows/python-app.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ jobs:
1717
runs-on:
1818
- ubuntu-latest
1919
steps:
20-
- uses: actions/checkout@v3
20+
- uses: actions/checkout@v4
2121
- name: Set up Python 3.11
22-
uses: actions/setup-python@v3
22+
uses: actions/setup-python@v5
2323
with:
2424
python-version: "3.11"
2525
- name: Install dependencies
@@ -35,9 +35,9 @@ jobs:
3535
runs-on:
3636
- windows-latest
3737
steps:
38-
- uses: actions/checkout@v3
38+
- uses: actions/checkout@v4
3939
- name: Set up Python 3.11
40-
uses: actions/setup-python@v3
40+
uses: actions/setup-python@v5
4141
with:
4242
python-version: "3.11"
4343
- name: Install dependencies

.github/workflows/tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,12 @@ jobs:
6666

6767
# Set up Node.js environment for Frontend
6868
- name: Set up Node.js
69-
uses: actions/setup-node@v3
69+
uses: actions/setup-node@v4
7070
with:
7171
node-version: '20' # Set the Node.js version
7272

7373
- name: Cache npm dependencies
74-
uses: actions/cache@v3
74+
uses: actions/cache@v4
7575
with:
7676
path: ~/.npm
7777
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

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

0 commit comments

Comments
 (0)