Skip to content

Commit 456b310

Browse files
committed
fix(infra): switch search connection to AAD auth and add KB RBAC roles
- Change AI Search Foundry connection from ApiKey to AAD with managed identity - Add Search Service Contributor role for user-assigned MI - Add Cognitive Services OpenAI User role for Search system MI on AI Services - Add post-deploy step to seed KB MCP RemoteTool connections
1 parent 533d74e commit 456b310

5 files changed

Lines changed: 271 additions & 86 deletions

File tree

infra/main.bicep

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,6 @@ param gptImageModelVersion string = '2025-12-16'
8787
@description('Optional. Version of the Azure OpenAI service to deploy. Defaults to 2024-12-01-preview.')
8888
param azureopenaiVersion string = '2024-12-01-preview'
8989

90-
@description('Optional. Version of the Azure AI Agent API version. Defaults to 2025-01-01-preview.')
91-
param azureAiAgentAPIVersion string = '2025-01-01-preview'
92-
9390
@minLength(1)
9491
@allowed([
9592
'Standard'
@@ -1348,10 +1345,6 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
13481345
name: 'AZURE_OPENAI_ENDPOINT'
13491346
value: 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/'
13501347
}
1351-
{
1352-
name: 'AZURE_OPENAI_MODEL_NAME'
1353-
value: aiFoundryAiServicesModelDeployment.name
1354-
}
13551348
{
13561349
name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
13571350
value: aiFoundryAiServicesModelDeployment.name
@@ -1392,18 +1385,14 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
13921385
// name: 'AZURE_AI_AGENT_ENDPOINT'
13931386
// value: aiFoundryAiProjectEndpoint
13941387
// }
1395-
{
1396-
name: 'AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME'
1397-
value: aiFoundryAiServicesModelDeployment.name
1398-
}
13991388
{
14001389
name: 'APP_ENV'
14011390
value: 'Prod'
14021391
}
1403-
{
1404-
name: 'AZURE_AI_SEARCH_CONNECTION_NAME'
1405-
value: aiSearchConnectionName
1406-
}
1392+
// NOTE: AZURE_AI_SEARCH_CONNECTION_NAME intentionally omitted.
1393+
// The app defaults to per-KB RemoteTool connection names (e.g.
1394+
// "macae-retail-customer-kb-mcp") which carry ProjectManagedIdentity
1395+
// auth required by the KB MCP endpoint.
14071396
{
14081397
name: 'AZURE_AI_SEARCH_ENDPOINT'
14091398
value: searchService.outputs.endpoint
@@ -1412,18 +1401,6 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
14121401
name: 'AZURE_COGNITIVE_SERVICES'
14131402
value: 'https://cognitiveservices.azure.com/.default'
14141403
}
1415-
{
1416-
name: 'AZURE_BING_CONNECTION_NAME'
1417-
value: 'binggrnd'
1418-
}
1419-
{
1420-
name: 'BING_CONNECTION_NAME'
1421-
value: 'binggrnd'
1422-
}
1423-
{
1424-
name: 'REASONING_MODEL_NAME'
1425-
value: aiFoundryAiServicesReasoningModelDeployment.name
1426-
}
14271404
{
14281405
name: 'AZURE_OPENAI_IMAGE_DEPLOYMENT'
14291406
value: aiFoundryAiServicesImageModelDeployment.name
@@ -1465,12 +1442,8 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
14651442
value: aiFoundryAiProjectEndpoint
14661443
}
14671444
{
1468-
name: 'AZURE_AI_AGENT_API_VERSION'
1469-
value: azureAiAgentAPIVersion
1470-
}
1471-
{
1472-
name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING'
1473-
value: '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
1445+
name: 'ORCHESTRATOR_MODEL_NAME'
1446+
value: aiFoundryAiServicesReasoningModelDeployment.name
14741447
}
14751448
{
14761449
name: 'AZURE_BASIC_LOGGING_LEVEL'
@@ -1825,6 +1798,11 @@ module searchService 'br/public:avm/res/search/search-service:0.11.1' = {
18251798
roleDefinitionIdOrName: 'Search Index Data Contributor'
18261799
principalType: 'ServicePrincipal'
18271800
}
1801+
{
1802+
principalId: userAssignedIdentity.outputs.principalId
1803+
roleDefinitionIdOrName: 'Search Service Contributor'
1804+
principalType: 'ServicePrincipal'
1805+
}
18281806
{
18291807
principalId: deployingUserPrincipalId
18301808
roleDefinitionIdOrName: 'Search Index Data Contributor'
@@ -1898,6 +1876,11 @@ module searchServiceIdentity 'br/public:avm/res/search/search-service:0.11.1' =
18981876
roleDefinitionIdOrName: 'Search Index Data Contributor'
18991877
principalType: 'ServicePrincipal'
19001878
}
1879+
{
1880+
principalId: userAssignedIdentity.outputs.principalId
1881+
roleDefinitionIdOrName: 'Search Service Contributor'
1882+
principalType: 'ServicePrincipal'
1883+
}
19011884
{
19021885
principalId: deployingUserPrincipalId
19031886
roleDefinitionIdOrName: 'Search Index Data Contributor'
@@ -1941,6 +1924,23 @@ module searchServiceIdentity 'br/public:avm/res/search/search-service:0.11.1' =
19411924
]
19421925
}
19431926

1927+
// ========== Search Service MI → AI Services Role Assignment ========== //
1928+
// The Search service system MI needs Cognitive Services OpenAI User on the AI Services account
1929+
// so that Knowledge Base MCP tools can call the model for semantic retrieval.
1930+
resource aiServicesForSearchRole 'Microsoft.CognitiveServices/accounts@2025-06-01' existing = {
1931+
name: aiFoundryAiServicesResourceName
1932+
}
1933+
1934+
resource searchServiceOpenAIRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
1935+
name: guid(aiServicesForSearchRole.id, searchServiceName, '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd')
1936+
scope: aiServicesForSearchRole
1937+
properties: {
1938+
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd') // Cognitive Services OpenAI User
1939+
principalId: searchServiceIdentity.outputs.systemAssignedMIPrincipalId!
1940+
principalType: 'ServicePrincipal'
1941+
}
1942+
}
1943+
19441944
// ========== Search Service - AI Project Connection ========== //
19451945

19461946
var aiSearchConnectionName = 'aifp-srch-connection-${solutionSuffix}'
@@ -1954,7 +1954,6 @@ module aiSearchFoundryConnection 'modules/aifp-connections.bicep' = {
19541954
searchServiceResourceId: searchService.outputs.resourceId
19551955
searchServiceLocation: searchService.outputs.location
19561956
searchServiceName: searchService.outputs.name
1957-
searchApiKey: searchService.outputs.primaryKey
19581957
}
19591958
}
19601959

