From 0b74ecb0ea8932a2f79e5d91f951f4f086e24de9 Mon Sep 17 00:00:00 2001 From: ju-pe Date: Tue, 12 May 2026 22:25:53 +0000 Subject: [PATCH 1/4] fix: update PlanModifyList to only touch id, name, and directory_hash --- internal/provider/template_resource.go | 39 ++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/internal/provider/template_resource.go b/internal/provider/template_resource.go index f053773b..d1e77cb7 100644 --- a/internal/provider/template_resource.go +++ b/internal/provider/template_resource.go @@ -1049,10 +1049,39 @@ func (d *versionsPlanModifier) PlanModifyList(ctx context.Context, req planmodif return } - resp.PlanValue, diag = types.ListValueFrom(ctx, req.PlanValue.ElementType(ctx), planVersions) - if diag.HasError() { - resp.Diagnostics.Append(diag...) - } + // Now patch the original plan elements with only the fields we changed + planElements := req.PlanValue.Elements() + newElements := make([]attr.Value, len(planElements)) + + for i, elem := range planElements { + obj, ok := elem.(types.Object) + if !ok { + resp.Diagnostics.AddError("Client Error", "Expected object element in versions list") + return + } + + attrs := obj.Attributes() + attrTypes := obj.AttributeTypes(ctx) + + // Overwrite only the fields the plan modifier manages + attrs["id"] = planVersions[i].ID + attrs["name"] = planVersions[i].Name + attrs["directory_hash"] = planVersions[i].DirectoryHash + + // tf_vars, provisioner_tags, directory, active, message — all untouched + + newObj, objDiag := types.ObjectValue(attrTypes, attrs) + if objDiag.HasError() { + resp.Diagnostics.Append(objDiag...) + return + } + newElements[i] = newObj + } + + resp.PlanValue, diag = types.ListValue(req.PlanValue.ElementType(ctx), newElements) + if diag.HasError() { + resp.Diagnostics.Append(diag...) + } } func hasOneActiveVersion(data Versions) (hasActiveVersion bool, diags diag.Diagnostics) { @@ -1517,7 +1546,7 @@ func (planVersions Versions) reconcileVersionIDs(lv LastVersionsByHash, configVe prevList := lv[planVersions[i].DirectoryHash.ValueString()] if len(prevList) > 0 && planVersions[i].ID.IsUnknown() { planVersions[i].ID = UUIDValue(prevList[0].ID) - if planVersions[i].Name.IsUnknown() { + if planVersions[i].Name.IsNull() { planVersions[i].Name = types.StringValue(prevList[0].Name) } lv[planVersions[i].DirectoryHash.ValueString()] = prevList[1:] From 2275b29201a4e9c2ab4a040a80fc2eda700cdc02 Mon Sep 17 00:00:00 2001 From: ju-pe Date: Tue, 12 May 2026 22:46:22 +0000 Subject: [PATCH 2/4] style: fix indentation in PlanModifyList (spaces to tabs) --- internal/provider/template_resource.go | 66 +++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/internal/provider/template_resource.go b/internal/provider/template_resource.go index d1e77cb7..99a3a4ef 100644 --- a/internal/provider/template_resource.go +++ b/internal/provider/template_resource.go @@ -1049,39 +1049,39 @@ func (d *versionsPlanModifier) PlanModifyList(ctx context.Context, req planmodif return } - // Now patch the original plan elements with only the fields we changed - planElements := req.PlanValue.Elements() - newElements := make([]attr.Value, len(planElements)) - - for i, elem := range planElements { - obj, ok := elem.(types.Object) - if !ok { - resp.Diagnostics.AddError("Client Error", "Expected object element in versions list") - return - } - - attrs := obj.Attributes() - attrTypes := obj.AttributeTypes(ctx) - - // Overwrite only the fields the plan modifier manages - attrs["id"] = planVersions[i].ID - attrs["name"] = planVersions[i].Name - attrs["directory_hash"] = planVersions[i].DirectoryHash - - // tf_vars, provisioner_tags, directory, active, message — all untouched - - newObj, objDiag := types.ObjectValue(attrTypes, attrs) - if objDiag.HasError() { - resp.Diagnostics.Append(objDiag...) - return - } - newElements[i] = newObj - } - - resp.PlanValue, diag = types.ListValue(req.PlanValue.ElementType(ctx), newElements) - if diag.HasError() { - resp.Diagnostics.Append(diag...) - } + // Now patch the original plan elements with only the fields we changed + planElements := req.PlanValue.Elements() + newElements := make([]attr.Value, len(planElements)) + + for i, elem := range planElements { + obj, ok := elem.(types.Object) + if !ok { + resp.Diagnostics.AddError("Client Error", "Expected object element in versions list") + return + } + + attrs := obj.Attributes() + attrTypes := obj.AttributeTypes(ctx) + + // Overwrite only the fields the plan modifier manages + attrs["id"] = planVersions[i].ID + attrs["name"] = planVersions[i].Name + attrs["directory_hash"] = planVersions[i].DirectoryHash + + // tf_vars, provisioner_tags, directory, active, message — all untouched + + newObj, objDiag := types.ObjectValue(attrTypes, attrs) + if objDiag.HasError() { + resp.Diagnostics.Append(objDiag...) + return + } + newElements[i] = newObj + } + + resp.PlanValue, diag = types.ListValue(req.PlanValue.ElementType(ctx), newElements) + if diag.HasError() { + resp.Diagnostics.Append(diag...) + } } func hasOneActiveVersion(data Versions) (hasActiveVersion bool, diags diag.Diagnostics) { From 95a503298fb1c1f5a910645f7606d75b863c1500 Mon Sep 17 00:00:00 2001 From: ju-pe Date: Tue, 12 May 2026 23:52:28 +0000 Subject: [PATCH 3/4] fix: replace planVersions with configVersions --- internal/provider/template_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/template_resource.go b/internal/provider/template_resource.go index 99a3a4ef..3cab62fb 100644 --- a/internal/provider/template_resource.go +++ b/internal/provider/template_resource.go @@ -1546,7 +1546,7 @@ func (planVersions Versions) reconcileVersionIDs(lv LastVersionsByHash, configVe prevList := lv[planVersions[i].DirectoryHash.ValueString()] if len(prevList) > 0 && planVersions[i].ID.IsUnknown() { planVersions[i].ID = UUIDValue(prevList[0].ID) - if planVersions[i].Name.IsNull() { + if configVersions[i].Name.IsNull() { planVersions[i].Name = types.StringValue(prevList[0].Name) } lv[planVersions[i].DirectoryHash.ValueString()] = prevList[1:] From 7045b299a2dc1fd119ad35117e58d40230d59c16 Mon Sep 17 00:00:00 2001 From: ju-pe Date: Wed, 13 May 2026 00:10:34 +0000 Subject: [PATCH 4/4] fix: update 'UnknownUsesStateInOrder' test to capture when config name is null or non-null --- internal/provider/template_resource_test.go | 60 ++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/internal/provider/template_resource_test.go b/internal/provider/template_resource_test.go index 971de6e6..f1ba360d 100644 --- a/internal/provider/template_resource_test.go +++ b/internal/provider/template_resource_test.go @@ -1388,6 +1388,8 @@ func TestReconcileVersionIDs(t *testing.T) { }, }, { + // Config name is null (auto-generated), plan name is unknown. + // Should backfill name from state since the user didn't set one. Name: "UnknownUsesStateInOrder", planVersions: []TemplateVersion{ { @@ -1408,7 +1410,7 @@ func TestReconcileVersionIDs(t *testing.T) { Name: types.StringValue("foo"), }, { - Name: types.StringValue("bar"), + Name: types.StringNull(), }, }, inputState: map[string][]PreviousTemplateVersion{ @@ -1440,6 +1442,62 @@ func TestReconcileVersionIDs(t *testing.T) { }, }, }, + { + // Config name is non-null (e.g. random_uuid.result), plan name is unknown + // because the upstream resource is being recreated. + // Should NOT backfill name — leave it unknown to resolve after apply. + Name: "UnknownNonNullConfigNameNotBackfilled", + planVersions: []TemplateVersion{ + { + Name: types.StringValue("foo"), + DirectoryHash: types.StringValue("aaa"), + ID: NewUUIDUnknown(), + TerraformVariables: []Variable{}, + }, + { + Name: types.StringUnknown(), + DirectoryHash: types.StringValue("aaa"), + ID: NewUUIDUnknown(), + TerraformVariables: []Variable{}, + }, + }, + configVersions: []TemplateVersion{ + { + Name: types.StringValue("foo"), + }, + { + Name: types.StringValue("bar"), + }, + }, + inputState: map[string][]PreviousTemplateVersion{ + "aaa": { + { + ID: aUUID, + Name: "qux", + TFVars: map[string]string{}, + }, + { + ID: bUUID, + Name: "baz", + TFVars: map[string]string{}, + }, + }, + }, + expectedVersions: []TemplateVersion{ + { + Name: types.StringValue("foo"), + DirectoryHash: types.StringValue("aaa"), + ID: UUIDValue(aUUID), + TerraformVariables: []Variable{}, + }, + { + Name: types.StringUnknown(), + DirectoryHash: types.StringValue("aaa"), + ID: UUIDValue(bUUID), + TerraformVariables: []Variable{}, + }, + }, + }, { Name: "NewVersionNewRandomName", planVersions: []TemplateVersion{