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