Skip to content

Commit 3c57915

Browse files
authored
Merge pull request #36394 from hashicorp/jbardin/refresh-orphan
orphaned resources must be refreshed before plan
2 parents 61acb0c + bfc6dc1 commit 3c57915

File tree

3 files changed

+66
-22
lines changed

3 files changed

+66
-22
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: BUG FIXES
2+
body: Refreshed state was not used in the plan for orphaned resource instances
3+
time: 2025-01-23T15:07:46.789595-05:00
4+
custom:
5+
Issue: "36394"

internal/terraform/context_plan2_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -6315,3 +6315,47 @@ resource "aws_instance" "foo" {
63156315
t.Errorf("unexpected error message\ngot: %s\nwant substring: %s", got, want)
63166316
}
63176317
}
6318+
6319+
func TestContext2Plan_orphanUpdateInstance(t *testing.T) {
6320+
// ean orphaned instance should still reflect the refreshed state in the plan
6321+
m := testModuleInline(t, map[string]string{
6322+
"main.tf": `
6323+
resource "test_object" "a" {
6324+
for_each = {}
6325+
}
6326+
`,
6327+
})
6328+
6329+
p := simpleMockProvider()
6330+
p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
6331+
state := req.PriorState.AsValueMap()
6332+
state["test_string"] = cty.StringVal("new")
6333+
resp.NewState = cty.ObjectVal(state)
6334+
return resp
6335+
}
6336+
6337+
state := states.BuildState(func(s *states.SyncState) {
6338+
s.SetResourceInstanceCurrent(mustResourceInstanceAddr(`test_object.a["old"]`), &states.ResourceInstanceObjectSrc{
6339+
AttrsJSON: []byte(`{"test_string":"old"}`),
6340+
Status: states.ObjectReady,
6341+
}, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`))
6342+
})
6343+
6344+
ctx := testContext2(t, &ContextOpts{
6345+
Providers: map[addrs.Provider]providers.Factory{
6346+
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
6347+
},
6348+
})
6349+
6350+
plan, diags := ctx.Plan(m, state, DefaultPlanOpts)
6351+
assertNoErrors(t, diags)
6352+
6353+
resourceType := p.GetProviderSchemaResponse.ResourceTypes["test_object"].Block.ImpliedType()
6354+
change, err := plan.Changes.ResourceInstance(mustResourceInstanceAddr(`test_object.a["old"]`)).Decode(resourceType)
6355+
if err != nil {
6356+
t.Fatal(err)
6357+
}
6358+
if change.Before.GetAttr("test_string").AsString() != "new" {
6359+
t.Fatalf("resource before value not refreshed in plan: %#v\n", change.Before)
6360+
}
6361+
}

internal/terraform/node_resource_plan_orphan.go

+17-22
Original file line numberDiff line numberDiff line change
@@ -122,22 +122,6 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon
122122
}
123123
}
124124

125-
shouldDefer := ctx.Deferrals().ShouldDeferResourceInstanceChanges(n.Addr, n.Dependencies)
126-
127-
var change *plans.ResourceInstanceChange
128-
var pDiags tfdiags.Diagnostics
129-
var deferred *providers.Deferred
130-
if forget {
131-
change, pDiags = n.planForget(ctx, oldState, "")
132-
diags = diags.Append(pDiags)
133-
} else {
134-
change, deferred, pDiags = n.planDestroy(ctx, oldState, "")
135-
diags = diags.Append(pDiags)
136-
}
137-
if diags.HasErrors() {
138-
return diags
139-
}
140-
141125
if !n.skipRefresh && !forget {
142126
// Refresh this instance even though it is going to be destroyed, in
143127
// order to catch missing resources. If this is a normal plan,
@@ -153,12 +137,7 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon
153137

154138
oldState = refreshedState
155139

156-
if deferred == nil {
157-
// set the overall deferred status if it wasn't already set.
158-
deferred = refreshDeferred
159-
}
160-
161-
if deferred == nil && !shouldDefer {
140+
if refreshDeferred == nil {
162141
// only update the state if we're not deferring the change
163142
diags = diags.Append(n.writeResourceInstanceState(ctx, refreshedState, refreshState))
164143
if diags.HasErrors() {
@@ -167,6 +146,22 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon
167146
}
168147
}
169148

149+
shouldDefer := ctx.Deferrals().ShouldDeferResourceInstanceChanges(n.Addr, n.Dependencies)
150+
151+
var change *plans.ResourceInstanceChange
152+
var pDiags tfdiags.Diagnostics
153+
var deferred *providers.Deferred
154+
if forget {
155+
change, pDiags = n.planForget(ctx, oldState, "")
156+
diags = diags.Append(pDiags)
157+
} else {
158+
change, deferred, pDiags = n.planDestroy(ctx, oldState, "")
159+
diags = diags.Append(pDiags)
160+
}
161+
if diags.HasErrors() {
162+
return diags
163+
}
164+
170165
// We might be able to offer an approximate reason for why we are
171166
// planning to delete this object. (This is best-effort; we might
172167
// sometimes not have a reason.)

0 commit comments

Comments
 (0)