fix: handle ARM expression strings in preflight resource fields - #7157
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes local ARM preflight parsing failures when Bicep emits conditional resource fields as ARM expression strings (e.g. "[if(...)]") by deferring typed parsing and preserving raw JSON.
Changes:
- Introduced a generic
armField[T]wrapper that stores raw JSON and provides typed access viaValue()when possible. - Updated
armTemplateResourceto usearmField[...]forTags,SKU,Plan,Identity,Copy, andZonesso expression-strings no longer break unmarshalling. - Added unit tests validating typed values, expression strings, absent/null handling, tags, and marshal/unmarshal round-tripping.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cli/azd/pkg/infra/provisioning/bicep/local_preflight.go | Adds armField[T] and migrates several armTemplateResource fields to tolerate ARM expression strings during snapshot parsing. |
| cli/azd/pkg/infra/provisioning/bicep/local_preflight_test.go | Adds focused unit tests covering armField[T] behavior across typed/object values, expression strings, and basic round-trip marshaling. |
You can also share your feedback on Copilot code review. Take the survey.
5af0d12 to
5af0d25
Compare
Bicep conditional expressions (e.g. `identity: cond ? {...} : {...}`) compile
to ARM expression strings like `"[if(equals(...))]"` instead of typed objects.
This caused json.Unmarshal to fail when parsing the bicep snapshot during local
preflight validation.
Introduce a generic `armField[T]` type that stores raw JSON bytes and defers
parsing. Callers use `Value()` for typed access (returns ok=false for
expressions) or `Raw()` for the underlying JSON. Applied to Tags, SKU, Plan,
Identity, Copy, and Zones fields in armTemplateResource. Uses `omitzero` tag
and `IsZero()` method to preserve correct marshal behavior.
Fixes #7154
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
5af0d25 to
6477793
Compare
weikanglim
left a comment
There was a problem hiding this comment.
Overall, this is perfectly fine for a tactical fix to address the immediate parsing failures.
It is possible that the proposed solution isn't completely exhaustive, I left two comments on potential places to look at.
One thing to perhaps consider: It does seem that the checks are on more experimental grounds with the reliance on snapshot, and failing provision outright doesn't seem quite right. I could see this failing if it were a dedicated command like validate.
…e#7157) Bicep conditional expressions (e.g. `identity: cond ? {...} : {...}`) compile to ARM expression strings like `"[if(equals(...))]"` instead of typed objects. This caused json.Unmarshal to fail when parsing the bicep snapshot during local preflight validation. Introduce a generic `armField[T]` type that stores raw JSON bytes and defers parsing. Callers use `Value()` for typed access (returns ok=false for expressions) or `Raw()` for the underlying JSON. Applied to Tags, SKU, Plan, Identity, Copy, and Zones fields in armTemplateResource. Uses `omitzero` tag and `IsZero()` method to preserve correct marshal behavior. Fixes Azure#7154 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e#7157) Bicep conditional expressions (e.g. `identity: cond ? {...} : {...}`) compile to ARM expression strings like `"[if(equals(...))]"` instead of typed objects. This caused json.Unmarshal to fail when parsing the bicep snapshot during local preflight validation. Introduce a generic `armField[T]` type that stores raw JSON bytes and defers parsing. Callers use `Value()` for typed access (returns ok=false for expressions) or `Raw()` for the underlying JSON. Applied to Tags, SKU, Plan, Identity, Copy, and Zones fields in armTemplateResource. Uses `omitzero` tag and `IsZero()` method to preserve correct marshal behavior. Fixes Azure#7154 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e#7157) Bicep conditional expressions (e.g. `identity: cond ? {...} : {...}`) compile to ARM expression strings like `"[if(equals(...))]"` instead of typed objects. This caused json.Unmarshal to fail when parsing the bicep snapshot during local preflight validation. Introduce a generic `armField[T]` type that stores raw JSON bytes and defers parsing. Callers use `Value()` for typed access (returns ok=false for expressions) or `Raw()` for the underlying JSON. Applied to Tags, SKU, Plan, Identity, Copy, and Zones fields in armTemplateResource. Uses `omitzero` tag and `IsZero()` method to preserve correct marshal behavior. Fixes Azure#7154 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e#7157) Bicep conditional expressions (e.g. `identity: cond ? {...} : {...}`) compile to ARM expression strings like `"[if(equals(...))]"` instead of typed objects. This caused json.Unmarshal to fail when parsing the bicep snapshot during local preflight validation. Introduce a generic `armField[T]` type that stores raw JSON bytes and defers parsing. Callers use `Value()` for typed access (returns ok=false for expressions) or `Raw()` for the underlying JSON. Applied to Tags, SKU, Plan, Identity, Copy, and Zones fields in armTemplateResource. Uses `omitzero` tag and `IsZero()` method to preserve correct marshal behavior. Fixes Azure#7154 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e#7157) Bicep conditional expressions (e.g. `identity: cond ? {...} : {...}`) compile to ARM expression strings like `"[if(equals(...))]"` instead of typed objects. This caused json.Unmarshal to fail when parsing the bicep snapshot during local preflight validation. Introduce a generic `armField[T]` type that stores raw JSON bytes and defers parsing. Callers use `Value()` for typed access (returns ok=false for expressions) or `Raw()` for the underlying JSON. Applied to Tags, SKU, Plan, Identity, Copy, and Zones fields in armTemplateResource. Uses `omitzero` tag and `IsZero()` method to preserve correct marshal behavior. Fixes Azure#7154 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Problem
When running
azd upon Bicep templates that use conditional expressions for resource fields (e.g.identity: condition ? {...} : {...}), local preflight validation fails with:Bicep compiles conditional expressions into ARM expression strings like
"[if(equals(...))]"instead of the expected typed JSON objects. The strongly-typed Go struct fields (*armTemplateIdentity,*armTemplateSKU, etc.) cannot unmarshal these strings.Solution
Introduce a generic
armField[T]type that stores raw JSON bytes and defers parsing:Value() (T, bool)— typed access; returnsfalseif the JSON is an ARM expression stringRaw() json.RawMessage— raw JSON bytes, always works regardless of shapeHasValue() bool— checks if the field was present and non-nullThis gives callers the best of both worlds: fast typed access when the value is a real object, and graceful fallback when it's an ARM expression.
Applied to these fields in
armTemplateResource:Tagsjson.RawMessagearmField[map[string]string]SKU*armTemplateSKUarmField[armTemplateSKU]Plan*armTemplatePlanarmField[armTemplatePlan]Identity*armTemplateIdentityarmField[armTemplateIdentity]Copy*armTemplateCopyarmField[armTemplateCopy]Zones[]stringarmField[[]string]Propertiesremainsjson.RawMessage(no single typed target),Conditionremainsany(already flexible).Testing
Fixes #7154