diff --git a/internal/terraform/context_apply2_test.go b/internal/terraform/context_apply2_test.go index 82a48e2d7e..c405f30aaf 100644 --- a/internal/terraform/context_apply2_test.go +++ b/internal/terraform/context_apply2_test.go @@ -549,3 +549,50 @@ resource "test_object" "y" { } } } + +func TestContext2Apply_destroyWithDeposed(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_object" "x" { + test_string = "ok" + lifecycle { + create_before_destroy = true + } +}`, + }) + + p := simpleMockProvider() + + deposedKey := states.NewDeposedKey() + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceDeposed( + mustResourceInstanceAddr("test_object.x").Resource, + deposedKey, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectTainted, + AttrsJSON: []byte(`{"test_string":"deposed"}`), + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + plan, diags := ctx.Plan(m, state, &PlanOpts{ + Mode: plans.DestroyMode, + }) + if diags.HasErrors() { + t.Fatalf("plan: %s", diags.Err()) + } + + _, diags = ctx.Apply(plan, m) + if diags.HasErrors() { + t.Fatalf("apply: %s", diags.Err()) + } + +} diff --git a/internal/terraform/node_resource_destroy_deposed.go b/internal/terraform/node_resource_destroy_deposed.go index ceb8739d59..7d639d135c 100644 --- a/internal/terraform/node_resource_destroy_deposed.go +++ b/internal/terraform/node_resource_destroy_deposed.go @@ -95,7 +95,12 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walk return diags } - if !n.skipRefresh { + // We don't refresh during the planDestroy walk, since that is only adding + // the destroy changes to the plan and the provider will not be configured + // at this point. The other nodes use separate types for plan and destroy, + // while deposed instances are always a destroy operation, so the logic + // here is a bit overloaded. + if !n.skipRefresh && op != walkPlanDestroy { // Refresh this object even though it is going to be destroyed, in // case it's already been deleted outside of Terraform. If this is a // normal plan, providers expect a Read request to remove missing