Summary
When provisioning Azure AI Search as a dependent resource (via AI_PROJECT_DEPENDENT_RESOURCES containing {"resource":"azure_ai_search","connectionName":"search"}), infra/core/search/azure_ai_search.bicep requires a co-provisioned storage account and silently fails when one is not also requested.
The bicep takes storageAccountResourceId as a required param and uses it for an existing Storage Account reference plus a Storage Blob Data Reader role assignment and a knowledge container. If the consumer only requests azure_ai_search (not also storage), infra/main.bicep passes an empty string for storageAccountResourceId, which makes the existing reference resolve to last(split('', '/')) → empty name and the deployment fails.
Why this matters
The companion azd ai agent init flow in Azure/azure-dev (init.go ToolKindAzureAiSearch block) only auto-prompts for two tool ids when scanning the agent manifest:
if toolResource.Id == "bing_grounding" || toolResource.Id == "azure_ai_search" {
// prompt for connection name, append to project.Resource entries
}
There is no auto-prompt for storage, so an agent author who declares kind: tool / id: azure_ai_search in their agent.manifest.yaml ends up with a generated azure.yaml that requests only azure_ai_search → provision blows up.
End users have to manually edit the generated azure.yaml after azd ai agent init and append a storage entry, which is fragile and surprising. We hit this in microsoft-foundry/foundry-samples-pr#283 (Azure AI Search RAG sample for .NET Agent Framework).
Repro
- Author an
agent.manifest.yaml with kind: tool / id: azure_ai_search (no storage).
azd ai agent init -m <manifest> (accept the prompt for the search connection name).
azd provision.
- Bicep deployment of
azure-ai-search module fails resolving the existing storage account.
Proposed fix (pick one)
Option A: Make storage optional in azure_ai_search.bicep. Skip the Storage Blob Data Reader role assignment and the knowledge container creation when storageAccountResourceId is empty:
@description('Optional. Azure storage account resource ID for the search-to-storage indexer scenario. When empty, the search-to-storage RBAC and knowledge container are skipped.')
param storageAccountResourceId string = ''
// gate the existing storage reference + container + role assignment behind a non-empty check
var hasStorage = !empty(storageAccountResourceId)
resource storageAccount '...' existing = if (hasStorage) {
name: last(split(storageAccountResourceId, '/'))
}
resource storageContainer '...' = if (hasStorage) { ... }
resource searchToStorageRoleAssignment '...' = if (hasStorage) { ... }
This matches how ai-project.bicep already gates the azureAiSearch module behind hasSearchConnection and how it treats other dependent resources as independent.
Option B: In Azure/azure-dev, also auto-add a storage dependent resource whenever azure_ai_search is requested. Less ideal because (1) it forces a Storage Account on agents that don't need indexer scenarios, and (2) it puts knowledge of starter internals into the extension.
Option C: Both. Make storage optional in the bicep (Option A) and have the extension auto-pair them only when the user actually wants the indexer scenario (future azd ai agent init UX, e.g. a follow-up prompt).
Option A is the smallest, most local fix and unblocks the manifest-only flow today. Happy to send a PR if that direction is preferred.
Workaround (for users hitting this today)
After azd ai agent init, manually append storage to the agent service's resources: array in the generated azure.yaml:
services:
<agent-name>:
config:
resources:
- resource: azure_ai_search
connectionName: search
- resource: storage
connectionName: storage
Context
Summary
When provisioning Azure AI Search as a dependent resource (via
AI_PROJECT_DEPENDENT_RESOURCEScontaining{"resource":"azure_ai_search","connectionName":"search"}),infra/core/search/azure_ai_search.biceprequires a co-provisioned storage account and silently fails when one is not also requested.The bicep takes
storageAccountResourceIdas a required param and uses it for anexistingStorage Account reference plus a Storage Blob Data Reader role assignment and a knowledge container. If the consumer only requestsazure_ai_search(not alsostorage),infra/main.biceppasses an empty string forstorageAccountResourceId, which makes theexistingreference resolve tolast(split('', '/'))→ empty name and the deployment fails.Why this matters
The companion
azd ai agent initflow inAzure/azure-dev(init.goToolKindAzureAiSearchblock) only auto-prompts for two tool ids when scanning the agent manifest:There is no auto-prompt for
storage, so an agent author who declareskind: tool/id: azure_ai_searchin theiragent.manifest.yamlends up with a generatedazure.yamlthat requests onlyazure_ai_search→ provision blows up.End users have to manually edit the generated
azure.yamlafterazd ai agent initand append astorageentry, which is fragile and surprising. We hit this in microsoft-foundry/foundry-samples-pr#283 (Azure AI Search RAG sample for .NET Agent Framework).Repro
agent.manifest.yamlwithkind: tool/id: azure_ai_search(no storage).azd ai agent init -m <manifest>(accept the prompt for the search connection name).azd provision.azure-ai-searchmodule fails resolving theexistingstorage account.Proposed fix (pick one)
Option A: Make storage optional in
azure_ai_search.bicep. Skip theStorage Blob Data Readerrole assignment and the knowledge container creation whenstorageAccountResourceIdis empty:This matches how
ai-project.bicepalready gates theazureAiSearchmodule behindhasSearchConnectionand how it treats other dependent resources as independent.Option B: In
Azure/azure-dev, also auto-add astoragedependent resource wheneverazure_ai_searchis requested. Less ideal because (1) it forces a Storage Account on agents that don't need indexer scenarios, and (2) it puts knowledge of starter internals into the extension.Option C: Both. Make storage optional in the bicep (Option A) and have the extension auto-pair them only when the user actually wants the indexer scenario (future
azd ai agent initUX, e.g. a follow-up prompt).Option A is the smallest, most local fix and unblocks the manifest-only flow today. Happy to send a PR if that direction is preferred.
Workaround (for users hitting this today)
After
azd ai agent init, manually appendstorageto the agent service'sresources:array in the generatedazure.yaml:Context
init.go,listen.go,parse.go