Skip to content

fix(internal/provider): handle unknown values in tf_vars and provisioner_tags#341

Draft
stirby wants to merge 3 commits into
mainfrom
fix/template-unknown-set-values
Draft

fix(internal/provider): handle unknown values in tf_vars and provisioner_tags#341
stirby wants to merge 3 commits into
mainfrom
fix/template-unknown-set-values

Conversation

@stirby
Copy link
Copy Markdown

@stirby stirby commented May 1, 2026

Problem

When tf_vars or provisioner_tags receive dynamic values through modules or variable interpolation, the provider fails during plan with:

Error: Value Conversion Error
Received unknown value, however the target type cannot handle unknown values.
Path: [0].tf_vars
Target Type: []provider.Variable
Suggested Type: basetypes.SetValue

The root cause is that TemplateVersion.TerraformVariables and TemplateVersion.ProvisionerTags use Go native slices ([]Variable), which cannot represent unknown Terraform values during plan. The schema already correctly defines these as SetNestedAttribute, but the struct type did not match.

Fix

Change both fields from []Variable to types.Set, aligning the struct with the schema. Add a varsFromSet helper to centralize Set-to-slice conversion with proper null/unknown handling at all four call sites (newVersion, setPrivateState, tfVariablesChanged, reconcileVersionIDs).

No schema changes, no breaking changes to HCL syntax or state format.

Approach evaluation

Five approaches were evaluated:

# Approach Breaking? Complexity Decision
1 types.Set direct (no helpers) No Medium Good but repetitive
2 types.List + schema change Yes Medium Rejected: changes Set to List semantics
3 Custom type wrapper No High (~200 LOC) Over-engineered
4 types.Map Yes Medium Rejected: breaks HCL syntax
5 types.Set + helper functions No Low Selected
Testing
  • All 18 existing unit tests pass (including 8 TestReconcileVersionIDs subtests)
  • New TestUnknownTerraformVariablesHandling validates unknown, null, populated, and change-detection scenarios
  • Acceptance tests pass (TestAccTemplateResourceAGPL, TestAccTemplateResourceBackCompat)
  • Side-by-side terraform plan with the exact config from the issue: old binary reproduces the error, fixed binary passes

Generated by Coder with Claude

Closes #305

stirby added 2 commits May 1, 2026 20:49
…ner_tags

Change TerraformVariables and ProvisionerTags fields in TemplateVersion
from Go native slices ([]Variable) to types.Set, matching the existing
SetNestedAttribute schema definition. Go slices cannot represent unknown
Terraform values during plan, causing Value Conversion Error when these
fields receive dynamic values through modules or variable interpolation.

Add varsFromSet helper to centralize Set-to-slice conversion with proper
null/unknown handling at all four call sites.

Closes #305
@ethanndickson ethanndickson self-requested a review May 7, 2026 03:01
// It computes directory hashes for each version and validates version constraints.
// Unlike the previous attribute-level plan modifier, this method only writes
// directory_hash values via SetAttribute, avoiding reconstruction of the entire
// versions list which would strip cty-level sensitivity marks from tf_vars.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This fix needs a regression test

},
},
PlanModifiers: []planmodifier.List{
NewVersionsPlanModifier(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we can't use the attribute-level plan modifier anymore, why are we keeping the code for it?

Comment thread internal/provider/template_resource.go Outdated
// relying on ID.IsUnknown() set by the plan modifier, which required
// reconstructing the entire versions list and stripped cty sensitivity marks.
var lv LastVersionsByHash
lvBytes, pvDiag := req.Private.GetKey(ctx, LastVersionsKey)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks like we're doing a lot of the same work here in Update, as we are in PlanModifyList? Why doesn't update just use the planned value?

@ethanndickson
Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jatcod3r jatcod3r force-pushed the fix/template-unknown-set-values branch 4 times, most recently from 9b59886 to edd5827 Compare May 7, 2026 20:23
When coderd_template has sensitive tf_vars and a deferred dependency
(like time_static) that gets replaced during apply, Terraform re-plans
the resource. The previous plan modifier used types.ListValueFrom() to
write the entire versions list back, which reconstructed tftypes values
and stripped Terraform core's cty-level sensitivity marks. This caused:
  'Provider produced inconsistent final plan: inconsistent values for
   sensitive attribute'

Changes:
- Replace attribute-level PlanModifyList with resource-level ModifyPlan
  that only writes directory_hash via SetAttribute (proven safe for
  sensitivity preservation)
- Move version reconciliation logic (new vs reuse) from plan modifier
  into Update, re-deriving from private state instead of ID.IsUnknown()
- Keep reconcileVersionIDs() for unit tests but no longer call from
  plan modifier

Fixes #305
@jatcod3r jatcod3r force-pushed the fix/template-unknown-set-values branch from edd5827 to ac4d097 Compare May 7, 2026 21:55
@jatcod3r
Copy link
Copy Markdown

jatcod3r commented May 8, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac4d097082

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// carries the correct cty value with any sensitivity marks from the
// config. reconcileVersionIDs only modifies name when config is null,
// so there's nothing new to write for user-set names.
if configVersions[i].Name.IsNull() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve reconciled version name when config name is unknown

When versions[i].name is set from an expression that is unknown during planning, reconcileVersionIDs() can still resolve a stable existing ID (and internally backfill the prior name), but ModifyPlan only writes name back when configVersions[i].Name.IsNull(). This leaves a plan with known id + unknown name, and Update() later calls UpdateTemplateVersion with newState.Versions[idx].Name.ValueString() (empty string for unknown), which can trigger unintended rename attempts or API errors for dynamic-name configurations. Previously, the list-level plan modifier rewrote the full versions list, so reconciled names were preserved.

Useful? React with 👍 / 👎.

@jatcod3r jatcod3r force-pushed the fix/template-unknown-set-values branch from 78b8465 to ac4d097 Compare May 8, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

coderd_template "Attributes Set" input types issue

4 participants