deploymentStacks: fold resolved stack options into deployment-state hash
- Dominant language
- Go
- Stars
- 569
- Forks
- 364
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 136
Description
## Context
Follow-up from PR #9238 (`${VAR}` substitution in `infra.deploymentStacks`).
The deployment-state shortcut in `bicep_provider.go` skips a redeploy when the compiled ARM template and parameters are unchanged (`SkippedReason: DeploymentStateSkipped`). Deployment-stacks control-plane options (`denySettings`, `actionOnUnmanage`) are **not** part of the template or parameters, so the hash does not observe them.
PR #9238 handles this with a narrow bypass: `useDeploymentStateShortcut` returns false whenever an active `deploymentStacks` block is present (`hasActiveDeploymentStacksConfig`). Reviewers (@jongio, @richardpark-msft, and Copilot) noted this gate is both too broad and too narrow.
## Problem
| Scenario | Current behavior | Desired |
|---|---|---|
| Change deny settings (incl. `${VAR}` result) | Redeploys — applied | applied |
| No change (stacks user, no `${VAR}`) | **Redeploys every `azd provision`** | shortcut should still apply |
| **Remove** the `deploymentStacks` block | Shortcut re-enabled -> **stale deny assignment stays in Azure** | should redeploy to clear |
| Empty literal `deploymentStacks:` | Bypass triggers | handled by hash |
## Proposed fix
Fold the **resolved** deployment-stacks map (the output of `resolveDeploymentStacksMap`, including its absence) into `currentParamsHash` (or an equivalent additional hash tag). Then:
- Unchanged config -> same hash -> shortcut preserved (no perf regression).
- Changed deny settings -> different hash -> redeploys.
- Removed block -> hash changes (present -> absent) -> redeploys and clears stale deny.
Remove the `hasActiveDeploymentStacksConfig` bypass in `useDeploymentStateShortcut` once hashing covers it. Add a **removal** regression test (previously provisioned with deny settings, block removed, assert redeploy is not skipped).
Note: `Options.DeploymentStacks` is a `*DeploymentStacksConfig` pointer (not a map/slice), so the empty-literal guard is a nil/empty-struct check, not `len()`.
## Scope / priority
Deployment stacks is an alpha feature (`AZD_ALPHA_ENABLE_DEPLOYMENT_STACKS`), so impact is limited to opt-in users. Non-blocking; both reviewers approved PR #9238.
Contributor guide
Research direction
Start in bicep_provider.go with resolveDeploymentStacksMap and useDeploymentStateShortcut, then trace how currentParamsHash is assembled. Add coverage for unchanged settings and for removing a previously configured deploymentStacks block, and run the relevant deployment-state tests. Done means unchanged options retain the shortcut, changes trigger redeployment, and removal clears stale settings.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, go
- Domain
- cli, cloud, devops
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100