@@ -1977,7 +1976,6 @@ output COSMOSDB_ENDPOINT string = 'https://${cosmosDbResourceName}.documents.azu
19771976
output COSMOSDB_DATABASE string = cosmosDbDatabaseName
19781977
output COSMOSDB_CONTAINER string = cosmosDbDatabaseMemoryContainerName
19791978
output AZURE_OPENAI_ENDPOINT string = 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/'
1980-
output AZURE_OPENAI_MODEL_NAME string = aiFoundryAiServicesModelDeployment.name
19811979
output AZURE_OPENAI_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
19821980
output AZURE_OPENAI_RAI_DEPLOYMENT_NAME string = aiFoundryAiServices4_1ModelDeployment.name
19831981
output AZURE_OPENAI_API_VERSION string = azureopenaiVersion
@@ -1987,7 +1985,6 @@ output AZURE_AI_SUBSCRIPTION_ID string = subscription().subscriptionId
19871985
output AZURE_AI_RESOURCE_GROUP string = resourceGroup().name
19881986
output AZURE_AI_PROJECT_NAME string = aiFoundryAiProjectName
19891987
// output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.outputs.connectionString
1990-
output AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
19911988
// output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
19921989
output APP_ENV string = 'Prod'
19931990
output AI_FOUNDRY_RESOURCE_ID string = !useExistingAiFoundryAiProject
@@ -1998,17 +1995,15 @@ output AZURE_SEARCH_ENDPOINT string = searchService.outputs.endpoint
19981995
#disable-next-line BCP318
19991996
output AZURE_CLIENT_ID string = userAssignedIdentity!.outputs.clientId
20001997
output AZURE_TENANT_ID string = tenant().tenantId
2001-
output AZURE_AI_SEARCH_CONNECTION_NAME string = aiSearchConnectionName
20021998
output AZURE_COGNITIVE_SERVICES string = 'https://cognitiveservices.azure.com/.default'
2003-
output REASONING_MODEL_NAME string = aiFoundryAiServicesReasoningModelDeployment.name
1999+
output ORCHESTRATOR_MODEL_NAME string = aiFoundryAiServicesReasoningModelDeployment.name
20042000
output MCP_SERVER_NAME string = 'MacaeMcpServer'
20052001
output MCP_SERVER_DESCRIPTION string = 'MCP server with greeting, HR, and planning tools'
20062002
output SUPPORTED_MODELS string = supportedModelsList
20072003
output BACKEND_URL string = 'https://${containerAppResourceName}.${containerAppEnvironment.outputs.defaultDomain}'
20082004
output AZURE_AI_PROJECT_ENDPOINT string = aiFoundryAiProjectEndpoint
20092005
output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
2010-
output AZURE_AI_AGENT_API_VERSION string = azureAiAgentAPIVersion
2011-
output AZURE_AI_AGENT_PROJECT_CONNECTION_STRING string = '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
2006+
20122007

