diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index 1026e0f0b..602c227f1 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -6539,3 +6539,63 @@ resource "test_instance" "a" { } } } + +func TestContext2Plan_validateIgnoreAll(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +resource "test_instance" "a" { + lifecycle { + ignore_changes = all + } +} +`, + }) + + p := testProvider("test") + p.GetSchemaReturn = &ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "data": {Type: cty.String, Optional: true}, + }, + }, + }, + } + p.PlanResourceChangeFn = testDiffFn + p.ValidateResourceTypeConfigFn = func(req providers.ValidateResourceTypeConfigRequest) providers.ValidateResourceTypeConfigResponse { + var diags tfdiags.Diagnostics + if req.TypeName == "test_instance" { + if !req.Config.GetAttr("id").IsNull() { + diags = diags.Append(errors.New("id cannot be set in config")) + } + } + return providers.ValidateResourceTypeConfigResponse{ + Diagnostics: diags, + } + } + + state := states.NewState() + root := state.EnsureModule(addrs.RootModuleInstance) + root.SetResourceInstanceCurrent( + mustResourceInstanceAddr("test_instance.a").Resource, + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectReady, + AttrsJSON: []byte(`{"id":"a","data":"foo"}`), + Dependencies: []addrs.ConfigResource{}, + }, + mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), + ) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + State: state, + }) + _, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.Err()) + } +} diff --git a/terraform/eval_diff.go b/terraform/eval_diff.go index 8ccb3b586..bf2c86b31 100644 --- a/terraform/eval_diff.go +++ b/terraform/eval_diff.go @@ -212,6 +212,24 @@ func (n *EvalDiff) Eval(ctx EvalContext) tfdiags.Diagnostics { unmarkedConfigVal, unmarkedPaths := origConfigVal.UnmarkDeepWithPaths() unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths() + log.Printf("[TRACE] Re-validating config for %q", n.Addr.Absolute(ctx.Path())) + // Allow the provider to validate the final set of values. + // The config was statically validated early on, but there may have been + // unknown values which the provider could not validate at the time. + // TODO: It would be more correct to validate the config after + // ignore_changes has been applied, but the current implementation cannot + // exclude computed-only attributes when given the `all` option. + validateResp := provider.ValidateResourceTypeConfig( + providers.ValidateResourceTypeConfigRequest{ + TypeName: n.Addr.Resource.Type, + Config: unmarkedConfigVal, + }, + ) + if validateResp.Diagnostics.HasErrors() { + diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config)) + return diags + } + // ignore_changes is meant to only apply to the configuration, so it must // be applied before we generate a plan. This ensures the config used for // the proposed value, the proposed value itself, and the config presented @@ -235,21 +253,6 @@ func (n *EvalDiff) Eval(ctx EvalContext) tfdiags.Diagnostics { } } - log.Printf("[TRACE] Re-validating config for %q", n.Addr.Absolute(ctx.Path())) - // Allow the provider to validate the final set of values. - // The config was statically validated early on, but there may have been - // unknown values which the provider could not validate at the time. - validateResp := provider.ValidateResourceTypeConfig( - providers.ValidateResourceTypeConfigRequest{ - TypeName: n.Addr.Resource.Type, - Config: configValIgnored, - }, - ) - if validateResp.Diagnostics.HasErrors() { - diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config)) - return diags - } - resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{ TypeName: n.Addr.Resource.Type, Config: configValIgnored,