feat(agents): split Foundry init into per-resource azure.yaml services#8675
Conversation
There was a problem hiding this comment.
Pull request overview
This PR completes the extension-side work to have azd ai agent init emit a unified host: microsoft.foundry service entry in azure.yaml, aligning init output with the new Foundry service target shape (single hosted agent) and removing the legacy agent.yaml/manifest outputs.
Changes:
- Add a new
microsoft.foundryservice target provider that binds inline Foundry config fromServiceConfig.AdditionalPropertiesand deploys a single hosted agent (image or code-zip runtime). - Refactor init flows (
init+init-from-code) to write the unified Foundry service entry (deployments at service level + agent inline), and keep emitting.agentignorefor packaging. - Introduce Foundry config parsing/validation utilities and unit tests (config validation, endpoint parsing, and init-to-project wiring).
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/extensions/azure.ai.agents/internal/project/service_target_foundry.go | Implements host: microsoft.foundry deploy lifecycle for a single hosted agent and reuses shared agent deploy helpers. |
| cli/azd/extensions/azure.ai.agents/internal/project/service_target_agent.go | Extracts shared auth setup and ZIP packaging helper for reuse by the Foundry target. |
| cli/azd/extensions/azure.ai.agents/internal/project/foundry_project_resolve.go | Adds endpoint → ARM project ID resolution and endpoint parsing helpers. |
| cli/azd/extensions/azure.ai.agents/internal/project/foundry_project_resolve_test.go | Unit tests for Foundry endpoint parsing. |
| cli/azd/extensions/azure.ai.agents/internal/project/foundry_config.go | Defines/validates the unified Foundry config shapes (service + agent) and maps to deploy request types. |
| cli/azd/extensions/azure.ai.agents/internal/project/foundry_config_test.go | Unit tests for Foundry config validation and binding from AdditionalProperties. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go | Registers the new microsoft.foundry service target. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init.go | Updates init to stop writing agent.yaml and to emit unified Foundry service config + .agentignore. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init_from_code.go | Updates init-from-code to emit unified Foundry service config and derive runtime/entrypoint from on-disk definition. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init_foundry.go | Adds helper to build unified Foundry service config + helper mappers + .agentignore writer. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init_foundry_test.go | Unit tests for the unified Foundry service config builder helpers. |
| cli/azd/extensions/azure.ai.agents/internal/cmd/init_foundry_addtoproject_test.go | End-to-end-ish tests asserting init writes the unified Foundry service shape via gRPC AddService. |
| cli/azd/extensions/azure.ai.agents/extension.yaml | Registers the microsoft.foundry provider in the extension manifest. |
bc38428 to
15ef2ed
Compare
Review: unified
|
|
This fixes #7962 |
therealjohn
left a comment
There was a problem hiding this comment.
After more review, some changes:
- each resource needs to be it's own service entry
- each extension would own the schema for that service entry, similar to how the agents ext owns the schema for host: azure.ai.agents
- each extension would need a hook on doing dataplane operations, similar to how the agents ext has a post-deploy hook to create an agent
Right now, AZD does not support different extensions contributing to the same service host - so separating them makes that possible, but it also means we can more easily manipulate each resource as a service.
31b9b43 to
5cb2336
Compare
491afd5 to
b9d8cb6
Compare
d192494 to
08fad8d
Compare
| // single azure.ai.project service. A stable name keeps repeated inits | ||
| // idempotent (AddService overwrites by name) so there is one project | ||
| // service per project, matching the unified Foundry config design. | ||
| aiProjectServiceName = "ai-project" |
There was a problem hiding this comment.
Instead of having this fixed, should it be the name of the user's Foundry project?
There was a problem hiding this comment.
My thinking was to keep ai-project as a fixed internal key (rather than a display name), so re-running init would consistently reuse the same service instead of creating duplicates. A real project name could also potentially clash with other keys. Does that reasoning make sense to you?
There was a problem hiding this comment.
It shouldn't conflict with other keys since it's specific to the azure.ai.project scope, right? It just feels weird to me that everything else we have the name matches, but this one doesn't. Re-running init shouldn't create duplicates as long as users are using the same Foundry project. If they want to use a different Foundry project we would need to handle that, but personally it feels better to handle that than to have this discrepancy in the services. @therealjohn maybe you have an opinion here?
There was a problem hiding this comment.
@huimiu can we remain consistent with our other approach where the service name matches the user defined name when its a new project, or the one they select if its existing?
That being said - I dont think we have a feature where we allow a user to define a project name - its determined in the Bicep today and it's not customizable.
So if we dont have the upstream feature to support that lets file and track that with this.
There was a problem hiding this comment.
Sure, let me update in next PR
What this PR does
Changes how
azd ai agent initwrites Foundry resources intoazure.yaml: each resource type becomes its own service entry instead of being bundled into the single agent service.Per-resource
azure.yamlservicesBefore: all Foundry resources (model deployments, connections, toolboxes) were bundled into the single agent service entry.
After: each resource type gets its own
azure.yamlservice entry with a dedicated host:azure.ai.project— the Foundry project + model deployments (one per agent)azure.ai.connection— one entry per connectionazure.ai.toolbox— one entry per toolboxThe agent service lists these as
uses:dependencies soazdprovisions them in the right order.Example
azure.yamlafter init:Why split: per-resource services give each Foundry resource a clear ownership boundary in
azure.yamland make the provisioning graph explicit viauses:. For now the agents extension registers all three resource hosts (azure.ai.project,azure.ai.connection,azure.ai.toolbox) as no-op service targets, so only that extension needs to be installed forazd up/deployto walk the entries; the resources themselves are still provisioned by Bicep / the data-plane API duringprovision.Provisioning behavior is unchanged — the agent extension re-sources deployments, connections, and toolboxes from the sibling services when setting provisioning environment variables and creating toolsets. Projects created before the split (resources bundled on the agent service) keep working: the collectors fall back to the agent service config when no sibling services exist, so an existing
azure.yamlstill provisions without re-runninginit.Scope / follow-up
This PR is intentionally limited to the
azure.yamlsplit plus registering the resource hosts from the agents extension. Fully onboardingazure.ai.projects/azure.ai.connections/azure.ai.toolboxesas standalone azd extensions that own their resource's deploy-time lifecycle is a larger effort and will be iterated on separately — each host can move from the shared no-op provider in the agents extension to its own extension when it gains real behavior.Base branch
This PR targets
huimiu/foundry-azure-yaml, which carries only the already-merged Foundry azure.yaml schema work (#8603, #8627, #8689, #8643).