feat(foundry): eject infrastructure for existing projects - #9683
feat(foundry): eject infrastructure for existing projects#9683Zhijie Huang (hund030) wants to merge 5 commits into
Conversation
- unify existing-project provisioning across embedded and on-disk graphs - generate mode-specific Bicep and Terraform ACR infrastructure
|
Azure Pipelines: Successfully started running 2 pipeline(s). 19 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Adds editable Bicep and Terraform infrastructure eject flows for existing Foundry projects while preserving ownership boundaries.
Changes:
- Adds existing-project IaC templates and ACR modes.
- Aligns provisioning, validation, outputs, and teardown behavior.
- Expands eject, synthesis, and dependency tests.
Reviewed changes
Copilot reviewed 62 out of 64 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
azure.ai.projects/.../terraform/outputs.tf.tmpl |
Reorders connection outputs. |
azure.ai.projects/.../terraform/container-registry.tf |
Adds greenfield ACR resources. |
azure.ai.projects/.../terraform-existing-project/variables.tf |
Defines existing-project inputs. |
azure.ai.projects/.../terraform-existing-project/provider.tf |
Configures cross-subscription providers. |
azure.ai.projects/.../terraform-existing-project/outputs.tf.tmpl |
Emits existing-project outputs. |
azure.ai.projects/.../terraform-existing-project/main.tf |
Manages model deployments. |
azure.ai.projects/.../terraform-existing-project/container-registry-reuse.tf |
Reuses ACR with role assignment. |
azure.ai.projects/.../terraform-existing-project/container-registry-create.tf |
Creates adjunct ACR infrastructure. |
azure.ai.projects/.../terraform-existing-project/container-registry-connect.tf |
Connects an existing ACR. |
azure.ai.projects/.../terraform-existing-project/connections.tf |
Manages declared connections. |
azure.ai.projects/.../modules/foundry-project.bicep |
Adds shared project-child module. |
azure.ai.projects/.../modules/container-registry.bicep |
Adds shared ACR module. |
azure.ai.projects/.../modules/container-registry-eject.bicep.tmpl |
Adds mode-specific eject module. |
azure.ai.projects/.../templates/main.bicep |
Adds connection endpoint output. |
azure.ai.projects/.../templates/main.arm.json |
Regenerates compiled ARM. |
azure.ai.projects/.../templates/existing-project.bicep |
Adds embedded existing-project graph. |
azure.ai.projects/.../templates/existing-project.arm.json |
Adds compiled existing-project ARM. |
azure.ai.projects/.../templates/existing-project-eject.bicep.tmpl |
Adds editable Bicep graph. |
azure.ai.projects/.../templates/brownfield.bicep |
Removes legacy brownfield template. |
azure.ai.projects/.../templates/brownfield.arm.json |
Removes legacy compiled ARM. |
azure.ai.projects/.../synthesis/templates_embed.go |
Embeds new template sets. |
azure.ai.projects/.../synthesis/synthesizer.go |
Adds existing-project synthesis. |
azure.ai.projects/.../synthesis/synthesizer_test.go |
Updates template embedding tests. |
azure.ai.projects/.../synthesis/schema_test.go |
Updates ARM drift checks. |
azure.ai.projects/.../synthesis/parity_test.go |
Tightens byte parity checks. |
azure.ai.projects/.../provisioning/resource_group_location_check.go |
Checks create-mode adjunct groups. |
azure.ai.projects/.../resource_group_location_check_validate_test.go |
Tests adjunct-group validation. |
azure.ai.projects/.../provisioning/ondisk_template.go |
Filters host parameters by template. |
azure.ai.projects/.../provisioning/ondisk_template_test.go |
Tests parameter filtering. |
azure.ai.projects/.../foundry_provisioning_provider_test.go |
Expands existing-project lifecycle tests. |
azure.ai.projects/.../foundry_provisioning_provider_brownfield_acr_test.go |
Removes obsolete brownfield tests. |
azure.ai.projects/.../exterrors/codes.go |
Adds connection operation codes. |
azure.ai.agents/.../terraform/outputs.tf.tmpl |
Reorders connection outputs. |
azure.ai.agents/.../terraform/container-registry.tf |
Adds greenfield ACR resources. |
azure.ai.agents/.../terraform-existing-project/variables.tf |
Defines existing-project inputs. |
azure.ai.agents/.../terraform-existing-project/provider.tf |
Configures cross-subscription providers. |
azure.ai.agents/.../terraform-existing-project/outputs.tf.tmpl |
Emits existing-project outputs. |
azure.ai.agents/.../terraform-existing-project/main.tf |
Manages model deployments. |
azure.ai.agents/.../terraform-existing-project/container-registry-reuse.tf |
Reuses ACR with role assignment. |
azure.ai.agents/.../terraform-existing-project/container-registry-create.tf |
Creates adjunct ACR infrastructure. |
azure.ai.agents/.../terraform-existing-project/container-registry-connect.tf |
Connects an existing ACR. |
azure.ai.agents/.../terraform-existing-project/connections.tf |
Manages declared connections. |
azure.ai.agents/.../modules/foundry-project.bicep |
Adds shared project-child module. |
azure.ai.agents/.../modules/container-registry.bicep |
Adds shared ACR module. |
azure.ai.agents/.../modules/container-registry-eject.bicep.tmpl |
Adds mode-specific eject module. |
azure.ai.agents/.../templates/main.bicep |
Adds connection endpoint output. |
azure.ai.agents/.../templates/main.arm.json |
Regenerates compiled ARM. |
azure.ai.agents/.../templates/existing-project.bicep |
Adds embedded existing-project graph. |
azure.ai.agents/.../templates/existing-project-eject.bicep.tmpl |
Adds editable Bicep graph. |
azure.ai.agents/.../templates/brownfield.bicep |
Removes legacy brownfield template. |
azure.ai.agents/.../templates/brownfield.arm.json |
Removes legacy compiled ARM. |
azure.ai.agents/.../synthesis/templates_embed.go |
Embeds new template sets. |
azure.ai.agents/.../synthesis/synthesizer.go |
Adds existing-project synthesis. |
azure.ai.agents/.../synthesis/synthesizer_test.go |
Updates embedding tests. |
azure.ai.agents/.../synthesis/schema_test.go |
Updates ARM drift checks. |
azure.ai.agents/.../project/foundry_dependencies.go |
Scopes connection readiness. |
azure.ai.agents/.../project/foundry_dependencies_test.go |
Tests cross-project rejection. |
azure.ai.agents/.../cmd/init.go |
Supplies environment state during eject. |
azure.ai.agents/.../cmd/init_infra_test.go |
Adds eject-mode coverage. |
azure.ai.agents/.../cmd/init_foundry_resources_helpers.go |
Persists ACR mode and assignment state. |
azure.ai.agents/docs/infrastructure-eject.md |
Documents existing-project eject behavior. |
Suppressed comments (1)
cli/azd/extensions/azure.ai.agents/internal/cmd/init_infra_test.go:1063
- [azd-code-reviewer] Checking only for
azmakes this test fail on machines that have Azure CLI but have not installed its Bicep component;az bicep buildmay prompt or attempt a download rather than skip. Detect an already-installed standalone or~/.azure/binBicep binary, as the synthesis drift tests do, and invoke it directly so this unit test stays offline and deterministic.
az, err := exec.LookPath("az")
if err != nil {
t.Skip("Azure CLI not found; skipping generated Bicep compilation")
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 63 out of 65 changed files in this pull request and generated no new comments.
Suppressed comments (3)
cli/azd/extensions/azure.ai.agents/internal/cmd/init_infra.go:2233
- An existing-project eject is explicitly marked as needing environment state, but this branch silently continues without it.
ejectInfrathen skips the project identity checks and, for container-based agents, inferscreatemode, so the generated templates may target an unvalidated project and fail to preserve the ACR choice made during init. Return a validation error requiring an active environment instead. [azd-code-reviewer]
if current == nil || current.Environment == nil || current.Environment.Name == "" {
return nil, nil
}
cli/azd/extensions/azure.ai.projects/internal/provisioning/resource_group_location_check.go:185
- This can report a blocking location mismatch for a prebuilt-image environment. The provider forces existing-project ACR mode to
nonewheneverAZD_AGENT_SKIP_ACR=true, but this check consults the persisted mode first; an environment switched fromcreateto prebuilt can therefore retainAZD_FOUNDRY_ACR_MODE=createand be treated as creating an adjunct resource group even though provisioning will not create one. Check the skip flag before the mode. [azd-code-reviewer]
cli/azd/extensions/azure.ai.projects/internal/provisioning/foundry_provisioning_provider.go:510 - The new
AZD_FOUNDRY_ACR_MODEandAZD_FOUNDRY_ACR_PULL_ASSIGNEDenvironment inputs are not documented incli/azd/docs/environment-variables.md, which is the repository’s required source of truth for every environment variable azd reads. Add both variables there with their accepted values/default behavior (and document the new connection-project endpoint key introduced by this change as well). [azd-code-reviewer]
| } | ||
| return false, exterrors.ServiceFromAzure(err, exterrors.OpResourceGroupGet) | ||
| p.foundryRGOwnerID = fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", p.subID, p.rgName) | ||
| return p.setEnv(recoveryCtx, envKeyFoundryRGOwner, p.foundryRGOwnerID) |
There was a problem hiding this comment.
[suggestion] [azd-code-reviewer] Persisting only the owner ID does not make a partially created adjunct group removable after a failed deployment. When AZURE_FOUNDRY_RESOURCE_GROUP was initially absent, the next Initialize derives the default name but leaves rgExplicit=false, so Destroy rejects the cleanup before it checks this recovered ownership marker. Persist envKeyFoundryRG together with envKeyFoundryRGOwner (or allow the exact owned ID to establish the group name), and cover the failed-first-provision cleanup path.
| p.existingProjectConnectionOnly = true | ||
| p.synthResult = connectionOnlyResult | ||
| p.foundryName = projectNameFromEndpoint(endpoint) | ||
| return p.resolveEnvName(ctx) |
There was a problem hiding this comment.
[suggestion] [azd-code-reviewer] This early return leaves armTemplate unset, but Preview no longer has the old brownfield no-op path and always calls resolveTemplate. As a result, azd provision --preview for an existing project with no deployments, connections, or ACR work now fails with CodeOnDiskTemplateMissing instead of returning an empty preview. Add an existingProjectConnectionOnly fast path to Preview (and a regression test), matching State and Deploy.
Summary
Existing Foundry projects can now eject editable Bicep or Terraform with
azd ai agent init --infra, instead of being limited to opaque brownfield provisioning. The generated infrastructure references the existing account and project without taking ownership of them.Design and UX
--infragenerates a rootinfra/layout for Foundry-only projects or an isolatedinfra/foundrylayer when infrastructure already exists.azure.yaml,AZURE_AI_PROJECT_ID, andFOUNDRY_PROJECT_ENDPOINTidentify the same project.azd downremoves only ownership-verified adjunct resources and their matching ACR connection; reused resources remain untouched.Changes
azure.ai.agents init: add existing-project Bicep and Terraform eject flows, layered-project migration, mode-specific ACR generation, and collision-safe installation.microsoft.foundry provider: unify embedded and on-disk existing-project provisioning, preserve endpoint-only compatibility, align planned outputs, and harden preview, state, and teardown behavior.synthesis templates: replace the brownfield templates with shared existing-project ARM/Bicep modules and editable Terraform templates.dependency outputs: scope connection readiness to the active Foundry project across embedded and ejected infrastructure.Manual Validation
terraform validate, and completed preview and provision for both IaC formats without modifying the reused account or project.azd downremoved only those owned resources.