Skip to content

feat(foundry): eject infrastructure for existing projects - #9683

Merged
Zhijie Huang (hund030) merged 9 commits into
Azure:mainfrom
hund030:feature/existing-project-infra-eject
Aug 25, 2026
Merged

feat(foundry): eject infrastructure for existing projects#9683
Zhijie Huang (hund030) merged 9 commits into
Azure:mainfrom
hund030:feature/existing-project-infra-eject

Conversation

@hund030

Copy link
Copy Markdown
Contributor

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

  • --infra generates a root infra/ layout for Foundry-only projects or an isolated infra/foundry layer when infrastructure already exists.
  • Embedded and ejected Bicep use the same existing-project resource graph; Terraform generates an equivalent mode-specific graph.
  • Generation preserves the ACR decision from init: create, reuse and connect, use an existing connection, or omit ACR.
  • Generated templates manage only declared project children and adjunct resources. Existing accounts, projects, registries, and connections remain references.
  • Eject is staged, never overwrites collisions, and refuses repeat eject so user edits remain protected.
  • Provisioning validates that azure.yaml, AZURE_AI_PROJECT_ID, and FOUNDRY_PROJECT_ENDPOINT identify the same project.
  • azd down removes 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

  • Initialized a hosted agent against a disposable existing Foundry project and validated embedded, ejected Bicep, and ejected Terraform flows.
  • Compiled generated Bicep, ran terraform validate, and completed preview and provision for both IaC formats without modifying the reused account or project.
  • Provisioned create-mode ACR infrastructure, confirmed the dedicated resource group, registry, role assignment, connection, and environment outputs, then verified azd down removed only those owned resources.
  • Verified already-connected and endpoint-only/prebuilt-image paths, endpoint mismatch rejection, idempotent preview, repeat-eject refusal, and preservation of the existing project binding after cleanup.

- unify existing-project provisioning across embedded and on-disk graphs

- generate mode-specific Bicep and Terraform ACR infrastructure
@azure-pipelines

Copy link
Copy Markdown
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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 az makes this test fail on machines that have Azure CLI but have not installed its Bicep component; az bicep build may prompt or attempt a download rather than skip. Detect an already-installed standalone or ~/.azure/bin Bicep 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.

Comment thread cli/azd/extensions/azure.ai.agents/internal/synthesis/templates_embed.go Outdated
Comment thread cli/azd/extensions/azure.ai.projects/internal/synthesis/templates_embed.go Outdated
Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_infra_test.go
Copilot AI review requested due to automatic review settings August 21, 2026 08:03
@github-actions github-actions Bot added ext-agents azure.ai.agents extension ext-projects azure.ai.projects extension labels Aug 21, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 62 out of 64 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings August 21, 2026 09:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. ejectInfra then skips the project identity checks and, for container-based agents, infers create mode, 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 none whenever AZD_AGENT_SKIP_ACR=true, but this check consults the persisted mode first; an environment switched from create to prebuilt can therefore retain AZD_FOUNDRY_ACR_MODE=create and 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_MODE and AZD_FOUNDRY_ACR_PULL_ASSIGNED environment inputs are not documented in cli/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]

Copilot AI review requested due to automatic review settings August 24, 2026 08:33
@github-actions github-actions Bot added the ext-foundry azure.ai.{agents,connections,inspector,projects,routines,skills,toolboxes}, microsoft.foundry label Aug 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 65 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

cli/azd/extensions/azure.ai.projects/internal/provisioning/foundry_provisioning_provider.go:1575

  • This rejects reuse-connect before cleanup, even though that mode creates an AcrPull role assignment and a project ACR connection while reusing the registry. Consequently azd down cannot remove the adjunct resources created by azd, contrary to the stated teardown behavior. Handle reuse-connect by deleting the ownership-verified connection and role assignment while leaving the registry and Foundry project untouched.

cli/azd/extensions/azure.ai.agents/internal/cmd/init_infra_test.go:1046

  • This test file still has multiple lines above the repository's enforced 125-character Go limit (including 1046, 1050–1051, 1056, 1061, and later additions). golangci-lint's lll check will fail preflight; please reformat all of the newly added long literals and calls.
		{name: "create", env: map[string]string{"AZD_FOUNDRY_ACR_MODE": "create"}, wantMode: "create", wantMain: "resource adjunctResourceGroup", wantRegistry: "resource registry ", expectRegistry: true},

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_infra.go Outdated
Copilot AI review requested due to automatic review settings August 25, 2026 05:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 65 changed files in this pull request and generated 1 comment.

Comment thread cli/azd/extensions/azure.ai.agents/internal/cmd/init_infra.go Outdated
Copilot AI review requested due to automatic review settings August 25, 2026 06:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 65 out of 67 changed files in this pull request and generated 3 comments.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

cli/azd/extensions/azure.ai.projects/internal/provisioning/foundry_provisioning_provider.go:1575

  • For an existing project, this gate also rejects none and already-connected, even though those generated graphs own no adjunct resource to delete. A Docker-backed project using an existing ACR connection therefore makes azd down fail instead of preserving the reused resources as a successful no-op; manually deleting project children cannot clear this error because the branch never inspects Azure. Handle the non-owning modes as no-op success (while keeping the stricter reuse-connect handling if needed) and add mode-specific teardown tests.

Copilot AI review requested due to automatic review settings August 25, 2026 08:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 65 out of 67 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

cli/azd/extensions/azure.ai.projects/internal/synthesis/templates/terraform-existing-project/outputs.tf.tmpl:25

  • [azd-code-reviewer] This precondition still validates only two environment-fed values. After eject, changing the service endpoint: in azure.yaml leaves both project_endpoint and project_resource_id pointing at the old project, so Terraform passes this check because the microsoft.foundry provider no longer runs. Bake the configured YAML endpoint into the generated Terraform inputs and compare it here as well to enforce the promised three-way identity validation.
    cli/azd/extensions/azure.ai.agents/internal/synthesis/templates/terraform-existing-project/outputs.tf.tmpl:25
  • [azd-code-reviewer] This precondition still validates only two environment-fed values. After eject, changing the service endpoint: in azure.yaml leaves both project_endpoint and project_resource_id pointing at the old project, so Terraform passes this check because the microsoft.foundry provider no longer runs. Bake the configured YAML endpoint into the generated Terraform inputs and compare it here as well to enforce the promised three-way identity validation.
  precondition {
    condition = (
      lower(local.project_endpoint_account) == lower(local.foundry_account_name) &&
      lower(local.project_endpoint_project) == lower(local.foundry_project_name)
    )
    error_message = "project_endpoint must identify the same Foundry project as project_resource_id."

cli/azd/extensions/azure.ai.projects/internal/provisioning/resource_group_location_check.go:184

  • [azd-code-reviewer] This returns true from persisted mode before checking the current service graph. If a project previously created ACR and later removes docker: (or sets AZD_AGENT_SKIP_ACR), AZD_FOUNDRY_ACR_MODE can remain create, while provider initialization correctly overrides the effective mode to none; validation can then block on an adjunct resource-group location that provision will not touch. Derive includeAcr first and require both it and effective create mode before enabling this check.

@hund030
Zhijie Huang (hund030) merged commit 4009866 into Azure:main Aug 25, 2026
46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ext-agents azure.ai.agents extension ext-foundry azure.ai.{agents,connections,inspector,projects,routines,skills,toolboxes}, microsoft.foundry ext-projects azure.ai.projects extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Agents Extension] Unable to eject infra with existing foundry project

4 participants