20132008

20142009
output AZURE_STORAGE_CONTAINER_NAME_RETAIL_CUSTOMER string = storageContainerNameRetailCustomer

infra/main_custom.bicep

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,6 @@ param gptImageModelVersion string = '2025-12-16'
8282
@description('Optional. Version of the Azure OpenAI service to deploy. Defaults to 2025-01-01-preview.')
8383
param azureopenaiVersion string = '2024-12-01-preview'
8484

85-
@description('Optional. Version of the Azure AI Agent API version. Defaults to 2025-01-01-preview.')
86-
param azureAiAgentAPIVersion string = '2025-01-01-preview'
87-
8885
@minLength(1)
8986
@allowed([
9087
'Standard'
@@ -1349,10 +1346,6 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
13491346
name: 'AZURE_OPENAI_ENDPOINT'
13501347
value: 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/'
13511348
}
1352-
{
1353-
name: 'AZURE_OPENAI_MODEL_NAME'
1354-
value: aiFoundryAiServicesModelDeployment.name
1355-
}
13561349
{
13571350
name: 'AZURE_OPENAI_DEPLOYMENT_NAME'
13581351
value: aiFoundryAiServicesModelDeployment.name
@@ -1393,18 +1386,14 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
13931386
// name: 'AZURE_AI_AGENT_ENDPOINT'
13941387
// value: aiFoundryAiProjectEndpoint
13951388
// }
1396-
{
1397-
name: 'AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME'
1398-
value: aiFoundryAiServicesModelDeployment.name
1399-
}
14001389
{
14011390
name: 'APP_ENV'
14021391
value: 'Prod'
14031392
}
1404-
{
1405-
name: 'AZURE_AI_SEARCH_CONNECTION_NAME'
1406-
value: aiSearchConnectionName
1407-
}
1393+
// NOTE: AZURE_AI_SEARCH_CONNECTION_NAME intentionally omitted.
1394+
// The app defaults to per-KB RemoteTool connection names (e.g.
1395+
// "macae-retail-customer-kb-mcp") which carry ProjectManagedIdentity
1396+
// auth required by the KB MCP endpoint.
14081397
{
14091398
name: 'AZURE_AI_SEARCH_ENDPOINT'
14101399
value: searchService.outputs.endpoint
@@ -1413,18 +1402,6 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
14131402
name: 'AZURE_COGNITIVE_SERVICES'
14141403
value: 'https://cognitiveservices.azure.com/.default'
14151404
}
1416-
{
1417-
name: 'AZURE_BING_CONNECTION_NAME'
1418-
value: 'binggrnd'
1419-
}
1420-
{
1421-
name: 'BING_CONNECTION_NAME'
1422-
value: 'binggrnd'
1423-
}
1424-
{
1425-
name: 'REASONING_MODEL_NAME'
1426-
value: aiFoundryAiServicesReasoningModelDeployment.name
1427-
}
14281405
{
14291406
name: 'MCP_SERVER_ENDPOINT'
14301407
value: 'https://${containerAppMcp.outputs.fqdn}/mcp'
@@ -1474,12 +1451,8 @@ module containerApp 'br/public:avm/res/app/container-app:0.18.1' = {
14741451
value: aiFoundryAiProjectEndpoint
14751452
}
14761453
{
1477-
name: 'AZURE_AI_AGENT_API_VERSION'
1478-
value: azureAiAgentAPIVersion
1479-
}
1480-
{
1481-
name: 'AZURE_AI_AGENT_PROJECT_CONNECTION_STRING'
1482-
value: '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
1454+
name: 'ORCHESTRATOR_MODEL_NAME'
1455+
value: aiFoundryAiServicesReasoningModelDeployment.name
14831456
}
14841457
{
14851458
name: 'AZURE_DEV_COLLECT_TELEMETRY'
@@ -1899,7 +1872,6 @@ module aiSearchFoundryConnection 'modules/aifp-connections.bicep' = {
18991872
searchServiceResourceId: searchService.outputs.resourceId
19001873
searchServiceLocation: searchService.outputs.location
19011874
searchServiceName: searchService.outputs.name
1902-
searchApiKey: searchService.outputs.primaryKey
19031875
}
19041876
}
19051877

@@ -1976,7 +1948,6 @@ output COSMOSDB_ENDPOINT string = 'https://${cosmosDbResourceName}.documents.azu
19761948
output COSMOSDB_DATABASE string = cosmosDbDatabaseName
19771949
output COSMOSDB_CONTAINER string = cosmosDbDatabaseMemoryContainerName
19781950
output AZURE_OPENAI_ENDPOINT string = 'https://${aiFoundryAiServicesResourceName}.openai.azure.com/'
1979-
output AZURE_OPENAI_MODEL_NAME string = aiFoundryAiServicesModelDeployment.name
19801951
output AZURE_OPENAI_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
19811952
output AZURE_OPENAI_RAI_DEPLOYMENT_NAME string = aiFoundryAiServices4_1ModelDeployment.name
19821953
output AZURE_OPENAI_API_VERSION string = azureopenaiVersion
@@ -1987,7 +1958,6 @@ output AZURE_AI_RESOURCE_GROUP string = resourceGroup().name
19871958
output AZURE_AI_PROJECT_NAME string = aiFoundryAiProjectName
19881959
output AZURE_AI_MODEL_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
19891960
// output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.outputs.connectionString
1990-
output AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME string = aiFoundryAiServicesModelDeployment.name
19911961
// output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
19921962
output APP_ENV string = 'Prod'
19931963
output AI_FOUNDRY_RESOURCE_ID string = !useExistingAiFoundryAiProject
@@ -1998,18 +1968,15 @@ output AZURE_SEARCH_ENDPOINT string = searchService.outputs.endpoint
19981968
#disable-next-line BCP318
19991969
output AZURE_CLIENT_ID string = userAssignedIdentity!.outputs.clientId
20001970
output AZURE_TENANT_ID string = tenant().tenantId
2001-
output AZURE_AI_SEARCH_CONNECTION_NAME string = aiSearchConnectionName
20021971
output AZURE_COGNITIVE_SERVICES string = 'https://cognitiveservices.azure.com/.default'
2003-
output REASONING_MODEL_NAME string = aiFoundryAiServicesReasoningModelDeployment.name
1972+
output ORCHESTRATOR_MODEL_NAME string = aiFoundryAiServicesReasoningModelDeployment.name
20041973
output MCP_SERVER_NAME string = 'MacaeMcpServer'
20051974
output MCP_SERVER_DESCRIPTION string = 'MCP server with greeting, HR, and planning tools'
20061975
output SUPPORTED_MODELS string = supportedModelsList
20071976
output AZURE_AI_SEARCH_API_KEY string = '<Deployed-Search-ApiKey>'
20081977
output BACKEND_URL string = 'https://${containerApp.outputs.fqdn}'
20091978
output AZURE_AI_PROJECT_ENDPOINT string = aiFoundryAiProjectEndpoint
20101979
output AZURE_AI_AGENT_ENDPOINT string = aiFoundryAiProjectEndpoint
2011-
output AZURE_AI_AGENT_API_VERSION string = azureAiAgentAPIVersion
2012-
output AZURE_AI_AGENT_PROJECT_CONNECTION_STRING string = '${aiFoundryAiServicesResourceName}.services.ai.azure.com;${aiFoundryAiServicesSubscriptionId};${aiFoundryAiServicesResourceGroupName};${aiFoundryAiProjectResourceName}'
20131980
output AZURE_DEV_COLLECT_TELEMETRY string = 'no'
20141981

20151982
// Container Registry Outputs

infra/modules/aifp-connections.bicep

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1+
// ========================================================================
2+
// Base AI Search connection (CognitiveSearch / AAD).
3+
// Per-KB RemoteTool connections (ProjectManagedIdentity) are created by
4+
// infra/scripts/seed_kb_connections.py at post-deploy time because the KB
5+
// names are dynamic and depend on selected content packs.
6+
// ========================================================================
7+
18
param aifSearchConnectionName string
29
param searchServiceName string
310
param searchServiceResourceId string
411
param searchServiceLocation string
512
param aiFoundryName string
613
param aiFoundryProjectName string
7-
@secure()
8-
param searchApiKey string
914

1015
resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-04-01-preview' = {
1116
name: '${aiFoundryName}/${aiFoundryProjectName}/${aifSearchConnectionName}'
1217
properties: {
1318
category: 'CognitiveSearch'
1419
target: 'https://${searchServiceName}.search.windows.net'
15-
authType: 'ApiKey'
16-
credentials: {
17-
key: searchApiKey
18-
}
20+
authType: 'AAD'
21+
useWorkspaceManagedIdentity: true
1922
isSharedToAll: true
2023
metadata: {
2124
ApiType: 'Azure'

infra/scripts/post_deploy.ps1

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,22 @@ if ($selectedDataPacks.Count -gt 0) {
464464
} else {
465465
Write-Host " Knowledge bases seeded successfully."
466466
}
467+
468+
# ──────────────────────────────────────────────────────────────────────────
469+
# 9. Create RemoteTool connections for KB MCP endpoints
470+
# ──────────────────────────────────────────────────────────────────────────
471+
472+
Write-Host "`n── Step 5: Creating KB MCP RemoteTool connections ──" -ForegroundColor Green
473+
474+
$env:AZURE_AI_SEARCH_ENDPOINT = $searchEndpoint
475+
$env:AZURE_AI_PROJECT_ENDPOINT = $projectEndpoint
476+
$process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/seed_kb_connections.py" -Wait -NoNewWindow -PassThru
477+
if ($process.ExitCode -ne 0) {
478+
Write-Host " ERROR: KB connection provisioning failed. Run 'python infra/scripts/seed_kb_connections.py' manually." -ForegroundColor Red
479+
$script:hasErrors = $true
480+
} else {
481+
Write-Host " KB MCP connections created successfully."
482+
}
467483
} else {
468484
Write-Host "`n Selected use case has no datasets to deploy — skipping blob/index/KB steps."
469485
}

0 commit comments

Comments
 (0)