Azure / Azure/azure-dev

deploymentStacks: fold resolved stack options into deployment-state hash

Open
#9,356 0 comments 0 reactions 0 assignees View on GitHub
area/bicep bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.