github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/terraform/node_resource_abstract_instance.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/hcl/v2"
     9  	"github.com/cycloidio/terraform/addrs"
    10  	"github.com/cycloidio/terraform/configs"
    11  	"github.com/cycloidio/terraform/configs/configschema"
    12  	"github.com/cycloidio/terraform/plans"
    13  	"github.com/cycloidio/terraform/plans/objchange"
    14  	"github.com/cycloidio/terraform/providers"
    15  	"github.com/cycloidio/terraform/provisioners"
    16  	"github.com/cycloidio/terraform/states"
    17  	"github.com/cycloidio/terraform/tfdiags"
    18  	"github.com/zclconf/go-cty/cty"
    19  )
    20  
    21  // NodeAbstractResourceInstance represents a resource instance with no
    22  // associated operations. It embeds NodeAbstractResource but additionally
    23  // contains an instance key, used to identify one of potentially many
    24  // instances that were created from a resource in configuration, e.g. using
    25  // the "count" or "for_each" arguments.
    26  type NodeAbstractResourceInstance struct {
    27  	NodeAbstractResource
    28  	Addr addrs.AbsResourceInstance
    29  
    30  	// These are set via the AttachState method.
    31  	instanceState *states.ResourceInstance
    32  	// storedProviderConfig is the provider address retrieved from the
    33  	// state, but since it is only stored in the whole Resource rather than the
    34  	// ResourceInstance, we extract it out here.
    35  	storedProviderConfig addrs.AbsProviderConfig
    36  
    37  	Dependencies []addrs.ConfigResource
    38  }
    39  
    40  // NewNodeAbstractResourceInstance creates an abstract resource instance graph
    41  // node for the given absolute resource instance address.
    42  func NewNodeAbstractResourceInstance(addr addrs.AbsResourceInstance) *NodeAbstractResourceInstance {
    43  	// Due to the fact that we embed NodeAbstractResource, the given address
    44  	// actually ends up split between the resource address in the embedded
    45  	// object and the InstanceKey field in our own struct. The
    46  	// ResourceInstanceAddr method will stick these back together again on
    47  	// request.
    48  	r := NewNodeAbstractResource(addr.ContainingResource().Config())
    49  	return &NodeAbstractResourceInstance{
    50  		NodeAbstractResource: *r,
    51  		Addr:                 addr,
    52  	}
    53  }
    54  
    55  func (n *NodeAbstractResourceInstance) Name() string {
    56  	return n.ResourceInstanceAddr().String()
    57  }
    58  
    59  func (n *NodeAbstractResourceInstance) Path() addrs.ModuleInstance {
    60  	return n.Addr.Module
    61  }
    62  
    63  // GraphNodeReferenceable
    64  func (n *NodeAbstractResourceInstance) ReferenceableAddrs() []addrs.Referenceable {
    65  	addr := n.ResourceInstanceAddr()
    66  	return []addrs.Referenceable{
    67  		addr.Resource,
    68  
    69  		// A resource instance can also be referenced by the address of its
    70  		// containing resource, so that e.g. a reference to aws_instance.foo
    71  		// would match both aws_instance.foo[0] and aws_instance.foo[1].
    72  		addr.ContainingResource().Resource,
    73  	}
    74  }
    75  
    76  // GraphNodeReferencer
    77  func (n *NodeAbstractResourceInstance) References() []*addrs.Reference {
    78  	// If we have a configuration attached then we'll delegate to our
    79  	// embedded abstract resource, which knows how to extract dependencies
    80  	// from configuration. If there is no config, then the dependencies will
    81  	// be connected during destroy from those stored in the state.
    82  	if n.Config != nil {
    83  		if n.Schema == nil {
    84  			// We'll produce a log message about this out here so that
    85  			// we can include the full instance address, since the equivalent
    86  			// message in NodeAbstractResource.References cannot see it.
    87  			log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name())
    88  			return nil
    89  		}
    90  		return n.NodeAbstractResource.References()
    91  	}
    92  
    93  	// If we have neither config nor state then we have no references.
    94  	return nil
    95  }
    96  
    97  // StateDependencies returns the dependencies saved in the state.
    98  func (n *NodeAbstractResourceInstance) StateDependencies() []addrs.ConfigResource {
    99  	if s := n.instanceState; s != nil {
   100  		if s.Current != nil {
   101  			return s.Current.Dependencies
   102  		}
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  // GraphNodeProviderConsumer
   109  func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.ProviderConfig, bool) {
   110  	// If we have a config we prefer that above all else
   111  	if n.Config != nil {
   112  		relAddr := n.Config.ProviderConfigAddr()
   113  		return addrs.LocalProviderConfig{
   114  			LocalName: relAddr.LocalName,
   115  			Alias:     relAddr.Alias,
   116  		}, false
   117  	}
   118  
   119  	// See if we have a valid provider config from the state.
   120  	if n.storedProviderConfig.Provider.Type != "" {
   121  		// An address from the state must match exactly, since we must ensure
   122  		// we refresh/destroy a resource with the same provider configuration
   123  		// that created it.
   124  		return n.storedProviderConfig, true
   125  	}
   126  
   127  	// No provider configuration found; return a default address
   128  	return addrs.AbsProviderConfig{
   129  		Provider: n.Provider(),
   130  		Module:   n.ModulePath(),
   131  	}, false
   132  }
   133  
   134  // GraphNodeProviderConsumer
   135  func (n *NodeAbstractResourceInstance) Provider() addrs.Provider {
   136  	if n.Config != nil {
   137  		return n.Config.Provider
   138  	}
   139  	if n.storedProviderConfig.Provider.Type != "" {
   140  		return n.storedProviderConfig.Provider
   141  	}
   142  	return addrs.ImpliedProviderForUnqualifiedType(n.Addr.Resource.ContainingResource().ImpliedProvider())
   143  }
   144  
   145  // GraphNodeResourceInstance
   146  func (n *NodeAbstractResourceInstance) ResourceInstanceAddr() addrs.AbsResourceInstance {
   147  	return n.Addr
   148  }
   149  
   150  // GraphNodeAttachResourceState
   151  func (n *NodeAbstractResourceInstance) AttachResourceState(s *states.Resource) {
   152  	if s == nil {
   153  		log.Printf("[WARN] attaching nil state to %s", n.Addr)
   154  		return
   155  	}
   156  	log.Printf("[TRACE] NodeAbstractResourceInstance.AttachResourceState for %s", n.Addr)
   157  	n.instanceState = s.Instance(n.Addr.Resource.Key)
   158  	n.storedProviderConfig = s.ProviderConfig
   159  }
   160  
   161  // readDiff returns the planned change for a particular resource instance
   162  // object.
   163  func (n *NodeAbstractResourceInstance) readDiff(ctx EvalContext, providerSchema *ProviderSchema) (*plans.ResourceInstanceChange, error) {
   164  	changes := ctx.Changes()
   165  	addr := n.ResourceInstanceAddr()
   166  
   167  	schema, _ := providerSchema.SchemaForResourceAddr(addr.Resource.Resource)
   168  	if schema == nil {
   169  		// Should be caught during validation, so we don't bother with a pretty error here
   170  		return nil, fmt.Errorf("provider does not support resource type %q", addr.Resource.Resource.Type)
   171  	}
   172  
   173  	gen := states.CurrentGen
   174  	csrc := changes.GetResourceInstanceChange(addr, gen)
   175  	if csrc == nil {
   176  		log.Printf("[TRACE] readDiff: No planned change recorded for %s", n.Addr)
   177  		return nil, nil
   178  	}
   179  
   180  	change, err := csrc.Decode(schema.ImpliedType())
   181  	if err != nil {
   182  		return nil, fmt.Errorf("failed to decode planned changes for %s: %s", n.Addr, err)
   183  	}
   184  
   185  	log.Printf("[TRACE] readDiff: Read %s change from plan for %s", change.Action, n.Addr)
   186  
   187  	return change, nil
   188  }
   189  
   190  func (n *NodeAbstractResourceInstance) checkPreventDestroy(change *plans.ResourceInstanceChange) error {
   191  	if change == nil || n.Config == nil || n.Config.Managed == nil {
   192  		return nil
   193  	}
   194  
   195  	preventDestroy := n.Config.Managed.PreventDestroy
   196  
   197  	if (change.Action == plans.Delete || change.Action.IsReplace()) && preventDestroy {
   198  		var diags tfdiags.Diagnostics
   199  		diags = diags.Append(&hcl.Diagnostic{
   200  			Severity: hcl.DiagError,
   201  			Summary:  "Instance cannot be destroyed",
   202  			Detail: fmt.Sprintf(
   203  				"Resource %s has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.",
   204  				n.Addr.String(),
   205  			),
   206  			Subject: &n.Config.DeclRange,
   207  		})
   208  		return diags.Err()
   209  	}
   210  
   211  	return nil
   212  }
   213  
   214  // preApplyHook calls the pre-Apply hook
   215  func (n *NodeAbstractResourceInstance) preApplyHook(ctx EvalContext, change *plans.ResourceInstanceChange) tfdiags.Diagnostics {
   216  	var diags tfdiags.Diagnostics
   217  
   218  	if change == nil {
   219  		panic(fmt.Sprintf("preApplyHook for %s called with nil Change", n.Addr))
   220  	}
   221  
   222  	// Only managed resources have user-visible apply actions.
   223  	if n.Addr.Resource.Resource.Mode == addrs.ManagedResourceMode {
   224  		priorState := change.Before
   225  		plannedNewState := change.After
   226  
   227  		diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
   228  			return h.PreApply(n.Addr, change.DeposedKey.Generation(), change.Action, priorState, plannedNewState)
   229  		}))
   230  		if diags.HasErrors() {
   231  			return diags
   232  		}
   233  	}
   234  
   235  	return nil
   236  }
   237  
   238  // postApplyHook calls the post-Apply hook
   239  func (n *NodeAbstractResourceInstance) postApplyHook(ctx EvalContext, state *states.ResourceInstanceObject, err error) tfdiags.Diagnostics {
   240  	var diags tfdiags.Diagnostics
   241  
   242  	// Only managed resources have user-visible apply actions.
   243  	if n.Addr.Resource.Resource.Mode == addrs.ManagedResourceMode {
   244  		var newState cty.Value
   245  		if state != nil {
   246  			newState = state.Value
   247  		} else {
   248  			newState = cty.NullVal(cty.DynamicPseudoType)
   249  		}
   250  		diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
   251  			return h.PostApply(n.Addr, nil, newState, err)
   252  		}))
   253  	}
   254  
   255  	return diags
   256  }
   257  
   258  type phaseState int
   259  
   260  const (
   261  	workingState phaseState = iota
   262  	refreshState
   263  	prevRunState
   264  )
   265  
   266  //go:generate go run golang.org/x/tools/cmd/stringer -type phaseState
   267  
   268  // writeResourceInstanceState saves the given object as the current object for
   269  // the selected resource instance.
   270  //
   271  // dependencies is a parameter, instead of those directly attacted to the
   272  // NodeAbstractResourceInstance, because we don't write dependencies for
   273  // datasources.
   274  //
   275  // targetState determines which context state we're writing to during plan. The
   276  // default is the global working state.
   277  func (n *NodeAbstractResourceInstance) writeResourceInstanceState(ctx EvalContext, obj *states.ResourceInstanceObject, targetState phaseState) error {
   278  	return n.writeResourceInstanceStateImpl(ctx, states.NotDeposed, obj, targetState)
   279  }
   280  
   281  func (n *NodeAbstractResourceInstance) writeResourceInstanceStateDeposed(ctx EvalContext, deposedKey states.DeposedKey, obj *states.ResourceInstanceObject, targetState phaseState) error {
   282  	if deposedKey == states.NotDeposed {
   283  		// Bail out to avoid silently doing something other than what the
   284  		// caller seems to have intended.
   285  		panic("trying to write current state object using writeResourceInstanceStateDeposed")
   286  	}
   287  	return n.writeResourceInstanceStateImpl(ctx, deposedKey, obj, targetState)
   288  }
   289  
   290  // (this is the private common body of both writeResourceInstanceState and
   291  // writeResourceInstanceStateDeposed. Don't call it directly; instead, use
   292  // one of the two wrappers to be explicit about which of the instance's
   293  // objects you are intending to write.
   294  func (n *NodeAbstractResourceInstance) writeResourceInstanceStateImpl(ctx EvalContext, deposedKey states.DeposedKey, obj *states.ResourceInstanceObject, targetState phaseState) error {
   295  	absAddr := n.Addr
   296  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   297  	if err != nil {
   298  		return err
   299  	}
   300  	logFuncName := "NodeAbstractResouceInstance.writeResourceInstanceState"
   301  	if deposedKey == states.NotDeposed {
   302  		log.Printf("[TRACE] %s to %s for %s", logFuncName, targetState, absAddr)
   303  	} else {
   304  		logFuncName = "NodeAbstractResouceInstance.writeResourceInstanceStateDeposed"
   305  		log.Printf("[TRACE] %s to %s for %s (deposed key %s)", logFuncName, targetState, absAddr, deposedKey)
   306  	}
   307  
   308  	var state *states.SyncState
   309  	switch targetState {
   310  	case workingState:
   311  		state = ctx.State()
   312  	case refreshState:
   313  		state = ctx.RefreshState()
   314  	case prevRunState:
   315  		state = ctx.PrevRunState()
   316  	default:
   317  		panic(fmt.Sprintf("unsupported phaseState value %#v", targetState))
   318  	}
   319  	if state == nil {
   320  		// Should not happen, because we shouldn't ever try to write to
   321  		// a state that isn't applicable to the current operation.
   322  		// (We can also get in here for unit tests which are using
   323  		// EvalContextMock but not populating PrevRunStateState with
   324  		// a suitable state object.)
   325  		return fmt.Errorf("state of type %s is not applicable to the current operation; this is a bug in Terraform", targetState)
   326  	}
   327  
   328  	// In spite of the name, this function also handles the non-deposed case
   329  	// via the writeResourceInstanceState wrapper, by setting deposedKey to
   330  	// the NotDeposed value (the zero value of DeposedKey).
   331  	var write func(src *states.ResourceInstanceObjectSrc)
   332  	if deposedKey == states.NotDeposed {
   333  		write = func(src *states.ResourceInstanceObjectSrc) {
   334  			state.SetResourceInstanceCurrent(absAddr, src, n.ResolvedProvider)
   335  		}
   336  	} else {
   337  		write = func(src *states.ResourceInstanceObjectSrc) {
   338  			state.SetResourceInstanceDeposed(absAddr, deposedKey, src, n.ResolvedProvider)
   339  		}
   340  	}
   341  
   342  	if obj == nil || obj.Value.IsNull() {
   343  		// No need to encode anything: we'll just write it directly.
   344  		write(nil)
   345  		log.Printf("[TRACE] %s: removing state object for %s", logFuncName, absAddr)
   346  		return nil
   347  	}
   348  
   349  	if providerSchema == nil {
   350  		// Should never happen, unless our state object is nil
   351  		panic("writeResourceInstanceStateImpl used with nil ProviderSchema")
   352  	}
   353  
   354  	if obj != nil {
   355  		log.Printf("[TRACE] %s: writing state object for %s", logFuncName, absAddr)
   356  	} else {
   357  		log.Printf("[TRACE] %s: removing state object for %s", logFuncName, absAddr)
   358  	}
   359  
   360  	schema, currentVersion := (*providerSchema).SchemaForResourceAddr(absAddr.ContainingResource().Resource)
   361  	if schema == nil {
   362  		// It shouldn't be possible to get this far in any real scenario
   363  		// without a schema, but we might end up here in contrived tests that
   364  		// fail to set up their world properly.
   365  		return fmt.Errorf("failed to encode %s in state: no resource type schema available", absAddr)
   366  	}
   367  
   368  	src, err := obj.Encode(schema.ImpliedType(), currentVersion)
   369  	if err != nil {
   370  		return fmt.Errorf("failed to encode %s in state: %s", absAddr, err)
   371  	}
   372  
   373  	write(src)
   374  	return nil
   375  }
   376  
   377  // planDestroy returns a plain destroy diff.
   378  func (n *NodeAbstractResourceInstance) planDestroy(ctx EvalContext, currentState *states.ResourceInstanceObject, deposedKey states.DeposedKey) (*plans.ResourceInstanceChange, tfdiags.Diagnostics) {
   379  	var diags tfdiags.Diagnostics
   380  
   381  	absAddr := n.Addr
   382  
   383  	if n.ResolvedProvider.Provider.Type == "" {
   384  		if deposedKey == "" {
   385  			panic(fmt.Sprintf("planDestroy for %s does not have ProviderAddr set", absAddr))
   386  		} else {
   387  			panic(fmt.Sprintf("planDestroy for %s (deposed %s) does not have ProviderAddr set", absAddr, deposedKey))
   388  		}
   389  	}
   390  
   391  	// If there is no state or our attributes object is null then we're already
   392  	// destroyed.
   393  	if currentState == nil || currentState.Value.IsNull() {
   394  		// We still need to generate a NoOp change, because that allows
   395  		// outside consumers of the plan to distinguish between us affirming
   396  		// that we checked something and concluded no changes were needed
   397  		// vs. that something being entirely excluded e.g. due to -target.
   398  		noop := &plans.ResourceInstanceChange{
   399  			Addr:        absAddr,
   400  			PrevRunAddr: n.prevRunAddr(ctx),
   401  			DeposedKey:  deposedKey,
   402  			Change: plans.Change{
   403  				Action: plans.NoOp,
   404  				Before: cty.NullVal(cty.DynamicPseudoType),
   405  				After:  cty.NullVal(cty.DynamicPseudoType),
   406  			},
   407  			ProviderAddr: n.ResolvedProvider,
   408  		}
   409  		return noop, nil
   410  	}
   411  
   412  	// Call pre-diff hook
   413  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
   414  		return h.PreDiff(
   415  			absAddr, deposedKey.Generation(),
   416  			currentState.Value,
   417  			cty.NullVal(cty.DynamicPseudoType),
   418  		)
   419  	}))
   420  	if diags.HasErrors() {
   421  		return nil, diags
   422  	}
   423  
   424  	// Plan is always the same for a destroy. We don't need the provider's
   425  	// help for this one.
   426  	plan := &plans.ResourceInstanceChange{
   427  		Addr:        absAddr,
   428  		PrevRunAddr: n.prevRunAddr(ctx),
   429  		DeposedKey:  deposedKey,
   430  		Change: plans.Change{
   431  			Action: plans.Delete,
   432  			Before: currentState.Value,
   433  			After:  cty.NullVal(cty.DynamicPseudoType),
   434  		},
   435  		Private:      currentState.Private,
   436  		ProviderAddr: n.ResolvedProvider,
   437  	}
   438  
   439  	// Call post-diff hook
   440  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
   441  		return h.PostDiff(
   442  			absAddr,
   443  			deposedKey.Generation(),
   444  			plan.Action,
   445  			plan.Before,
   446  			plan.After,
   447  		)
   448  	}))
   449  
   450  	return plan, diags
   451  }
   452  
   453  // writeChange saves a planned change for an instance object into the set of
   454  // global planned changes.
   455  func (n *NodeAbstractResourceInstance) writeChange(ctx EvalContext, change *plans.ResourceInstanceChange, deposedKey states.DeposedKey) error {
   456  	changes := ctx.Changes()
   457  
   458  	if change == nil {
   459  		// Caller sets nil to indicate that we need to remove a change from
   460  		// the set of changes.
   461  		gen := states.CurrentGen
   462  		if deposedKey != states.NotDeposed {
   463  			gen = deposedKey
   464  		}
   465  		changes.RemoveResourceInstanceChange(n.Addr, gen)
   466  		return nil
   467  	}
   468  
   469  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   470  	if err != nil {
   471  		return err
   472  	}
   473  
   474  	if change.Addr.String() != n.Addr.String() || change.DeposedKey != deposedKey {
   475  		// Should never happen, and indicates a bug in the caller.
   476  		panic("inconsistent address and/or deposed key in writeChange")
   477  	}
   478  	if change.PrevRunAddr.Resource.Resource.Type == "" {
   479  		// Should never happen, and indicates a bug in the caller.
   480  		// (The change.Encode function actually has its own fixup to just
   481  		// quietly make this match change.Addr in the incorrect case, but we
   482  		// intentionally panic here in order to catch incorrect callers where
   483  		// the stack trace will hopefully be actually useful. The tolerance
   484  		// at the next layer down is mainly to accommodate sloppy input in
   485  		// older tests.)
   486  		panic("unpopulated ResourceInstanceChange.PrevRunAddr in writeChange")
   487  	}
   488  
   489  	ri := n.Addr.Resource
   490  	schema, _ := providerSchema.SchemaForResourceAddr(ri.Resource)
   491  	if schema == nil {
   492  		// Should be caught during validation, so we don't bother with a pretty error here
   493  		return fmt.Errorf("provider does not support resource type %q", ri.Resource.Type)
   494  	}
   495  
   496  	csrc, err := change.Encode(schema.ImpliedType())
   497  	if err != nil {
   498  		return fmt.Errorf("failed to encode planned changes for %s: %s", n.Addr, err)
   499  	}
   500  
   501  	changes.AppendResourceInstanceChange(csrc)
   502  	if deposedKey == states.NotDeposed {
   503  		log.Printf("[TRACE] writeChange: recorded %s change for %s", change.Action, n.Addr)
   504  	} else {
   505  		log.Printf("[TRACE] writeChange: recorded %s change for %s deposed object %s", change.Action, n.Addr, deposedKey)
   506  	}
   507  
   508  	return nil
   509  }
   510  
   511  // refresh does a refresh for a resource
   512  func (n *NodeAbstractResourceInstance) refresh(ctx EvalContext, deposedKey states.DeposedKey, state *states.ResourceInstanceObject) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
   513  	var diags tfdiags.Diagnostics
   514  	absAddr := n.Addr
   515  	if deposedKey == states.NotDeposed {
   516  		log.Printf("[TRACE] NodeAbstractResourceInstance.refresh for %s", absAddr)
   517  	} else {
   518  		log.Printf("[TRACE] NodeAbstractResourceInstance.refresh for %s (deposed object %s)", absAddr, deposedKey)
   519  	}
   520  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   521  	if err != nil {
   522  		return state, diags.Append(err)
   523  	}
   524  	// If we have no state, we don't do any refreshing
   525  	if state == nil {
   526  		log.Printf("[DEBUG] refresh: %s: no state, so not refreshing", absAddr)
   527  		return state, diags
   528  	}
   529  
   530  	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.Resource.ContainingResource())
   531  	if schema == nil {
   532  		// Should be caught during validation, so we don't bother with a pretty error here
   533  		diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type))
   534  		return state, diags
   535  	}
   536  
   537  	metaConfigVal, metaDiags := n.providerMetas(ctx)
   538  	diags = diags.Append(metaDiags)
   539  	if diags.HasErrors() {
   540  		return state, diags
   541  	}
   542  
   543  	hookGen := states.CurrentGen
   544  	if deposedKey != states.NotDeposed {
   545  		hookGen = deposedKey
   546  	}
   547  
   548  	// Call pre-refresh hook
   549  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
   550  		return h.PreRefresh(absAddr, hookGen, state.Value)
   551  	}))
   552  	if diags.HasErrors() {
   553  		return state, diags
   554  	}
   555  
   556  	// Refresh!
   557  	priorVal := state.Value
   558  
   559  	// Unmarked before sending to provider
   560  	var priorPaths []cty.PathValueMarks
   561  	if priorVal.ContainsMarked() {
   562  		priorVal, priorPaths = priorVal.UnmarkDeepWithPaths()
   563  	}
   564  
   565  	providerReq := providers.ReadResourceRequest{
   566  		TypeName:     n.Addr.Resource.Resource.Type,
   567  		PriorState:   priorVal,
   568  		Private:      state.Private,
   569  		ProviderMeta: metaConfigVal,
   570  	}
   571  
   572  	resp := provider.ReadResource(providerReq)
   573  	if n.Config != nil {
   574  		resp.Diagnostics = resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String())
   575  	}
   576  
   577  	diags = diags.Append(resp.Diagnostics)
   578  	if diags.HasErrors() {
   579  		return state, diags
   580  	}
   581  
   582  	if resp.NewState == cty.NilVal {
   583  		// This ought not to happen in real cases since it's not possible to
   584  		// send NilVal over the plugin RPC channel, but it can come up in
   585  		// tests due to sloppy mocking.
   586  		panic("new state is cty.NilVal")
   587  	}
   588  
   589  	for _, err := range resp.NewState.Type().TestConformance(schema.ImpliedType()) {
   590  		diags = diags.Append(tfdiags.Sourceless(
   591  			tfdiags.Error,
   592  			"Provider produced invalid object",
   593  			fmt.Sprintf(
   594  				"Provider %q planned an invalid value for %s during refresh: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
   595  				n.ResolvedProvider.Provider.String(), absAddr, tfdiags.FormatError(err),
   596  			),
   597  		))
   598  	}
   599  	if diags.HasErrors() {
   600  		return state, diags
   601  	}
   602  
   603  	// We have no way to exempt provider using the legacy SDK from this check,
   604  	// so we can only log inconsistencies with the updated state values.
   605  	// In most cases these are not errors anyway, and represent "drift" from
   606  	// external changes which will be handled by the subsequent plan.
   607  	if errs := objchange.AssertObjectCompatible(schema, priorVal, resp.NewState); len(errs) > 0 {
   608  		var buf strings.Builder
   609  		fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s during refresh.", n.ResolvedProvider.Provider.String(), absAddr)
   610  		for _, err := range errs {
   611  			fmt.Fprintf(&buf, "\n      - %s", tfdiags.FormatError(err))
   612  		}
   613  		log.Print(buf.String())
   614  	}
   615  
   616  	ret := state.DeepCopy()
   617  	ret.Value = resp.NewState
   618  	ret.Private = resp.Private
   619  
   620  	// Call post-refresh hook
   621  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
   622  		return h.PostRefresh(absAddr, hookGen, priorVal, ret.Value)
   623  	}))
   624  	if diags.HasErrors() {
   625  		return ret, diags
   626  	}
   627  
   628  	// Mark the value if necessary
   629  	if len(priorPaths) > 0 {
   630  		ret.Value = ret.Value.MarkWithPaths(priorPaths)
   631  	}
   632  
   633  	return ret, diags
   634  }
   635  
   636  func (n *NodeAbstractResourceInstance) plan(
   637  	ctx EvalContext,
   638  	plannedChange *plans.ResourceInstanceChange,
   639  	currentState *states.ResourceInstanceObject,
   640  	createBeforeDestroy bool,
   641  	forceReplace []addrs.AbsResourceInstance) (*plans.ResourceInstanceChange, *states.ResourceInstanceObject, tfdiags.Diagnostics) {
   642  	var diags tfdiags.Diagnostics
   643  	var state *states.ResourceInstanceObject
   644  	var plan *plans.ResourceInstanceChange
   645  
   646  	config := *n.Config
   647  	resource := n.Addr.Resource.Resource
   648  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   649  	if err != nil {
   650  		return plan, state, diags.Append(err)
   651  	}
   652  
   653  	if plannedChange != nil {
   654  		// If we already planned the action, we stick to that plan
   655  		createBeforeDestroy = plannedChange.Action == plans.CreateThenDelete
   656  	}
   657  
   658  	if providerSchema == nil {
   659  		diags = diags.Append(fmt.Errorf("provider schema is unavailable for %s", n.Addr))
   660  		return plan, state, diags
   661  	}
   662  
   663  	// Evaluate the configuration
   664  	schema, _ := providerSchema.SchemaForResourceAddr(resource)
   665  	if schema == nil {
   666  		// Should be caught during validation, so we don't bother with a pretty error here
   667  		diags = diags.Append(fmt.Errorf("provider does not support resource type %q", resource.Type))
   668  		return plan, state, diags
   669  	}
   670  
   671  	forEach, _ := evaluateForEachExpression(n.Config.ForEach, ctx)
   672  
   673  	keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
   674  	origConfigVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
   675  	diags = diags.Append(configDiags)
   676  	if configDiags.HasErrors() {
   677  		return plan, state, diags
   678  	}
   679  
   680  	metaConfigVal, metaDiags := n.providerMetas(ctx)
   681  	diags = diags.Append(metaDiags)
   682  	if diags.HasErrors() {
   683  		return plan, state, diags
   684  	}
   685  
   686  	var priorVal cty.Value
   687  	var priorValTainted cty.Value
   688  	var priorPrivate []byte
   689  	if currentState != nil {
   690  		if currentState.Status != states.ObjectTainted {
   691  			priorVal = currentState.Value
   692  			priorPrivate = currentState.Private
   693  		} else {
   694  			// If the prior state is tainted then we'll proceed below like
   695  			// we're creating an entirely new object, but then turn it into
   696  			// a synthetic "Replace" change at the end, creating the same
   697  			// result as if the provider had marked at least one argument
   698  			// change as "requires replacement".
   699  			priorValTainted = currentState.Value
   700  			priorVal = cty.NullVal(schema.ImpliedType())
   701  		}
   702  	} else {
   703  		priorVal = cty.NullVal(schema.ImpliedType())
   704  	}
   705  
   706  	log.Printf("[TRACE] Re-validating config for %q", n.Addr)
   707  	// Allow the provider to validate the final set of values.  The config was
   708  	// statically validated early on, but there may have been unknown values
   709  	// which the provider could not validate at the time.
   710  	//
   711  	// TODO: It would be more correct to validate the config after
   712  	// ignore_changes has been applied, but the current implementation cannot
   713  	// exclude computed-only attributes when given the `all` option.
   714  
   715  	// we must unmark and use the original config, since the ignore_changes
   716  	// handling below needs access to the marks.
   717  	unmarkedConfigVal, _ := origConfigVal.UnmarkDeep()
   718  	validateResp := provider.ValidateResourceConfig(
   719  		providers.ValidateResourceConfigRequest{
   720  			TypeName: n.Addr.Resource.Resource.Type,
   721  			Config:   unmarkedConfigVal,
   722  		},
   723  	)
   724  	diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
   725  	if diags.HasErrors() {
   726  		return plan, state, diags
   727  	}
   728  
   729  	// ignore_changes is meant to only apply to the configuration, so it must
   730  	// be applied before we generate a plan. This ensures the config used for
   731  	// the proposed value, the proposed value itself, and the config presented
   732  	// to the provider in the PlanResourceChange request all agree on the
   733  	// starting values.
   734  	// Here we operate on the marked values, so as to revert any changes to the
   735  	// marks as well as the value.
   736  	configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(priorVal, origConfigVal)
   737  	diags = diags.Append(ignoreChangeDiags)
   738  	if ignoreChangeDiags.HasErrors() {
   739  		return plan, state, diags
   740  	}
   741  
   742  	// Create an unmarked version of our config val and our prior val.
   743  	// Store the paths for the config val to re-mark after we've sent things
   744  	// over the wire.
   745  	unmarkedConfigVal, unmarkedPaths := configValIgnored.UnmarkDeepWithPaths()
   746  	unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths()
   747  
   748  	proposedNewVal := objchange.ProposedNew(schema, unmarkedPriorVal, unmarkedConfigVal)
   749  
   750  	// Call pre-diff hook
   751  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
   752  		return h.PreDiff(n.Addr, states.CurrentGen, priorVal, proposedNewVal)
   753  	}))
   754  	if diags.HasErrors() {
   755  		return plan, state, diags
   756  	}
   757  
   758  	resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{
   759  		TypeName:         n.Addr.Resource.Resource.Type,
   760  		Config:           unmarkedConfigVal,
   761  		PriorState:       unmarkedPriorVal,
   762  		ProposedNewState: proposedNewVal,
   763  		PriorPrivate:     priorPrivate,
   764  		ProviderMeta:     metaConfigVal,
   765  	})
   766  	diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
   767  	if diags.HasErrors() {
   768  		return plan, state, diags
   769  	}
   770  
   771  	plannedNewVal := resp.PlannedState
   772  	plannedPrivate := resp.PlannedPrivate
   773  
   774  	if plannedNewVal == cty.NilVal {
   775  		// Should never happen. Since real-world providers return via RPC a nil
   776  		// is always a bug in the client-side stub. This is more likely caused
   777  		// by an incompletely-configured mock provider in tests, though.
   778  		panic(fmt.Sprintf("PlanResourceChange of %s produced nil value", n.Addr))
   779  	}
   780  
   781  	// We allow the planned new value to disagree with configuration _values_
   782  	// here, since that allows the provider to do special logic like a
   783  	// DiffSuppressFunc, but we still require that the provider produces
   784  	// a value whose type conforms to the schema.
   785  	for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) {
   786  		diags = diags.Append(tfdiags.Sourceless(
   787  			tfdiags.Error,
   788  			"Provider produced invalid plan",
   789  			fmt.Sprintf(
   790  				"Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
   791  				n.ResolvedProvider.Provider, tfdiags.FormatErrorPrefixed(err, n.Addr.String()),
   792  			),
   793  		))
   794  	}
   795  	if diags.HasErrors() {
   796  		return plan, state, diags
   797  	}
   798  
   799  	if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, unmarkedConfigVal, plannedNewVal); len(errs) > 0 {
   800  		if resp.LegacyTypeSystem {
   801  			// The shimming of the old type system in the legacy SDK is not precise
   802  			// enough to pass this consistency check, so we'll give it a pass here,
   803  			// but we will generate a warning about it so that we are more likely
   804  			// to notice in the logs if an inconsistency beyond the type system
   805  			// leads to a downstream provider failure.
   806  			var buf strings.Builder
   807  			fmt.Fprintf(&buf,
   808  				"[WARN] Provider %q produced an invalid plan for %s, but we are tolerating it because it is using the legacy plugin SDK.\n    The following problems may be the cause of any confusing errors from downstream operations:",
   809  				n.ResolvedProvider.Provider, n.Addr,
   810  			)
   811  			for _, err := range errs {
   812  				fmt.Fprintf(&buf, "\n      - %s", tfdiags.FormatError(err))
   813  			}
   814  			log.Print(buf.String())
   815  		} else {
   816  			for _, err := range errs {
   817  				diags = diags.Append(tfdiags.Sourceless(
   818  					tfdiags.Error,
   819  					"Provider produced invalid plan",
   820  					fmt.Sprintf(
   821  						"Provider %q planned an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
   822  						n.ResolvedProvider.Provider, tfdiags.FormatErrorPrefixed(err, n.Addr.String()),
   823  					),
   824  				))
   825  			}
   826  			return plan, state, diags
   827  		}
   828  	}
   829  
   830  	if resp.LegacyTypeSystem {
   831  		// Because we allow legacy providers to depart from the contract and
   832  		// return changes to non-computed values, the plan response may have
   833  		// altered values that were already suppressed with ignore_changes.
   834  		// A prime example of this is where providers attempt to obfuscate
   835  		// config data by turning the config value into a hash and storing the
   836  		// hash value in the state. There are enough cases of this in existing
   837  		// providers that we must accommodate the behavior for now, so for
   838  		// ignore_changes to work at all on these values, we will revert the
   839  		// ignored values once more.
   840  		plannedNewVal, ignoreChangeDiags = n.processIgnoreChanges(unmarkedPriorVal, plannedNewVal)
   841  		diags = diags.Append(ignoreChangeDiags)
   842  		if ignoreChangeDiags.HasErrors() {
   843  			return plan, state, diags
   844  		}
   845  	}
   846  
   847  	// Add the marks back to the planned new value -- this must happen after ignore changes
   848  	// have been processed
   849  	unmarkedPlannedNewVal := plannedNewVal
   850  	if len(unmarkedPaths) > 0 {
   851  		plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths)
   852  	}
   853  
   854  	// The provider produces a list of paths to attributes whose changes mean
   855  	// that we must replace rather than update an existing remote object.
   856  	// However, we only need to do that if the identified attributes _have_
   857  	// actually changed -- particularly after we may have undone some of the
   858  	// changes in processIgnoreChanges -- so now we'll filter that list to
   859  	// include only where changes are detected.
   860  	reqRep := cty.NewPathSet()
   861  	if len(resp.RequiresReplace) > 0 {
   862  		for _, path := range resp.RequiresReplace {
   863  			if priorVal.IsNull() {
   864  				// If prior is null then we don't expect any RequiresReplace at all,
   865  				// because this is a Create action.
   866  				continue
   867  			}
   868  
   869  			priorChangedVal, priorPathDiags := hcl.ApplyPath(unmarkedPriorVal, path, nil)
   870  			plannedChangedVal, plannedPathDiags := hcl.ApplyPath(plannedNewVal, path, nil)
   871  			if plannedPathDiags.HasErrors() && priorPathDiags.HasErrors() {
   872  				// This means the path was invalid in both the prior and new
   873  				// values, which is an error with the provider itself.
   874  				diags = diags.Append(tfdiags.Sourceless(
   875  					tfdiags.Error,
   876  					"Provider produced invalid plan",
   877  					fmt.Sprintf(
   878  						"Provider %q has indicated \"requires replacement\" on %s for a non-existent attribute path %#v.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
   879  						n.ResolvedProvider.Provider, n.Addr, path,
   880  					),
   881  				))
   882  				continue
   883  			}
   884  
   885  			// Make sure we have valid Values for both values.
   886  			// Note: if the opposing value was of the type
   887  			// cty.DynamicPseudoType, the type assigned here may not exactly
   888  			// match the schema. This is fine here, since we're only going to
   889  			// check for equality, but if the NullVal is to be used, we need to
   890  			// check the schema for th true type.
   891  			switch {
   892  			case priorChangedVal == cty.NilVal && plannedChangedVal == cty.NilVal:
   893  				// this should never happen without ApplyPath errors above
   894  				panic("requires replace path returned 2 nil values")
   895  			case priorChangedVal == cty.NilVal:
   896  				priorChangedVal = cty.NullVal(plannedChangedVal.Type())
   897  			case plannedChangedVal == cty.NilVal:
   898  				plannedChangedVal = cty.NullVal(priorChangedVal.Type())
   899  			}
   900  
   901  			// Unmark for this value for the equality test. If only sensitivity has changed,
   902  			// this does not require an Update or Replace
   903  			unmarkedPlannedChangedVal, _ := plannedChangedVal.UnmarkDeep()
   904  			eqV := unmarkedPlannedChangedVal.Equals(priorChangedVal)
   905  			if !eqV.IsKnown() || eqV.False() {
   906  				reqRep.Add(path)
   907  			}
   908  		}
   909  		if diags.HasErrors() {
   910  			return plan, state, diags
   911  		}
   912  	}
   913  
   914  	// The user might also ask us to force replacing a particular resource
   915  	// instance, regardless of whether the provider thinks it needs replacing.
   916  	// For example, users typically do this if they learn a particular object
   917  	// has become degraded in an immutable infrastructure scenario and so
   918  	// replacing it with a new object is a viable repair path.
   919  	matchedForceReplace := false
   920  	for _, candidateAddr := range forceReplace {
   921  		if candidateAddr.Equal(n.Addr) {
   922  			matchedForceReplace = true
   923  			break
   924  		}
   925  
   926  		// For "force replace" purposes we require an exact resource instance
   927  		// address to match. If a user forgets to include the instance key
   928  		// for a multi-instance resource then it won't match here, but we
   929  		// have an earlier check in NodePlannableResource.Execute that should
   930  		// prevent us from getting here in that case.
   931  	}
   932  
   933  	// Unmark for this test for value equality.
   934  	eqV := unmarkedPlannedNewVal.Equals(unmarkedPriorVal)
   935  	eq := eqV.IsKnown() && eqV.True()
   936  
   937  	var action plans.Action
   938  	var actionReason plans.ResourceInstanceChangeActionReason
   939  	switch {
   940  	case priorVal.IsNull():
   941  		action = plans.Create
   942  	case eq && !matchedForceReplace:
   943  		action = plans.NoOp
   944  	case matchedForceReplace || !reqRep.Empty():
   945  		// If the user "forced replace" of this instance of if there are any
   946  		// "requires replace" paths left _after our filtering above_ then this
   947  		// is a replace action.
   948  		if createBeforeDestroy {
   949  			action = plans.CreateThenDelete
   950  		} else {
   951  			action = plans.DeleteThenCreate
   952  		}
   953  		switch {
   954  		case matchedForceReplace:
   955  			actionReason = plans.ResourceInstanceReplaceByRequest
   956  		case !reqRep.Empty():
   957  			actionReason = plans.ResourceInstanceReplaceBecauseCannotUpdate
   958  		}
   959  	default:
   960  		action = plans.Update
   961  		// "Delete" is never chosen here, because deletion plans are always
   962  		// created more directly elsewhere, such as in "orphan" handling.
   963  	}
   964  
   965  	if action.IsReplace() {
   966  		// In this strange situation we want to produce a change object that
   967  		// shows our real prior object but has a _new_ object that is built
   968  		// from a null prior object, since we're going to delete the one
   969  		// that has all the computed values on it.
   970  		//
   971  		// Therefore we'll ask the provider to plan again here, giving it
   972  		// a null object for the prior, and then we'll meld that with the
   973  		// _actual_ prior state to produce a correctly-shaped replace change.
   974  		// The resulting change should show any computed attributes changing
   975  		// from known prior values to unknown values, unless the provider is
   976  		// able to predict new values for any of these computed attributes.
   977  		nullPriorVal := cty.NullVal(schema.ImpliedType())
   978  
   979  		// Since there is no prior state to compare after replacement, we need
   980  		// a new unmarked config from our original with no ignored values.
   981  		unmarkedConfigVal := origConfigVal
   982  		if origConfigVal.ContainsMarked() {
   983  			unmarkedConfigVal, _ = origConfigVal.UnmarkDeep()
   984  		}
   985  
   986  		// create a new proposed value from the null state and the config
   987  		proposedNewVal = objchange.ProposedNew(schema, nullPriorVal, unmarkedConfigVal)
   988  
   989  		resp = provider.PlanResourceChange(providers.PlanResourceChangeRequest{
   990  			TypeName:         n.Addr.Resource.Resource.Type,
   991  			Config:           unmarkedConfigVal,
   992  			PriorState:       nullPriorVal,
   993  			ProposedNewState: proposedNewVal,
   994  			PriorPrivate:     plannedPrivate,
   995  			ProviderMeta:     metaConfigVal,
   996  		})
   997  		// We need to tread carefully here, since if there are any warnings
   998  		// in here they probably also came out of our previous call to
   999  		// PlanResourceChange above, and so we don't want to repeat them.
  1000  		// Consequently, we break from the usual pattern here and only
  1001  		// append these new diagnostics if there's at least one error inside.
  1002  		if resp.Diagnostics.HasErrors() {
  1003  			diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
  1004  			return plan, state, diags
  1005  		}
  1006  		plannedNewVal = resp.PlannedState
  1007  		plannedPrivate = resp.PlannedPrivate
  1008  
  1009  		if len(unmarkedPaths) > 0 {
  1010  			plannedNewVal = plannedNewVal.MarkWithPaths(unmarkedPaths)
  1011  		}
  1012  
  1013  		for _, err := range plannedNewVal.Type().TestConformance(schema.ImpliedType()) {
  1014  			diags = diags.Append(tfdiags.Sourceless(
  1015  				tfdiags.Error,
  1016  				"Provider produced invalid plan",
  1017  				fmt.Sprintf(
  1018  					"Provider %q planned an invalid value for %s%s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
  1019  					n.ResolvedProvider.Provider, n.Addr, tfdiags.FormatError(err),
  1020  				),
  1021  			))
  1022  		}
  1023  		if diags.HasErrors() {
  1024  			return plan, state, diags
  1025  		}
  1026  	}
  1027  
  1028  	// If our prior value was tainted then we actually want this to appear
  1029  	// as a replace change, even though so far we've been treating it as a
  1030  	// create.
  1031  	if action == plans.Create && !priorValTainted.IsNull() {
  1032  		if createBeforeDestroy {
  1033  			action = plans.CreateThenDelete
  1034  		} else {
  1035  			action = plans.DeleteThenCreate
  1036  		}
  1037  		priorVal = priorValTainted
  1038  		actionReason = plans.ResourceInstanceReplaceBecauseTainted
  1039  	}
  1040  
  1041  	// If we plan to write or delete sensitive paths from state,
  1042  	// this is an Update action
  1043  	if action == plans.NoOp && !marksEqual(unmarkedPaths, priorPaths) {
  1044  		action = plans.Update
  1045  	}
  1046  
  1047  	// As a special case, if we have a previous diff (presumably from the plan
  1048  	// phases, whereas we're now in the apply phase) and it was for a replace,
  1049  	// we've already deleted the original object from state by the time we
  1050  	// get here and so we would've ended up with a _create_ action this time,
  1051  	// which we now need to paper over to get a result consistent with what
  1052  	// we originally intended.
  1053  	if plannedChange != nil {
  1054  		prevChange := *plannedChange
  1055  		if prevChange.Action.IsReplace() && action == plans.Create {
  1056  			log.Printf("[TRACE] plan: %s treating Create change as %s change to match with earlier plan", n.Addr, prevChange.Action)
  1057  			action = prevChange.Action
  1058  			priorVal = prevChange.Before
  1059  		}
  1060  	}
  1061  
  1062  	// Call post-refresh hook
  1063  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1064  		return h.PostDiff(n.Addr, states.CurrentGen, action, priorVal, plannedNewVal)
  1065  	}))
  1066  	if diags.HasErrors() {
  1067  		return plan, state, diags
  1068  	}
  1069  
  1070  	// Update our return plan
  1071  	plan = &plans.ResourceInstanceChange{
  1072  		Addr:         n.Addr,
  1073  		PrevRunAddr:  n.prevRunAddr(ctx),
  1074  		Private:      plannedPrivate,
  1075  		ProviderAddr: n.ResolvedProvider,
  1076  		Change: plans.Change{
  1077  			Action: action,
  1078  			Before: priorVal,
  1079  			// Pass the marked planned value through in our change
  1080  			// to propogate through evaluation.
  1081  			// Marks will be removed when encoding.
  1082  			After: plannedNewVal,
  1083  		},
  1084  		ActionReason:    actionReason,
  1085  		RequiredReplace: reqRep,
  1086  	}
  1087  
  1088  	// Update our return state
  1089  	state = &states.ResourceInstanceObject{
  1090  		// We use the special "planned" status here to note that this
  1091  		// object's value is not yet complete. Objects with this status
  1092  		// cannot be used during expression evaluation, so the caller
  1093  		// must _also_ record the returned change in the active plan,
  1094  		// which the expression evaluator will use in preference to this
  1095  		// incomplete value recorded in the state.
  1096  		Status:  states.ObjectPlanned,
  1097  		Value:   plannedNewVal,
  1098  		Private: plannedPrivate,
  1099  	}
  1100  
  1101  	return plan, state, diags
  1102  }
  1103  
  1104  func (n *NodeAbstractResource) processIgnoreChanges(prior, config cty.Value) (cty.Value, tfdiags.Diagnostics) {
  1105  	// ignore_changes only applies when an object already exists, since we
  1106  	// can't ignore changes to a thing we've not created yet.
  1107  	if prior.IsNull() {
  1108  		return config, nil
  1109  	}
  1110  
  1111  	ignoreChanges := traversalsToPaths(n.Config.Managed.IgnoreChanges)
  1112  	ignoreAll := n.Config.Managed.IgnoreAllChanges
  1113  
  1114  	if len(ignoreChanges) == 0 && !ignoreAll {
  1115  		return config, nil
  1116  	}
  1117  	if ignoreAll {
  1118  		return prior, nil
  1119  	}
  1120  	if prior.IsNull() || config.IsNull() {
  1121  		// Ignore changes doesn't apply when we're creating for the first time.
  1122  		// Proposed should never be null here, but if it is then we'll just let it be.
  1123  		return config, nil
  1124  	}
  1125  
  1126  	ret, diags := processIgnoreChangesIndividual(prior, config, ignoreChanges)
  1127  
  1128  	return ret, diags
  1129  }
  1130  
  1131  // Convert the hcl.Traversal values we get form the configuration to the
  1132  // cty.Path values we need to operate on the cty.Values
  1133  func traversalsToPaths(traversals []hcl.Traversal) []cty.Path {
  1134  	paths := make([]cty.Path, len(traversals))
  1135  	for i, traversal := range traversals {
  1136  		path := make(cty.Path, len(traversal))
  1137  		for si, step := range traversal {
  1138  			switch ts := step.(type) {
  1139  			case hcl.TraverseRoot:
  1140  				path[si] = cty.GetAttrStep{
  1141  					Name: ts.Name,
  1142  				}
  1143  			case hcl.TraverseAttr:
  1144  				path[si] = cty.GetAttrStep{
  1145  					Name: ts.Name,
  1146  				}
  1147  			case hcl.TraverseIndex:
  1148  				path[si] = cty.IndexStep{
  1149  					Key: ts.Key,
  1150  				}
  1151  			default:
  1152  				panic(fmt.Sprintf("unsupported traversal step %#v", step))
  1153  			}
  1154  		}
  1155  		paths[i] = path
  1156  	}
  1157  	return paths
  1158  }
  1159  
  1160  func processIgnoreChangesIndividual(prior, config cty.Value, ignoreChangesPath []cty.Path) (cty.Value, tfdiags.Diagnostics) {
  1161  	type ignoreChange struct {
  1162  		// Path is the full path, minus any trailing map index
  1163  		path cty.Path
  1164  		// Value is the value we are to retain at the above path. If there is a
  1165  		// key value, this must be a map and the desired value will be at the
  1166  		// key index.
  1167  		value cty.Value
  1168  		// Key is the index key if the ignored path ends in a map index.
  1169  		key cty.Value
  1170  	}
  1171  	var ignoredValues []ignoreChange
  1172  
  1173  	// Find the actual changes first and store them in the ignoreChange struct.
  1174  	// If the change was to a map value, and the key doesn't exist in the
  1175  	// config, it would never be visited in the transform walk.
  1176  	for _, icPath := range ignoreChangesPath {
  1177  		key := cty.NullVal(cty.String)
  1178  		// check for a map index, since maps are the only structure where we
  1179  		// could have invalid path steps.
  1180  		last, ok := icPath[len(icPath)-1].(cty.IndexStep)
  1181  		if ok {
  1182  			if last.Key.Type() == cty.String {
  1183  				icPath = icPath[:len(icPath)-1]
  1184  				key = last.Key
  1185  			}
  1186  		}
  1187  
  1188  		// The structure should have been validated already, and we already
  1189  		// trimmed the trailing map index. Any other intermediate index error
  1190  		// means we wouldn't be able to apply the value below, so no need to
  1191  		// record this.
  1192  		p, err := icPath.Apply(prior)
  1193  		if err != nil {
  1194  			continue
  1195  		}
  1196  		c, err := icPath.Apply(config)
  1197  		if err != nil {
  1198  			continue
  1199  		}
  1200  
  1201  		// If this is a map, it is checking the entire map value for equality
  1202  		// rather than the individual key. This means that the change is stored
  1203  		// here even if our ignored key doesn't change. That is OK since it
  1204  		// won't cause any changes in the transformation, but allows us to skip
  1205  		// breaking up the maps and checking for key existence here too.
  1206  		if !p.RawEquals(c) {
  1207  			// there a change to ignore at this path, store the prior value
  1208  			ignoredValues = append(ignoredValues, ignoreChange{icPath, p, key})
  1209  		}
  1210  	}
  1211  
  1212  	if len(ignoredValues) == 0 {
  1213  		return config, nil
  1214  	}
  1215  
  1216  	ret, _ := cty.Transform(config, func(path cty.Path, v cty.Value) (cty.Value, error) {
  1217  		// Easy path for when we are only matching the entire value. The only
  1218  		// values we break up for inspection are maps.
  1219  		if !v.Type().IsMapType() {
  1220  			for _, ignored := range ignoredValues {
  1221  				if path.Equals(ignored.path) {
  1222  					return ignored.value, nil
  1223  				}
  1224  			}
  1225  			return v, nil
  1226  		}
  1227  		// We now know this must be a map, so we need to accumulate the values
  1228  		// key-by-key.
  1229  
  1230  		if !v.IsNull() && !v.IsKnown() {
  1231  			// since v is not known, we cannot ignore individual keys
  1232  			return v, nil
  1233  		}
  1234  
  1235  		// The map values will remain as cty values, so we only need to store
  1236  		// the marks from the outer map itself
  1237  		v, vMarks := v.Unmark()
  1238  
  1239  		// The configMap is the current configuration value, which we will
  1240  		// mutate based on the ignored paths and the prior map value.
  1241  		var configMap map[string]cty.Value
  1242  		switch {
  1243  		case v.IsNull() || v.LengthInt() == 0:
  1244  			configMap = map[string]cty.Value{}
  1245  		default:
  1246  			configMap = v.AsValueMap()
  1247  		}
  1248  
  1249  		for _, ignored := range ignoredValues {
  1250  			if !path.Equals(ignored.path) {
  1251  				continue
  1252  			}
  1253  
  1254  			if ignored.key.IsNull() {
  1255  				// The map address is confirmed to match at this point,
  1256  				// so if there is no key, we want the entire map and can
  1257  				// stop accumulating values.
  1258  				return ignored.value, nil
  1259  			}
  1260  			// Now we know we are ignoring a specific index of this map, so get
  1261  			// the config map and modify, add, or remove the desired key.
  1262  
  1263  			// We also need to create a prior map, so we can check for
  1264  			// existence while getting the value, because Value.Index will
  1265  			// return null for a key with a null value and for a non-existent
  1266  			// key.
  1267  			var priorMap map[string]cty.Value
  1268  
  1269  			// We need to drop the marks from the ignored map for handling. We
  1270  			// don't need to store these, as we now know the ignored value is
  1271  			// only within the map, not the map itself.
  1272  			ignoredVal, _ := ignored.value.Unmark()
  1273  
  1274  			switch {
  1275  			case ignored.value.IsNull() || ignoredVal.LengthInt() == 0:
  1276  				priorMap = map[string]cty.Value{}
  1277  			default:
  1278  				priorMap = ignoredVal.AsValueMap()
  1279  			}
  1280  
  1281  			key := ignored.key.AsString()
  1282  			priorElem, keep := priorMap[key]
  1283  
  1284  			switch {
  1285  			case !keep:
  1286  				// this didn't exist in the old map value, so we're keeping the
  1287  				// "absence" of the key by removing it from the config
  1288  				delete(configMap, key)
  1289  			default:
  1290  				configMap[key] = priorElem
  1291  			}
  1292  		}
  1293  
  1294  		var newVal cty.Value
  1295  		switch {
  1296  		case len(configMap) > 0:
  1297  			newVal = cty.MapVal(configMap)
  1298  		case v.IsNull():
  1299  			// if the config value was null, and no values remain in the map,
  1300  			// reset the value to null.
  1301  			newVal = v
  1302  		default:
  1303  			newVal = cty.MapValEmpty(v.Type().ElementType())
  1304  		}
  1305  
  1306  		if len(vMarks) > 0 {
  1307  			newVal = newVal.WithMarks(vMarks)
  1308  		}
  1309  
  1310  		return newVal, nil
  1311  	})
  1312  	return ret, nil
  1313  }
  1314  
  1315  // readDataSource handles everything needed to call ReadDataSource on the provider.
  1316  // A previously evaluated configVal can be passed in, or a new one is generated
  1317  // from the resource configuration.
  1318  func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) {
  1319  	var diags tfdiags.Diagnostics
  1320  	var newVal cty.Value
  1321  
  1322  	config := *n.Config
  1323  
  1324  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1325  	diags = diags.Append(err)
  1326  	if diags.HasErrors() {
  1327  		return newVal, diags
  1328  	}
  1329  	if providerSchema == nil {
  1330  		diags = diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1331  		return newVal, diags
  1332  	}
  1333  	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
  1334  	if schema == nil {
  1335  		// Should be caught during validation, so we don't bother with a pretty error here
  1336  		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
  1337  		return newVal, diags
  1338  	}
  1339  
  1340  	metaConfigVal, metaDiags := n.providerMetas(ctx)
  1341  	diags = diags.Append(metaDiags)
  1342  	if diags.HasErrors() {
  1343  		return newVal, diags
  1344  	}
  1345  
  1346  	// Unmark before sending to provider, will re-mark before returning
  1347  	var pvm []cty.PathValueMarks
  1348  	configVal, pvm = configVal.UnmarkDeepWithPaths()
  1349  
  1350  	log.Printf("[TRACE] readDataSource: Re-validating config for %s", n.Addr)
  1351  	validateResp := provider.ValidateDataResourceConfig(
  1352  		providers.ValidateDataResourceConfigRequest{
  1353  			TypeName: n.Addr.ContainingResource().Resource.Type,
  1354  			Config:   configVal,
  1355  		},
  1356  	)
  1357  	diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
  1358  	if diags.HasErrors() {
  1359  		return newVal, diags
  1360  	}
  1361  
  1362  	// If we get down here then our configuration is complete and we're read
  1363  	// to actually call the provider to read the data.
  1364  	log.Printf("[TRACE] readDataSource: %s configuration is complete, so reading from provider", n.Addr)
  1365  
  1366  	resp := provider.ReadDataSource(providers.ReadDataSourceRequest{
  1367  		TypeName:     n.Addr.ContainingResource().Resource.Type,
  1368  		Config:       configVal,
  1369  		ProviderMeta: metaConfigVal,
  1370  	})
  1371  	diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
  1372  	if diags.HasErrors() {
  1373  		return newVal, diags
  1374  	}
  1375  	newVal = resp.State
  1376  	if newVal == cty.NilVal {
  1377  		// This can happen with incompletely-configured mocks. We'll allow it
  1378  		// and treat it as an alias for a properly-typed null value.
  1379  		newVal = cty.NullVal(schema.ImpliedType())
  1380  	}
  1381  
  1382  	for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
  1383  		diags = diags.Append(tfdiags.Sourceless(
  1384  			tfdiags.Error,
  1385  			"Provider produced invalid object",
  1386  			fmt.Sprintf(
  1387  				"Provider %q produced an invalid value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
  1388  				n.ResolvedProvider, tfdiags.FormatErrorPrefixed(err, n.Addr.String()),
  1389  			),
  1390  		))
  1391  	}
  1392  	if diags.HasErrors() {
  1393  		return newVal, diags
  1394  	}
  1395  
  1396  	if newVal.IsNull() {
  1397  		diags = diags.Append(tfdiags.Sourceless(
  1398  			tfdiags.Error,
  1399  			"Provider produced null object",
  1400  			fmt.Sprintf(
  1401  				"Provider %q produced a null value for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
  1402  				n.ResolvedProvider, n.Addr,
  1403  			),
  1404  		))
  1405  	}
  1406  
  1407  	if !newVal.IsNull() && !newVal.IsWhollyKnown() {
  1408  		diags = diags.Append(tfdiags.Sourceless(
  1409  			tfdiags.Error,
  1410  			"Provider produced invalid object",
  1411  			fmt.Sprintf(
  1412  				"Provider %q produced a value for %s that is not wholly known.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
  1413  				n.ResolvedProvider, n.Addr,
  1414  			),
  1415  		))
  1416  
  1417  		// We'll still save the object, but we need to eliminate any unknown
  1418  		// values first because we can't serialize them in the state file.
  1419  		// Note that this may cause set elements to be coalesced if they
  1420  		// differed only by having unknown values, but we don't worry about
  1421  		// that here because we're saving the value only for inspection
  1422  		// purposes; the error we added above will halt the graph walk.
  1423  		newVal = cty.UnknownAsNull(newVal)
  1424  	}
  1425  
  1426  	if len(pvm) > 0 {
  1427  		newVal = newVal.MarkWithPaths(pvm)
  1428  	}
  1429  
  1430  	return newVal, diags
  1431  }
  1432  
  1433  func (n *NodeAbstractResourceInstance) providerMetas(ctx EvalContext) (cty.Value, tfdiags.Diagnostics) {
  1434  	var diags tfdiags.Diagnostics
  1435  	metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
  1436  
  1437  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1438  	if err != nil {
  1439  		return metaConfigVal, diags.Append(err)
  1440  	}
  1441  	if providerSchema == nil {
  1442  		return metaConfigVal, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1443  	}
  1444  	if n.ProviderMetas != nil {
  1445  		if m, ok := n.ProviderMetas[n.ResolvedProvider.Provider]; ok && m != nil {
  1446  			// if the provider doesn't support this feature, throw an error
  1447  			if providerSchema.ProviderMeta == nil {
  1448  				diags = diags.Append(&hcl.Diagnostic{
  1449  					Severity: hcl.DiagError,
  1450  					Summary:  fmt.Sprintf("Provider %s doesn't support provider_meta", n.ResolvedProvider.Provider.String()),
  1451  					Detail:   fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr.Resource),
  1452  					Subject:  &m.ProviderRange,
  1453  				})
  1454  			} else {
  1455  				var configDiags tfdiags.Diagnostics
  1456  				metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, providerSchema.ProviderMeta, nil, EvalDataForNoInstanceKey)
  1457  				diags = diags.Append(configDiags)
  1458  			}
  1459  		}
  1460  	}
  1461  	return metaConfigVal, diags
  1462  }
  1463  
  1464  // planDataSource deals with the main part of the data resource lifecycle:
  1465  // either actually reading from the data source or generating a plan to do so.
  1466  //
  1467  // currentState is the current state for the data source, and the new state is
  1468  // returned. While data sources are read-only, we need to start with the prior
  1469  // state to determine if we have a change or not.  If we needed to read a new
  1470  // value, but it still matches the previous state, then we can record a NoNop
  1471  // change. If the states don't match then we record a Read change so that the
  1472  // new value is applied to the state.
  1473  func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, currentState *states.ResourceInstanceObject) (*plans.ResourceInstanceChange, *states.ResourceInstanceObject, tfdiags.Diagnostics) {
  1474  	var diags tfdiags.Diagnostics
  1475  	var configVal cty.Value
  1476  
  1477  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1478  	if err != nil {
  1479  		return nil, nil, diags.Append(err)
  1480  	}
  1481  	if providerSchema == nil {
  1482  		return nil, nil, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1483  	}
  1484  
  1485  	config := *n.Config
  1486  	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
  1487  	if schema == nil {
  1488  		// Should be caught during validation, so we don't bother with a pretty error here
  1489  		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
  1490  		return nil, nil, diags
  1491  	}
  1492  
  1493  	objTy := schema.ImpliedType()
  1494  	priorVal := cty.NullVal(objTy)
  1495  	if currentState != nil {
  1496  		priorVal = currentState.Value
  1497  	}
  1498  
  1499  	forEach, _ := evaluateForEachExpression(config.ForEach, ctx)
  1500  	keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
  1501  
  1502  	var configDiags tfdiags.Diagnostics
  1503  	configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData)
  1504  	diags = diags.Append(configDiags)
  1505  	if configDiags.HasErrors() {
  1506  		return nil, nil, diags
  1507  	}
  1508  
  1509  	unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
  1510  	// We drop marks on the values used here as the result is only
  1511  	// temporarily used for validation.
  1512  	unmarkedPriorVal, _ := priorVal.UnmarkDeep()
  1513  
  1514  	configKnown := configVal.IsWhollyKnown()
  1515  	// If our configuration contains any unknown values, or we depend on any
  1516  	// unknown values then we must defer the read to the apply phase by
  1517  	// producing a "Read" change for this resource, and a placeholder value for
  1518  	// it in the state.
  1519  	if n.forcePlanReadData(ctx) || !configKnown {
  1520  		if configKnown {
  1521  			log.Printf("[TRACE] planDataSource: %s configuration is fully known, but we're forcing a read plan to be created", n.Addr)
  1522  		} else {
  1523  			log.Printf("[TRACE] planDataSource: %s configuration not fully known yet, so deferring to apply phase", n.Addr)
  1524  		}
  1525  
  1526  		proposedNewVal := objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
  1527  
  1528  		diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1529  			return h.PreDiff(n.Addr, states.CurrentGen, priorVal, proposedNewVal)
  1530  		}))
  1531  		if diags.HasErrors() {
  1532  			return nil, nil, diags
  1533  		}
  1534  		proposedNewVal = proposedNewVal.MarkWithPaths(configMarkPaths)
  1535  
  1536  		// Apply detects that the data source will need to be read by the After
  1537  		// value containing unknowns from PlanDataResourceObject.
  1538  		plannedChange := &plans.ResourceInstanceChange{
  1539  			Addr:         n.Addr,
  1540  			PrevRunAddr:  n.prevRunAddr(ctx),
  1541  			ProviderAddr: n.ResolvedProvider,
  1542  			Change: plans.Change{
  1543  				Action: plans.Read,
  1544  				Before: priorVal,
  1545  				After:  proposedNewVal,
  1546  			},
  1547  		}
  1548  
  1549  		plannedNewState := &states.ResourceInstanceObject{
  1550  			Value:  proposedNewVal,
  1551  			Status: states.ObjectPlanned,
  1552  		}
  1553  
  1554  		diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1555  			return h.PostDiff(n.Addr, states.CurrentGen, plans.Read, priorVal, proposedNewVal)
  1556  		}))
  1557  
  1558  		return plannedChange, plannedNewState, diags
  1559  	}
  1560  
  1561  	// While this isn't a "diff", continue to call this for data sources.
  1562  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1563  		return h.PreDiff(n.Addr, states.CurrentGen, priorVal, configVal)
  1564  	}))
  1565  	if diags.HasErrors() {
  1566  		return nil, nil, diags
  1567  	}
  1568  	// We have a complete configuration with no dependencies to wait on, so we
  1569  	// can read the data source into the state.
  1570  	newVal, readDiags := n.readDataSource(ctx, configVal)
  1571  	diags = diags.Append(readDiags)
  1572  	if diags.HasErrors() {
  1573  		return nil, nil, diags
  1574  	}
  1575  
  1576  	// if we have a prior value, we can check for any irregularities in the response
  1577  	if !priorVal.IsNull() {
  1578  		// While we don't propose planned changes for data sources, we can
  1579  		// generate a proposed value for comparison to ensure the data source
  1580  		// is returning a result following the rules of the provider contract.
  1581  		proposedVal := objchange.ProposedNew(schema, unmarkedPriorVal, unmarkedConfigVal)
  1582  		if errs := objchange.AssertObjectCompatible(schema, proposedVal, newVal); len(errs) > 0 {
  1583  			// Resources have the LegacyTypeSystem field to signal when they are
  1584  			// using an SDK which may not produce precise values. While data
  1585  			// sources are read-only, they can still return a value which is not
  1586  			// compatible with the config+schema. Since we can't detect the legacy
  1587  			// type system, we can only warn about this for now.
  1588  			var buf strings.Builder
  1589  			fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s.",
  1590  				n.ResolvedProvider, n.Addr)
  1591  			for _, err := range errs {
  1592  				fmt.Fprintf(&buf, "\n      - %s", tfdiags.FormatError(err))
  1593  			}
  1594  			log.Print(buf.String())
  1595  		}
  1596  	}
  1597  
  1598  	plannedNewState := &states.ResourceInstanceObject{
  1599  		Value:  newVal,
  1600  		Status: states.ObjectReady,
  1601  	}
  1602  
  1603  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1604  		return h.PostDiff(n.Addr, states.CurrentGen, plans.Update, priorVal, newVal)
  1605  	}))
  1606  	return nil, plannedNewState, diags
  1607  }
  1608  
  1609  // forcePlanReadData determines if we need to override the usual behavior of
  1610  // immediately reading from the data source where possible, instead forcing us
  1611  // to generate a plan.
  1612  func (n *NodeAbstractResourceInstance) forcePlanReadData(ctx EvalContext) bool {
  1613  	nModInst := n.Addr.Module
  1614  	nMod := nModInst.Module()
  1615  
  1616  	// Check and see if any depends_on dependencies have
  1617  	// changes, since they won't show up as changes in the
  1618  	// configuration.
  1619  	changes := ctx.Changes()
  1620  	for _, d := range n.dependsOn {
  1621  		if d.Resource.Mode == addrs.DataResourceMode {
  1622  			// Data sources have no external side effects, so they pose a need
  1623  			// to delay this read. If they do have a change planned, it must be
  1624  			// because of a dependency on a managed resource, in which case
  1625  			// we'll also encounter it in this list of dependencies.
  1626  			continue
  1627  		}
  1628  
  1629  		for _, change := range changes.GetChangesForConfigResource(d) {
  1630  			changeModInst := change.Addr.Module
  1631  			changeMod := changeModInst.Module()
  1632  
  1633  			if changeMod.Equal(nMod) && !changeModInst.Equal(nModInst) {
  1634  				// Dependencies are tracked by configuration address, which
  1635  				// means we may have changes from other instances of parent
  1636  				// modules. The actual reference can only take effect within
  1637  				// the same module instance, so skip any that aren't an exact
  1638  				// match
  1639  				continue
  1640  			}
  1641  
  1642  			if change != nil && change.Action != plans.NoOp {
  1643  				return true
  1644  			}
  1645  		}
  1646  	}
  1647  	return false
  1648  }
  1649  
  1650  // apply deals with the main part of the data resource lifecycle: either
  1651  // actually reading from the data source or generating a plan to do so.
  1652  func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned *plans.ResourceInstanceChange) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
  1653  	var diags tfdiags.Diagnostics
  1654  
  1655  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1656  	if err != nil {
  1657  		return nil, diags.Append(err)
  1658  	}
  1659  	if providerSchema == nil {
  1660  		return nil, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1661  	}
  1662  
  1663  	if planned != nil && planned.Action != plans.Read {
  1664  		// If any other action gets in here then that's always a bug; this
  1665  		// EvalNode only deals with reading.
  1666  		diags = diags.Append(fmt.Errorf(
  1667  			"invalid action %s for %s: only Read is supported (this is a bug in Terraform; please report it!)",
  1668  			planned.Action, n.Addr,
  1669  		))
  1670  		return nil, diags
  1671  	}
  1672  
  1673  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1674  		return h.PreApply(n.Addr, states.CurrentGen, planned.Action, planned.Before, planned.After)
  1675  	}))
  1676  	if diags.HasErrors() {
  1677  		return nil, diags
  1678  	}
  1679  
  1680  	config := *n.Config
  1681  	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
  1682  	if schema == nil {
  1683  		// Should be caught during validation, so we don't bother with a pretty error here
  1684  		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
  1685  		return nil, diags
  1686  	}
  1687  
  1688  	forEach, _ := evaluateForEachExpression(config.ForEach, ctx)
  1689  	keyData := EvalDataForInstanceKey(n.Addr.Resource.Key, forEach)
  1690  
  1691  	configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
  1692  	diags = diags.Append(configDiags)
  1693  	if configDiags.HasErrors() {
  1694  		return nil, diags
  1695  	}
  1696  
  1697  	newVal, readDiags := n.readDataSource(ctx, configVal)
  1698  	diags = diags.Append(readDiags)
  1699  	if diags.HasErrors() {
  1700  		return nil, diags
  1701  	}
  1702  
  1703  	state := &states.ResourceInstanceObject{
  1704  		Value:  newVal,
  1705  		Status: states.ObjectReady,
  1706  	}
  1707  
  1708  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1709  		return h.PostApply(n.Addr, states.CurrentGen, newVal, diags.Err())
  1710  	}))
  1711  
  1712  	return state, diags
  1713  }
  1714  
  1715  // evalApplyProvisioners determines if provisioners need to be run, and if so
  1716  // executes the provisioners for a resource and returns an updated error if
  1717  // provisioning fails.
  1718  func (n *NodeAbstractResourceInstance) evalApplyProvisioners(ctx EvalContext, state *states.ResourceInstanceObject, createNew bool, when configs.ProvisionerWhen) tfdiags.Diagnostics {
  1719  	var diags tfdiags.Diagnostics
  1720  
  1721  	if state == nil {
  1722  		log.Printf("[TRACE] evalApplyProvisioners: %s has no state, so skipping provisioners", n.Addr)
  1723  		return nil
  1724  	}
  1725  	if when == configs.ProvisionerWhenCreate && !createNew {
  1726  		// If we're not creating a new resource, then don't run provisioners
  1727  		log.Printf("[TRACE] evalApplyProvisioners: %s is not freshly-created, so no provisioning is required", n.Addr)
  1728  		return nil
  1729  	}
  1730  	if state.Status == states.ObjectTainted {
  1731  		// No point in provisioning an object that is already tainted, since
  1732  		// it's going to get recreated on the next apply anyway.
  1733  		log.Printf("[TRACE] evalApplyProvisioners: %s is tainted, so skipping provisioning", n.Addr)
  1734  		return nil
  1735  	}
  1736  
  1737  	provs := filterProvisioners(n.Config, when)
  1738  	if len(provs) == 0 {
  1739  		// We have no provisioners, so don't do anything
  1740  		return nil
  1741  	}
  1742  
  1743  	// Call pre hook
  1744  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1745  		return h.PreProvisionInstance(n.Addr, state.Value)
  1746  	}))
  1747  	if diags.HasErrors() {
  1748  		return diags
  1749  	}
  1750  
  1751  	// If there are no errors, then we append it to our output error
  1752  	// if we have one, otherwise we just output it.
  1753  	diags = diags.Append(n.applyProvisioners(ctx, state, when, provs))
  1754  	if diags.HasErrors() {
  1755  		log.Printf("[TRACE] evalApplyProvisioners: %s provisioning failed, but we will continue anyway at the caller's request", n.Addr)
  1756  		return diags
  1757  	}
  1758  
  1759  	// Call post hook
  1760  	return diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1761  		return h.PostProvisionInstance(n.Addr, state.Value)
  1762  	}))
  1763  }
  1764  
  1765  // filterProvisioners filters the provisioners on the resource to only
  1766  // the provisioners specified by the "when" option.
  1767  func filterProvisioners(config *configs.Resource, when configs.ProvisionerWhen) []*configs.Provisioner {
  1768  	// Fast path the zero case
  1769  	if config == nil || config.Managed == nil {
  1770  		return nil
  1771  	}
  1772  
  1773  	if len(config.Managed.Provisioners) == 0 {
  1774  		return nil
  1775  	}
  1776  
  1777  	result := make([]*configs.Provisioner, 0, len(config.Managed.Provisioners))
  1778  	for _, p := range config.Managed.Provisioners {
  1779  		if p.When == when {
  1780  			result = append(result, p)
  1781  		}
  1782  	}
  1783  
  1784  	return result
  1785  }
  1786  
  1787  // applyProvisioners executes the provisioners for a resource.
  1788  func (n *NodeAbstractResourceInstance) applyProvisioners(ctx EvalContext, state *states.ResourceInstanceObject, when configs.ProvisionerWhen, provs []*configs.Provisioner) tfdiags.Diagnostics {
  1789  	var diags tfdiags.Diagnostics
  1790  
  1791  	// this self is only used for destroy provisioner evaluation, and must
  1792  	// refer to the last known value of the resource.
  1793  	self := state.Value
  1794  
  1795  	var evalScope func(EvalContext, hcl.Body, cty.Value, *configschema.Block) (cty.Value, tfdiags.Diagnostics)
  1796  	switch when {
  1797  	case configs.ProvisionerWhenDestroy:
  1798  		evalScope = n.evalDestroyProvisionerConfig
  1799  	default:
  1800  		evalScope = n.evalProvisionerConfig
  1801  	}
  1802  
  1803  	// If there's a connection block defined directly inside the resource block
  1804  	// then it'll serve as a base connection configuration for all of the
  1805  	// provisioners.
  1806  	var baseConn hcl.Body
  1807  	if n.Config.Managed != nil && n.Config.Managed.Connection != nil {
  1808  		baseConn = n.Config.Managed.Connection.Config
  1809  	}
  1810  
  1811  	for _, prov := range provs {
  1812  		log.Printf("[TRACE] applyProvisioners: provisioning %s with %q", n.Addr, prov.Type)
  1813  
  1814  		// Get the provisioner
  1815  		provisioner, err := ctx.Provisioner(prov.Type)
  1816  		if err != nil {
  1817  			return diags.Append(err)
  1818  		}
  1819  
  1820  		schema, err := ctx.ProvisionerSchema(prov.Type)
  1821  		if err != nil {
  1822  			// This error probably won't be a great diagnostic, but in practice
  1823  			// we typically catch this problem long before we get here, so
  1824  			// it should be rare to return via this codepath.
  1825  			diags = diags.Append(err)
  1826  			return diags
  1827  		}
  1828  
  1829  		config, configDiags := evalScope(ctx, prov.Config, self, schema)
  1830  		diags = diags.Append(configDiags)
  1831  		if diags.HasErrors() {
  1832  			return diags
  1833  		}
  1834  
  1835  		// If the provisioner block contains a connection block of its own then
  1836  		// it can override the base connection configuration, if any.
  1837  		var localConn hcl.Body
  1838  		if prov.Connection != nil {
  1839  			localConn = prov.Connection.Config
  1840  		}
  1841  
  1842  		var connBody hcl.Body
  1843  		switch {
  1844  		case baseConn != nil && localConn != nil:
  1845  			// Our standard merging logic applies here, similar to what we do
  1846  			// with _override.tf configuration files: arguments from the
  1847  			// base connection block will be masked by any arguments of the
  1848  			// same name in the local connection block.
  1849  			connBody = configs.MergeBodies(baseConn, localConn)
  1850  		case baseConn != nil:
  1851  			connBody = baseConn
  1852  		case localConn != nil:
  1853  			connBody = localConn
  1854  		}
  1855  
  1856  		// start with an empty connInfo
  1857  		connInfo := cty.NullVal(connectionBlockSupersetSchema.ImpliedType())
  1858  
  1859  		if connBody != nil {
  1860  			var connInfoDiags tfdiags.Diagnostics
  1861  			connInfo, connInfoDiags = evalScope(ctx, connBody, self, connectionBlockSupersetSchema)
  1862  			diags = diags.Append(connInfoDiags)
  1863  			if diags.HasErrors() {
  1864  				return diags
  1865  			}
  1866  		}
  1867  
  1868  		{
  1869  			// Call pre hook
  1870  			err := ctx.Hook(func(h Hook) (HookAction, error) {
  1871  				return h.PreProvisionInstanceStep(n.Addr, prov.Type)
  1872  			})
  1873  			if err != nil {
  1874  				return diags.Append(err)
  1875  			}
  1876  		}
  1877  
  1878  		// The output function
  1879  		outputFn := func(msg string) {
  1880  			ctx.Hook(func(h Hook) (HookAction, error) {
  1881  				h.ProvisionOutput(n.Addr, prov.Type, msg)
  1882  				return HookActionContinue, nil
  1883  			})
  1884  		}
  1885  
  1886  		// If our config or connection info contains any marked values, ensure
  1887  		// those are stripped out before sending to the provisioner. Unlike
  1888  		// resources, we have no need to capture the marked paths and reapply
  1889  		// later.
  1890  		unmarkedConfig, configMarks := config.UnmarkDeep()
  1891  		unmarkedConnInfo, _ := connInfo.UnmarkDeep()
  1892  
  1893  		// Marks on the config might result in leaking sensitive values through
  1894  		// provisioner logging, so we conservatively suppress all output in
  1895  		// this case. This should not apply to connection info values, which
  1896  		// provisioners ought not to be logging anyway.
  1897  		if len(configMarks) > 0 {
  1898  			outputFn = func(msg string) {
  1899  				ctx.Hook(func(h Hook) (HookAction, error) {
  1900  					h.ProvisionOutput(n.Addr, prov.Type, "(output suppressed due to sensitive value in config)")
  1901  					return HookActionContinue, nil
  1902  				})
  1903  			}
  1904  		}
  1905  
  1906  		output := CallbackUIOutput{OutputFn: outputFn}
  1907  		resp := provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{
  1908  			Config:     unmarkedConfig,
  1909  			Connection: unmarkedConnInfo,
  1910  			UIOutput:   &output,
  1911  		})
  1912  		applyDiags := resp.Diagnostics.InConfigBody(prov.Config, n.Addr.String())
  1913  
  1914  		// Call post hook
  1915  		hookErr := ctx.Hook(func(h Hook) (HookAction, error) {
  1916  			return h.PostProvisionInstanceStep(n.Addr, prov.Type, applyDiags.Err())
  1917  		})
  1918  
  1919  		switch prov.OnFailure {
  1920  		case configs.ProvisionerOnFailureContinue:
  1921  			if applyDiags.HasErrors() {
  1922  				log.Printf("[WARN] Errors while provisioning %s with %q, but continuing as requested in configuration", n.Addr, prov.Type)
  1923  			} else {
  1924  				// Maybe there are warnings that we still want to see
  1925  				diags = diags.Append(applyDiags)
  1926  			}
  1927  		default:
  1928  			diags = diags.Append(applyDiags)
  1929  			if applyDiags.HasErrors() {
  1930  				log.Printf("[WARN] Errors while provisioning %s with %q, so aborting", n.Addr, prov.Type)
  1931  				return diags
  1932  			}
  1933  		}
  1934  
  1935  		// Deal with the hook
  1936  		if hookErr != nil {
  1937  			return diags.Append(hookErr)
  1938  		}
  1939  	}
  1940  
  1941  	return diags
  1942  }
  1943  
  1944  func (n *NodeAbstractResourceInstance) evalProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) {
  1945  	var diags tfdiags.Diagnostics
  1946  
  1947  	forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx)
  1948  	diags = diags.Append(forEachDiags)
  1949  
  1950  	keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
  1951  
  1952  	config, _, configDiags := ctx.EvaluateBlock(body, schema, n.ResourceInstanceAddr().Resource, keyData)
  1953  	diags = diags.Append(configDiags)
  1954  
  1955  	return config, diags
  1956  }
  1957  
  1958  // during destroy a provisioner can only evaluate within the scope of the parent resource
  1959  func (n *NodeAbstractResourceInstance) evalDestroyProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) {
  1960  	var diags tfdiags.Diagnostics
  1961  
  1962  	// For a destroy-time provisioner forEach is intentionally nil here,
  1963  	// which EvalDataForInstanceKey responds to by not populating EachValue
  1964  	// in its result. That's okay because each.value is prohibited for
  1965  	// destroy-time provisioners.
  1966  	keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, nil)
  1967  
  1968  	evalScope := ctx.EvaluationScope(n.ResourceInstanceAddr().Resource, keyData)
  1969  	config, evalDiags := evalScope.EvalSelfBlock(body, self, schema, keyData)
  1970  	diags = diags.Append(evalDiags)
  1971  
  1972  	return config, diags
  1973  }
  1974  
  1975  // apply accepts an applyConfig, instead of using n.Config, so destroy plans can
  1976  // send a nil config. Most of the errors generated in apply are returned as
  1977  // diagnostics, but if provider.ApplyResourceChange itself fails, that error is
  1978  // returned as an error and nil diags are returned.
  1979  func (n *NodeAbstractResourceInstance) apply(
  1980  	ctx EvalContext,
  1981  	state *states.ResourceInstanceObject,
  1982  	change *plans.ResourceInstanceChange,
  1983  	applyConfig *configs.Resource,
  1984  	createBeforeDestroy bool) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
  1985  
  1986  	var diags tfdiags.Diagnostics
  1987  	if state == nil {
  1988  		state = &states.ResourceInstanceObject{}
  1989  	}
  1990  
  1991  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1992  	if err != nil {
  1993  		return nil, diags.Append(err)
  1994  	}
  1995  	schema, _ := providerSchema.SchemaForResourceType(n.Addr.Resource.Resource.Mode, n.Addr.Resource.Resource.Type)
  1996  	if schema == nil {
  1997  		// Should be caught during validation, so we don't bother with a pretty error here
  1998  		diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type))
  1999  		return nil, diags
  2000  	}
  2001  
  2002  	log.Printf("[INFO] Starting apply for %s", n.Addr)
  2003  
  2004  	configVal := cty.NullVal(cty.DynamicPseudoType)
  2005  	if applyConfig != nil {
  2006  		var configDiags tfdiags.Diagnostics
  2007  		forEach, _ := evaluateForEachExpression(applyConfig.ForEach, ctx)
  2008  		keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
  2009  		configVal, _, configDiags = ctx.EvaluateBlock(applyConfig.Config, schema, nil, keyData)
  2010  		diags = diags.Append(configDiags)
  2011  		if configDiags.HasErrors() {
  2012  			return nil, diags
  2013  		}
  2014  	}
  2015  
  2016  	if !configVal.IsWhollyKnown() {
  2017  		diags = diags.Append(fmt.Errorf(
  2018  			"configuration for %s still contains unknown values during apply (this is a bug in Terraform; please report it!)",
  2019  			n.Addr,
  2020  		))
  2021  		return nil, diags
  2022  	}
  2023  
  2024  	metaConfigVal, metaDiags := n.providerMetas(ctx)
  2025  	diags = diags.Append(metaDiags)
  2026  	if diags.HasErrors() {
  2027  		return nil, diags
  2028  	}
  2029  
  2030  	log.Printf("[DEBUG] %s: applying the planned %s change", n.Addr, change.Action)
  2031  
  2032  	// If our config, Before or After value contain any marked values,
  2033  	// ensure those are stripped out before sending
  2034  	// this to the provider
  2035  	unmarkedConfigVal, _ := configVal.UnmarkDeep()
  2036  	unmarkedBefore, beforePaths := change.Before.UnmarkDeepWithPaths()
  2037  	unmarkedAfter, afterPaths := change.After.UnmarkDeepWithPaths()
  2038  
  2039  	// If we have an Update action, our before and after values are equal,
  2040  	// and only differ on their sensitivity, the newVal is the after val
  2041  	// and we should not communicate with the provider. We do need to update
  2042  	// the state with this new value, to ensure the sensitivity change is
  2043  	// persisted.
  2044  	eqV := unmarkedBefore.Equals(unmarkedAfter)
  2045  	eq := eqV.IsKnown() && eqV.True()
  2046  	if change.Action == plans.Update && eq && !marksEqual(beforePaths, afterPaths) {
  2047  		// Copy the previous state, changing only the value
  2048  		newState := &states.ResourceInstanceObject{
  2049  			CreateBeforeDestroy: state.CreateBeforeDestroy,
  2050  			Dependencies:        state.Dependencies,
  2051  			Private:             state.Private,
  2052  			Status:              state.Status,
  2053  			Value:               change.After,
  2054  		}
  2055  		return newState, diags
  2056  	}
  2057  
  2058  	resp := provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{
  2059  		TypeName:       n.Addr.Resource.Resource.Type,
  2060  		PriorState:     unmarkedBefore,
  2061  		Config:         unmarkedConfigVal,
  2062  		PlannedState:   unmarkedAfter,
  2063  		PlannedPrivate: change.Private,
  2064  		ProviderMeta:   metaConfigVal,
  2065  	})
  2066  	applyDiags := resp.Diagnostics
  2067  	if applyConfig != nil {
  2068  		applyDiags = applyDiags.InConfigBody(applyConfig.Config, n.Addr.String())
  2069  	}
  2070  	diags = diags.Append(applyDiags)
  2071  
  2072  	// Even if there are errors in the returned diagnostics, the provider may
  2073  	// have returned a _partial_ state for an object that already exists but
  2074  	// failed to fully configure, and so the remaining code must always run
  2075  	// to completion but must be defensive against the new value being
  2076  	// incomplete.
  2077  	newVal := resp.NewState
  2078  
  2079  	// If we have paths to mark, mark those on this new value
  2080  	if len(afterPaths) > 0 {
  2081  		newVal = newVal.MarkWithPaths(afterPaths)
  2082  	}
  2083  
  2084  	if newVal == cty.NilVal {
  2085  		// Providers are supposed to return a partial new value even when errors
  2086  		// occur, but sometimes they don't and so in that case we'll patch that up
  2087  		// by just using the prior state, so we'll at least keep track of the
  2088  		// object for the user to retry.
  2089  		newVal = change.Before
  2090  
  2091  		// As a special case, we'll set the new value to null if it looks like
  2092  		// we were trying to execute a delete, because the provider in this case
  2093  		// probably left the newVal unset intending it to be interpreted as "null".
  2094  		if change.After.IsNull() {
  2095  			newVal = cty.NullVal(schema.ImpliedType())
  2096  		}
  2097  
  2098  		if !diags.HasErrors() {
  2099  			diags = diags.Append(tfdiags.Sourceless(
  2100  				tfdiags.Error,
  2101  				"Provider produced invalid object",
  2102  				fmt.Sprintf(
  2103  					"Provider %q produced an invalid nil value after apply for %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
  2104  					n.ResolvedProvider.String(), n.Addr.String(),
  2105  				),
  2106  			))
  2107  		}
  2108  	}
  2109  
  2110  	var conformDiags tfdiags.Diagnostics
  2111  	for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
  2112  		conformDiags = conformDiags.Append(tfdiags.Sourceless(
  2113  			tfdiags.Error,
  2114  			"Provider produced invalid object",
  2115  			fmt.Sprintf(
  2116  				"Provider %q produced an invalid value after apply for %s. The result cannot not be saved in the Terraform state.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
  2117  				n.ResolvedProvider.String(), tfdiags.FormatErrorPrefixed(err, n.Addr.String()),
  2118  			),
  2119  		))
  2120  	}
  2121  	diags = diags.Append(conformDiags)
  2122  	if conformDiags.HasErrors() {
  2123  		// Bail early in this particular case, because an object that doesn't
  2124  		// conform to the schema can't be saved in the state anyway -- the
  2125  		// serializer will reject it.
  2126  		return nil, diags
  2127  	}
  2128  
  2129  	// After this point we have a type-conforming result object and so we
  2130  	// must always run to completion to ensure it can be saved. If n.Error
  2131  	// is set then we must not return a non-nil error, in order to allow
  2132  	// evaluation to continue to a later point where our state object will
  2133  	// be saved.
  2134  
  2135  	// By this point there must not be any unknown values remaining in our
  2136  	// object, because we've applied the change and we can't save unknowns
  2137  	// in our persistent state. If any are present then we will indicate an
  2138  	// error (which is always a bug in the provider) but we will also replace
  2139  	// them with nulls so that we can successfully save the portions of the
  2140  	// returned value that are known.
  2141  	if !newVal.IsWhollyKnown() {
  2142  		// To generate better error messages, we'll go for a walk through the
  2143  		// value and make a separate diagnostic for each unknown value we
  2144  		// find.
  2145  		cty.Walk(newVal, func(path cty.Path, val cty.Value) (bool, error) {
  2146  			if !val.IsKnown() {
  2147  				pathStr := tfdiags.FormatCtyPath(path)
  2148  				diags = diags.Append(tfdiags.Sourceless(
  2149  					tfdiags.Error,
  2150  					"Provider returned invalid result object after apply",
  2151  					fmt.Sprintf(
  2152  						"After the apply operation, the provider still indicated an unknown value for %s%s. All values must be known after apply, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save the other known object values in the state.",
  2153  						n.Addr, pathStr,
  2154  					),
  2155  				))
  2156  			}
  2157  			return true, nil
  2158  		})
  2159  
  2160  		// NOTE: This operation can potentially be lossy if there are multiple
  2161  		// elements in a set that differ only by unknown values: after
  2162  		// replacing with null these will be merged together into a single set
  2163  		// element. Since we can only get here in the presence of a provider
  2164  		// bug, we accept this because storing a result here is always a
  2165  		// best-effort sort of thing.
  2166  		newVal = cty.UnknownAsNull(newVal)
  2167  	}
  2168  
  2169  	if change.Action != plans.Delete && !diags.HasErrors() {
  2170  		// Only values that were marked as unknown in the planned value are allowed
  2171  		// to change during the apply operation. (We do this after the unknown-ness
  2172  		// check above so that we also catch anything that became unknown after
  2173  		// being known during plan.)
  2174  		//
  2175  		// If we are returning other errors anyway then we'll give this
  2176  		// a pass since the other errors are usually the explanation for
  2177  		// this one and so it's more helpful to let the user focus on the
  2178  		// root cause rather than distract with this extra problem.
  2179  		if errs := objchange.AssertObjectCompatible(schema, change.After, newVal); len(errs) > 0 {
  2180  			if resp.LegacyTypeSystem {
  2181  				// The shimming of the old type system in the legacy SDK is not precise
  2182  				// enough to pass this consistency check, so we'll give it a pass here,
  2183  				// but we will generate a warning about it so that we are more likely
  2184  				// to notice in the logs if an inconsistency beyond the type system
  2185  				// leads to a downstream provider failure.
  2186  				var buf strings.Builder
  2187  				fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s, but we are tolerating it because it is using the legacy plugin SDK.\n    The following problems may be the cause of any confusing errors from downstream operations:", n.ResolvedProvider.String(), n.Addr)
  2188  				for _, err := range errs {
  2189  					fmt.Fprintf(&buf, "\n      - %s", tfdiags.FormatError(err))
  2190  				}
  2191  				log.Print(buf.String())
  2192  
  2193  				// The sort of inconsistency we won't catch here is if a known value
  2194  				// in the plan is changed during apply. That can cause downstream
  2195  				// problems because a dependent resource would make its own plan based
  2196  				// on the planned value, and thus get a different result during the
  2197  				// apply phase. This will usually lead to a "Provider produced invalid plan"
  2198  				// error that incorrectly blames the downstream resource for the change.
  2199  
  2200  			} else {
  2201  				for _, err := range errs {
  2202  					diags = diags.Append(tfdiags.Sourceless(
  2203  						tfdiags.Error,
  2204  						"Provider produced inconsistent result after apply",
  2205  						fmt.Sprintf(
  2206  							"When applying changes to %s, provider %q produced an unexpected new value: %s.\n\nThis is a bug in the provider, which should be reported in the provider's own issue tracker.",
  2207  							n.Addr, n.ResolvedProvider.String(), tfdiags.FormatError(err),
  2208  						),
  2209  					))
  2210  				}
  2211  			}
  2212  		}
  2213  	}
  2214  
  2215  	// If a provider returns a null or non-null object at the wrong time then
  2216  	// we still want to save that but it often causes some confusing behaviors
  2217  	// where it seems like Terraform is failing to take any action at all,
  2218  	// so we'll generate some errors to draw attention to it.
  2219  	if !diags.HasErrors() {
  2220  		if change.Action == plans.Delete && !newVal.IsNull() {
  2221  			diags = diags.Append(tfdiags.Sourceless(
  2222  				tfdiags.Error,
  2223  				"Provider returned invalid result object after apply",
  2224  				fmt.Sprintf(
  2225  					"After applying a %s plan, the provider returned a non-null object for %s. Destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository. Terraform will still save this errant object in the state for debugging and recovery.",
  2226  					change.Action, n.Addr,
  2227  				),
  2228  			))
  2229  		}
  2230  		if change.Action != plans.Delete && newVal.IsNull() {
  2231  			diags = diags.Append(tfdiags.Sourceless(
  2232  				tfdiags.Error,
  2233  				"Provider returned invalid result object after apply",
  2234  				fmt.Sprintf(
  2235  					"After applying a %s plan, the provider returned a null object for %s. Only destroying should always produce a null value, so this is always a bug in the provider and should be reported in the provider's own repository.",
  2236  					change.Action, n.Addr,
  2237  				),
  2238  			))
  2239  		}
  2240  	}
  2241  
  2242  	switch {
  2243  	case diags.HasErrors() && newVal.IsNull():
  2244  		// Sometimes providers return a null value when an operation fails for
  2245  		// some reason, but we'd rather keep the prior state so that the error
  2246  		// can be corrected on a subsequent run. We must only do this for null
  2247  		// new value though, or else we may discard partial updates the
  2248  		// provider was able to complete. Otherwise, we'll continue using the
  2249  		// prior state as the new value, making this effectively a no-op.  If
  2250  		// the item really _has_ been deleted then our next refresh will detect
  2251  		// that and fix it up.
  2252  		return state.DeepCopy(), diags
  2253  
  2254  	case diags.HasErrors() && !newVal.IsNull():
  2255  		// if we have an error, make sure we restore the object status in the new state
  2256  		newState := &states.ResourceInstanceObject{
  2257  			Status:              state.Status,
  2258  			Value:               newVal,
  2259  			Private:             resp.Private,
  2260  			CreateBeforeDestroy: createBeforeDestroy,
  2261  		}
  2262  
  2263  		// if the resource was being deleted, the dependencies are not going to
  2264  		// be recalculated and we need to restore those as well.
  2265  		if change.Action == plans.Delete {
  2266  			newState.Dependencies = state.Dependencies
  2267  		}
  2268  
  2269  		return newState, diags
  2270  
  2271  	case !newVal.IsNull():
  2272  		// Non error case with a new state
  2273  		newState := &states.ResourceInstanceObject{
  2274  			Status:              states.ObjectReady,
  2275  			Value:               newVal,
  2276  			Private:             resp.Private,
  2277  			CreateBeforeDestroy: createBeforeDestroy,
  2278  		}
  2279  		return newState, diags
  2280  
  2281  	default:
  2282  		// Non error case, were the object was deleted
  2283  		return nil, diags
  2284  	}
  2285  }
  2286  
  2287  func (n *NodeAbstractResourceInstance) prevRunAddr(ctx EvalContext) addrs.AbsResourceInstance {
  2288  	return resourceInstancePrevRunAddr(ctx, n.Addr)
  2289  }
  2290  
  2291  func resourceInstancePrevRunAddr(ctx EvalContext, currentAddr addrs.AbsResourceInstance) addrs.AbsResourceInstance {
  2292  	table := ctx.MoveResults()
  2293  	return table.OldAddr(currentAddr)
  2294  }