Fix azd down --no-prompt hang in CI for Terraform (#4317)#9143
Fix azd down --no-prompt hang in CI for Terraform (#4317)#9143hemarina wants to merge 8 commits into
Conversation
|
Azure Pipelines: Successfully started running 1 pipeline(s). 21 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 a Terraform destroy-preview path to prevent azd down --no-prompt from hanging in CI.
Changes:
- Runs
terraform plan -destroyin CI without--force. - Reports skipped deletion through provisioning and command layers.
- Adds tests for CI, force, and non-CI paths.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
cli/azd/pkg/tools/terraform/terraform.go |
Adds the destroy-plan command wrapper. |
cli/azd/pkg/infra/provisioning/terraform/terraform_provider.go |
Implements CI preview behavior. |
cli/azd/pkg/infra/provisioning/terraform/terraform_provider_test.go |
Tests destroy-path scenarios. |
cli/azd/pkg/infra/provisioning/provider.go |
Adds the skipped-deletion result field. |
cli/azd/cmd/down.go |
Handles skipped deletion at command level. |
jongio
left a comment
There was a problem hiding this comment.
The preview path fixes the hang for --no-prompt without --force. One gap on the --force side before this lands, noted inline.
…na/tf-down-no-prompt-4317
…na/tf-down-no-prompt-4317
jongio
left a comment
There was a problem hiding this comment.
Init now runs non-interactively before createOutputParameters on both the preview and --force CI paths, which closes the backend initialization required gap I raised. The clearCIEnv helper unsets every var IsRunningOnCI checks, so the non-CI test stays deterministic in real pipelines.
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
jongio
left a comment
There was a problem hiding this comment.
The two new tests exercise the down action's SkippedDeletion handling: the skipped case asserts no teardown header, the normal case asserts the header is present. Both match down.go's behavior of returning an empty ActionResult when a layer was skipped.
One open item on the init gating, in the area I flagged earlier. init is keyed on automatedCI := resource.IsRunningOnCI() && t.console.IsNoPromptMode(). --force and --no-prompt are independent flags (down's forceDelete doesn't set console no-prompt), so azd down --force in CI without --no-prompt skips the init call and falls straight into createOutputParameters/terraform output. On a fresh agent with a remote backend, that's the same "backend initialization required" failure this PR is trying to avoid. My earlier approval note said init covers the --force CI path, but it only does so when --no-prompt is also set.
Is scoping the preview and init to no-prompt-only intentional? If azd down --force is still a documented CI path, keying init on IsRunningOnCI() alone, or on CI plus (no-prompt or force), would close that gap. Not blocking; the no-prompt hang fix itself is sound.
JeffreyCA
left a comment
There was a problem hiding this comment.
One gap (non-blocking): init is gated on IsRunningOnCI() && IsNoPromptMode(), but --force doesn't set no-prompt mode, so azd down --force in CI skips init and hits the same "backend initialization required" error from #4317.
Gating init on IsRunningOnCI() alone (keeping preview/skip on no-prompt-without-force) would close that path. A --force-in-CI test would be nice too.
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
jongio
left a comment
There was a problem hiding this comment.
The unified automated := IsRunningOnCI() || IsNoPromptMode() gate closes the init gap I raised earlier: azd down --force in CI now runs terraform init -input=false before createOutputParameters, so the backend initialization required symptom from #4317 is covered on the force path too. Folding --no-prompt outside CI into the same preview-and-skip path is consistent, since --no-prompt never answered terraform's own confirmation before. Tests cover the CI-preview, CI-force, non-CI --no-prompt, and interactive paths.
| // re-running it when the backend is already initialized is a safe no-op. | ||
| automated := resource.IsRunningOnCI() || t.console.IsNoPromptMode() | ||
| if automated { | ||
| if initRes, err := t.init(ctx, isRemoteBackendConfig, "-input=false"); err != nil { |
| // In an automated context without --force, azd cannot answer terraform's confirmation, so instead of | ||
| // hanging, preview the destroy plan (read-only) and stop without deleting — as if the confirmation were | ||
| // answered "no". Use --force to delete non-interactively. See issue #4317. | ||
| if automated && !options.Force() { |
jongio
left a comment
There was a problem hiding this comment.
Incremental changes since my last review are test-only and address the earlier feedback. TestTerraformDestroy now clears CI env so the interactive path isn't skipped under a CI runner, and both preview tests now assert terraform plan -destroy actually runs rather than just checking SkippedDeletion. Mock registration order (LIFO after preparePlanningMocks) and the matcher are correct.
Fixes #4317
Problem
azd down --no-prompthangs in CI/CD pipelines for Terraform-based projects.azddelegates the destroy confirmation to Terraform's own interactive prompt (terraform destroywithout-auto-approve). In a non-interactive context there is no TTY to answer it, so Terraform blocks forever on stdin. The only workaround was--force. A related symptom (#4317) isazd down --forcefailing on a fresh agent with "backend initialization required".Fix
When running non-interactively — detected as either a CI/CD environment (
resource.IsRunningOnCI()) or--no-promptmode — the Terraform provider now:terraform init -input=falsefor every automated destroy path (preview and--force), so a fresh agent no longer fails with "backend initialization required",--forceis not passed, shows a read-only destroy preview viaterraform plan -destroy -input=false(nothing is deleted), prints a warning scoped to the Terraform configuration that no resources were deleted and to re-run with--force, and returns cleanly (exit 0) instead of hanging.All other paths are unchanged:
--no-prompt): Terraform's normal confirmation prompt (user chooses); azd does not force init or preview.--force:terraform destroy -auto-approve(deletes).For a multi-layer
down, the environment cache is always invalidated (another layer may have deleted resources even if a Terraform layer was preview-only), and the full-teardown success message is suppressed whenever any layer was skipped.Behavior
--force--no-prompt--no-promptTesting
plan -destroyran, no delete), non-CI--no-promptpreview (assertsplan -destroyran, no delete), CI +--force(asserts init runs, deletes), interactive (no forced init/preview, deletes). Pre-existingTestTerraformDestroynow clears CI env so it does not inherit runner state.PlanDestroyadded to the terraform command-wrapper table test (asserts exact args incl.-destroyand-input=false).downtests assert the full-teardown header is suppressed onSkippedDeletionand that the cache is still invalidated.go build ./...,gofmt -s,golangci-lint, and existingdown/terraform tests all pass.