github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-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/muratcelep/terraform/not-internal/addrs"
    10  	"github.com/muratcelep/terraform/not-internal/configs"
    11  	"github.com/muratcelep/terraform/not-internal/configs/configschema"
    12  	"github.com/muratcelep/terraform/not-internal/plans"
    13  	"github.com/muratcelep/terraform/not-internal/plans/objchange"
    14  	"github.com/muratcelep/terraform/not-internal/providers"
    15  	"github.com/muratcelep/terraform/not-internal/provisioners"
    16  	"github.com/muratcelep/terraform/not-internal/states"
    17  	"github.com/muratcelep/terraform/not-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  		switch {
  1293  		case len(configMap) > 0:
  1294  			newVal = cty.MapVal(configMap)
  1295  		case v.IsNull():
  1296  			// if the config value was null, and no values remain in the map,
  1297  			// reset the value to null.
  1298  			newVal = v
  1299  		default:
  1300  			newVal = cty.MapValEmpty(v.Type().ElementType())
  1301  		}
  1302  
  1303  		if len(vMarks) > 0 {
  1304  			newVal = newVal.WithMarks(vMarks)
  1305  		}
  1306  
  1307  		return newVal, nil
  1308  	})
  1309  	return ret, nil
  1310  }
  1311  
  1312  // readDataSource handles everything needed to call ReadDataSource on the provider.
  1313  // A previously evaluated configVal can be passed in, or a new one is generated
  1314  // from the resource configuration.
  1315  func (n *NodeAbstractResourceInstance) readDataSource(ctx EvalContext, configVal cty.Value) (cty.Value, tfdiags.Diagnostics) {
  1316  	var diags tfdiags.Diagnostics
  1317  	var newVal cty.Value
  1318  
  1319  	config := *n.Config
  1320  
  1321  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1322  	diags = diags.Append(err)
  1323  	if diags.HasErrors() {
  1324  		return newVal, diags
  1325  	}
  1326  	if providerSchema == nil {
  1327  		diags = diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1328  		return newVal, diags
  1329  	}
  1330  	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
  1331  	if schema == nil {
  1332  		// Should be caught during validation, so we don't bother with a pretty error here
  1333  		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
  1334  		return newVal, diags
  1335  	}
  1336  
  1337  	metaConfigVal, metaDiags := n.providerMetas(ctx)
  1338  	diags = diags.Append(metaDiags)
  1339  	if diags.HasErrors() {
  1340  		return newVal, diags
  1341  	}
  1342  
  1343  	// Unmark before sending to provider, will re-mark before returning
  1344  	var pvm []cty.PathValueMarks
  1345  	configVal, pvm = configVal.UnmarkDeepWithPaths()
  1346  
  1347  	log.Printf("[TRACE] readDataSource: Re-validating config for %s", n.Addr)
  1348  	validateResp := provider.ValidateDataResourceConfig(
  1349  		providers.ValidateDataResourceConfigRequest{
  1350  			TypeName: n.Addr.ContainingResource().Resource.Type,
  1351  			Config:   configVal,
  1352  		},
  1353  	)
  1354  	diags = diags.Append(validateResp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
  1355  	if diags.HasErrors() {
  1356  		return newVal, diags
  1357  	}
  1358  
  1359  	// If we get down here then our configuration is complete and we're read
  1360  	// to actually call the provider to read the data.
  1361  	log.Printf("[TRACE] readDataSource: %s configuration is complete, so reading from provider", n.Addr)
  1362  
  1363  	resp := provider.ReadDataSource(providers.ReadDataSourceRequest{
  1364  		TypeName:     n.Addr.ContainingResource().Resource.Type,
  1365  		Config:       configVal,
  1366  		ProviderMeta: metaConfigVal,
  1367  	})
  1368  	diags = diags.Append(resp.Diagnostics.InConfigBody(config.Config, n.Addr.String()))
  1369  	if diags.HasErrors() {
  1370  		return newVal, diags
  1371  	}
  1372  	newVal = resp.State
  1373  	if newVal == cty.NilVal {
  1374  		// This can happen with incompletely-configured mocks. We'll allow it
  1375  		// and treat it as an alias for a properly-typed null value.
  1376  		newVal = cty.NullVal(schema.ImpliedType())
  1377  	}
  1378  
  1379  	for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
  1380  		diags = diags.Append(tfdiags.Sourceless(
  1381  			tfdiags.Error,
  1382  			"Provider produced invalid object",
  1383  			fmt.Sprintf(
  1384  				"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.",
  1385  				n.ResolvedProvider, tfdiags.FormatErrorPrefixed(err, n.Addr.String()),
  1386  			),
  1387  		))
  1388  	}
  1389  	if diags.HasErrors() {
  1390  		return newVal, diags
  1391  	}
  1392  
  1393  	if newVal.IsNull() {
  1394  		diags = diags.Append(tfdiags.Sourceless(
  1395  			tfdiags.Error,
  1396  			"Provider produced null object",
  1397  			fmt.Sprintf(
  1398  				"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.",
  1399  				n.ResolvedProvider, n.Addr,
  1400  			),
  1401  		))
  1402  	}
  1403  
  1404  	if !newVal.IsNull() && !newVal.IsWhollyKnown() {
  1405  		diags = diags.Append(tfdiags.Sourceless(
  1406  			tfdiags.Error,
  1407  			"Provider produced invalid object",
  1408  			fmt.Sprintf(
  1409  				"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.",
  1410  				n.ResolvedProvider, n.Addr,
  1411  			),
  1412  		))
  1413  
  1414  		// We'll still save the object, but we need to eliminate any unknown
  1415  		// values first because we can't serialize them in the state file.
  1416  		// Note that this may cause set elements to be coalesced if they
  1417  		// differed only by having unknown values, but we don't worry about
  1418  		// that here because we're saving the value only for inspection
  1419  		// purposes; the error we added above will halt the graph walk.
  1420  		newVal = cty.UnknownAsNull(newVal)
  1421  	}
  1422  
  1423  	if len(pvm) > 0 {
  1424  		newVal = newVal.MarkWithPaths(pvm)
  1425  	}
  1426  
  1427  	return newVal, diags
  1428  }
  1429  
  1430  func (n *NodeAbstractResourceInstance) providerMetas(ctx EvalContext) (cty.Value, tfdiags.Diagnostics) {
  1431  	var diags tfdiags.Diagnostics
  1432  	metaConfigVal := cty.NullVal(cty.DynamicPseudoType)
  1433  
  1434  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1435  	if err != nil {
  1436  		return metaConfigVal, diags.Append(err)
  1437  	}
  1438  	if providerSchema == nil {
  1439  		return metaConfigVal, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1440  	}
  1441  	if n.ProviderMetas != nil {
  1442  		if m, ok := n.ProviderMetas[n.ResolvedProvider.Provider]; ok && m != nil {
  1443  			// if the provider doesn't support this feature, throw an error
  1444  			if providerSchema.ProviderMeta == nil {
  1445  				diags = diags.Append(&hcl.Diagnostic{
  1446  					Severity: hcl.DiagError,
  1447  					Summary:  fmt.Sprintf("Provider %s doesn't support provider_meta", n.ResolvedProvider.Provider.String()),
  1448  					Detail:   fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr.Resource),
  1449  					Subject:  &m.ProviderRange,
  1450  				})
  1451  			} else {
  1452  				var configDiags tfdiags.Diagnostics
  1453  				metaConfigVal, _, configDiags = ctx.EvaluateBlock(m.Config, providerSchema.ProviderMeta, nil, EvalDataForNoInstanceKey)
  1454  				diags = diags.Append(configDiags)
  1455  			}
  1456  		}
  1457  	}
  1458  	return metaConfigVal, diags
  1459  }
  1460  
  1461  // planDataSource deals with the main part of the data resource lifecycle:
  1462  // either actually reading from the data source or generating a plan to do so.
  1463  //
  1464  // currentState is the current state for the data source, and the new state is
  1465  // returned. While data sources are read-only, we need to start with the prior
  1466  // state to determine if we have a change or not.  If we needed to read a new
  1467  // value, but it still matches the previous state, then we can record a NoNop
  1468  // change. If the states don't match then we record a Read change so that the
  1469  // new value is applied to the state.
  1470  func (n *NodeAbstractResourceInstance) planDataSource(ctx EvalContext, currentState *states.ResourceInstanceObject) (*plans.ResourceInstanceChange, *states.ResourceInstanceObject, tfdiags.Diagnostics) {
  1471  	var diags tfdiags.Diagnostics
  1472  	var configVal cty.Value
  1473  
  1474  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1475  	if err != nil {
  1476  		return nil, nil, diags.Append(err)
  1477  	}
  1478  	if providerSchema == nil {
  1479  		return nil, nil, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1480  	}
  1481  
  1482  	config := *n.Config
  1483  	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
  1484  	if schema == nil {
  1485  		// Should be caught during validation, so we don't bother with a pretty error here
  1486  		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
  1487  		return nil, nil, diags
  1488  	}
  1489  
  1490  	objTy := schema.ImpliedType()
  1491  	priorVal := cty.NullVal(objTy)
  1492  	if currentState != nil {
  1493  		priorVal = currentState.Value
  1494  	}
  1495  
  1496  	forEach, _ := evaluateForEachExpression(config.ForEach, ctx)
  1497  	keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
  1498  
  1499  	var configDiags tfdiags.Diagnostics
  1500  	configVal, _, configDiags = ctx.EvaluateBlock(config.Config, schema, nil, keyData)
  1501  	diags = diags.Append(configDiags)
  1502  	if configDiags.HasErrors() {
  1503  		return nil, nil, diags
  1504  	}
  1505  
  1506  	unmarkedConfigVal, configMarkPaths := configVal.UnmarkDeepWithPaths()
  1507  	// We drop marks on the values used here as the result is only
  1508  	// temporarily used for validation.
  1509  	unmarkedPriorVal, _ := priorVal.UnmarkDeep()
  1510  
  1511  	configKnown := configVal.IsWhollyKnown()
  1512  	// If our configuration contains any unknown values, or we depend on any
  1513  	// unknown values then we must defer the read to the apply phase by
  1514  	// producing a "Read" change for this resource, and a placeholder value for
  1515  	// it in the state.
  1516  	if n.forcePlanReadData(ctx) || !configKnown {
  1517  		if configKnown {
  1518  			log.Printf("[TRACE] planDataSource: %s configuration is fully known, but we're forcing a read plan to be created", n.Addr)
  1519  		} else {
  1520  			log.Printf("[TRACE] planDataSource: %s configuration not fully known yet, so deferring to apply phase", n.Addr)
  1521  		}
  1522  
  1523  		proposedNewVal := objchange.PlannedDataResourceObject(schema, unmarkedConfigVal)
  1524  
  1525  		diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1526  			return h.PreDiff(n.Addr, states.CurrentGen, priorVal, proposedNewVal)
  1527  		}))
  1528  		if diags.HasErrors() {
  1529  			return nil, nil, diags
  1530  		}
  1531  		proposedNewVal = proposedNewVal.MarkWithPaths(configMarkPaths)
  1532  
  1533  		// Apply detects that the data source will need to be read by the After
  1534  		// value containing unknowns from PlanDataResourceObject.
  1535  		plannedChange := &plans.ResourceInstanceChange{
  1536  			Addr:         n.Addr,
  1537  			PrevRunAddr:  n.prevRunAddr(ctx),
  1538  			ProviderAddr: n.ResolvedProvider,
  1539  			Change: plans.Change{
  1540  				Action: plans.Read,
  1541  				Before: priorVal,
  1542  				After:  proposedNewVal,
  1543  			},
  1544  		}
  1545  
  1546  		plannedNewState := &states.ResourceInstanceObject{
  1547  			Value:  proposedNewVal,
  1548  			Status: states.ObjectPlanned,
  1549  		}
  1550  
  1551  		diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1552  			return h.PostDiff(n.Addr, states.CurrentGen, plans.Read, priorVal, proposedNewVal)
  1553  		}))
  1554  
  1555  		return plannedChange, plannedNewState, diags
  1556  	}
  1557  
  1558  	// While this isn't a "diff", continue to call this for data sources.
  1559  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1560  		return h.PreDiff(n.Addr, states.CurrentGen, priorVal, configVal)
  1561  	}))
  1562  	if diags.HasErrors() {
  1563  		return nil, nil, diags
  1564  	}
  1565  	// We have a complete configuration with no dependencies to wait on, so we
  1566  	// can read the data source into the state.
  1567  	newVal, readDiags := n.readDataSource(ctx, configVal)
  1568  	diags = diags.Append(readDiags)
  1569  	if diags.HasErrors() {
  1570  		return nil, nil, diags
  1571  	}
  1572  
  1573  	// if we have a prior value, we can check for any irregularities in the response
  1574  	if !priorVal.IsNull() {
  1575  		// While we don't propose planned changes for data sources, we can
  1576  		// generate a proposed value for comparison to ensure the data source
  1577  		// is returning a result following the rules of the provider contract.
  1578  		proposedVal := objchange.ProposedNew(schema, unmarkedPriorVal, unmarkedConfigVal)
  1579  		if errs := objchange.AssertObjectCompatible(schema, proposedVal, newVal); len(errs) > 0 {
  1580  			// Resources have the LegacyTypeSystem field to signal when they are
  1581  			// using an SDK which may not produce precise values. While data
  1582  			// sources are read-only, they can still return a value which is not
  1583  			// compatible with the config+schema. Since we can't detect the legacy
  1584  			// type system, we can only warn about this for now.
  1585  			var buf strings.Builder
  1586  			fmt.Fprintf(&buf, "[WARN] Provider %q produced an unexpected new value for %s.",
  1587  				n.ResolvedProvider, n.Addr)
  1588  			for _, err := range errs {
  1589  				fmt.Fprintf(&buf, "\n      - %s", tfdiags.FormatError(err))
  1590  			}
  1591  			log.Print(buf.String())
  1592  		}
  1593  	}
  1594  
  1595  	plannedNewState := &states.ResourceInstanceObject{
  1596  		Value:  newVal,
  1597  		Status: states.ObjectReady,
  1598  	}
  1599  
  1600  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1601  		return h.PostDiff(n.Addr, states.CurrentGen, plans.Update, priorVal, newVal)
  1602  	}))
  1603  	return nil, plannedNewState, diags
  1604  }
  1605  
  1606  // forcePlanReadData determines if we need to override the usual behavior of
  1607  // immediately reading from the data source where possible, instead forcing us
  1608  // to generate a plan.
  1609  func (n *NodeAbstractResourceInstance) forcePlanReadData(ctx EvalContext) bool {
  1610  	nModInst := n.Addr.Module
  1611  	nMod := nModInst.Module()
  1612  
  1613  	// Check and see if any depends_on dependencies have
  1614  	// changes, since they won't show up as changes in the
  1615  	// configuration.
  1616  	changes := ctx.Changes()
  1617  	for _, d := range n.dependsOn {
  1618  		if d.Resource.Mode == addrs.DataResourceMode {
  1619  			// Data sources have no external side effects, so they pose a need
  1620  			// to delay this read. If they do have a change planned, it must be
  1621  			// because of a dependency on a managed resource, in which case
  1622  			// we'll also encounter it in this list of dependencies.
  1623  			continue
  1624  		}
  1625  
  1626  		for _, change := range changes.GetChangesForConfigResource(d) {
  1627  			changeModInst := change.Addr.Module
  1628  			changeMod := changeModInst.Module()
  1629  
  1630  			if changeMod.Equal(nMod) && !changeModInst.Equal(nModInst) {
  1631  				// Dependencies are tracked by configuration address, which
  1632  				// means we may have changes from other instances of parent
  1633  				// modules. The actual reference can only take effect within
  1634  				// the same module instance, so skip any that aren't an exact
  1635  				// match
  1636  				continue
  1637  			}
  1638  
  1639  			if change != nil && change.Action != plans.NoOp {
  1640  				return true
  1641  			}
  1642  		}
  1643  	}
  1644  	return false
  1645  }
  1646  
  1647  // apply deals with the main part of the data resource lifecycle: either
  1648  // actually reading from the data source or generating a plan to do so.
  1649  func (n *NodeAbstractResourceInstance) applyDataSource(ctx EvalContext, planned *plans.ResourceInstanceChange) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
  1650  	var diags tfdiags.Diagnostics
  1651  
  1652  	_, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1653  	if err != nil {
  1654  		return nil, diags.Append(err)
  1655  	}
  1656  	if providerSchema == nil {
  1657  		return nil, diags.Append(fmt.Errorf("provider schema not available for %s", n.Addr))
  1658  	}
  1659  
  1660  	if planned != nil && planned.Action != plans.Read {
  1661  		// If any other action gets in here then that's always a bug; this
  1662  		// EvalNode only deals with reading.
  1663  		diags = diags.Append(fmt.Errorf(
  1664  			"invalid action %s for %s: only Read is supported (this is a bug in Terraform; please report it!)",
  1665  			planned.Action, n.Addr,
  1666  		))
  1667  		return nil, diags
  1668  	}
  1669  
  1670  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1671  		return h.PreApply(n.Addr, states.CurrentGen, planned.Action, planned.Before, planned.After)
  1672  	}))
  1673  	if diags.HasErrors() {
  1674  		return nil, diags
  1675  	}
  1676  
  1677  	config := *n.Config
  1678  	schema, _ := providerSchema.SchemaForResourceAddr(n.Addr.ContainingResource().Resource)
  1679  	if schema == nil {
  1680  		// Should be caught during validation, so we don't bother with a pretty error here
  1681  		diags = diags.Append(fmt.Errorf("provider %q does not support data source %q", n.ResolvedProvider, n.Addr.ContainingResource().Resource.Type))
  1682  		return nil, diags
  1683  	}
  1684  
  1685  	forEach, _ := evaluateForEachExpression(config.ForEach, ctx)
  1686  	keyData := EvalDataForInstanceKey(n.Addr.Resource.Key, forEach)
  1687  
  1688  	configVal, _, configDiags := ctx.EvaluateBlock(config.Config, schema, nil, keyData)
  1689  	diags = diags.Append(configDiags)
  1690  	if configDiags.HasErrors() {
  1691  		return nil, diags
  1692  	}
  1693  
  1694  	newVal, readDiags := n.readDataSource(ctx, configVal)
  1695  	diags = diags.Append(readDiags)
  1696  	if diags.HasErrors() {
  1697  		return nil, diags
  1698  	}
  1699  
  1700  	state := &states.ResourceInstanceObject{
  1701  		Value:  newVal,
  1702  		Status: states.ObjectReady,
  1703  	}
  1704  
  1705  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1706  		return h.PostApply(n.Addr, states.CurrentGen, newVal, diags.Err())
  1707  	}))
  1708  
  1709  	return state, diags
  1710  }
  1711  
  1712  // evalApplyProvisioners determines if provisioners need to be run, and if so
  1713  // executes the provisioners for a resource and returns an updated error if
  1714  // provisioning fails.
  1715  func (n *NodeAbstractResourceInstance) evalApplyProvisioners(ctx EvalContext, state *states.ResourceInstanceObject, createNew bool, when configs.ProvisionerWhen) tfdiags.Diagnostics {
  1716  	var diags tfdiags.Diagnostics
  1717  
  1718  	if state == nil {
  1719  		log.Printf("[TRACE] evalApplyProvisioners: %s has no state, so skipping provisioners", n.Addr)
  1720  		return nil
  1721  	}
  1722  	if when == configs.ProvisionerWhenCreate && !createNew {
  1723  		// If we're not creating a new resource, then don't run provisioners
  1724  		log.Printf("[TRACE] evalApplyProvisioners: %s is not freshly-created, so no provisioning is required", n.Addr)
  1725  		return nil
  1726  	}
  1727  	if state.Status == states.ObjectTainted {
  1728  		// No point in provisioning an object that is already tainted, since
  1729  		// it's going to get recreated on the next apply anyway.
  1730  		log.Printf("[TRACE] evalApplyProvisioners: %s is tainted, so skipping provisioning", n.Addr)
  1731  		return nil
  1732  	}
  1733  
  1734  	provs := filterProvisioners(n.Config, when)
  1735  	if len(provs) == 0 {
  1736  		// We have no provisioners, so don't do anything
  1737  		return nil
  1738  	}
  1739  
  1740  	// Call pre hook
  1741  	diags = diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1742  		return h.PreProvisionInstance(n.Addr, state.Value)
  1743  	}))
  1744  	if diags.HasErrors() {
  1745  		return diags
  1746  	}
  1747  
  1748  	// If there are no errors, then we append it to our output error
  1749  	// if we have one, otherwise we just output it.
  1750  	diags = diags.Append(n.applyProvisioners(ctx, state, when, provs))
  1751  	if diags.HasErrors() {
  1752  		log.Printf("[TRACE] evalApplyProvisioners: %s provisioning failed, but we will continue anyway at the caller's request", n.Addr)
  1753  		return diags
  1754  	}
  1755  
  1756  	// Call post hook
  1757  	return diags.Append(ctx.Hook(func(h Hook) (HookAction, error) {
  1758  		return h.PostProvisionInstance(n.Addr, state.Value)
  1759  	}))
  1760  }
  1761  
  1762  // filterProvisioners filters the provisioners on the resource to only
  1763  // the provisioners specified by the "when" option.
  1764  func filterProvisioners(config *configs.Resource, when configs.ProvisionerWhen) []*configs.Provisioner {
  1765  	// Fast path the zero case
  1766  	if config == nil || config.Managed == nil {
  1767  		return nil
  1768  	}
  1769  
  1770  	if len(config.Managed.Provisioners) == 0 {
  1771  		return nil
  1772  	}
  1773  
  1774  	result := make([]*configs.Provisioner, 0, len(config.Managed.Provisioners))
  1775  	for _, p := range config.Managed.Provisioners {
  1776  		if p.When == when {
  1777  			result = append(result, p)
  1778  		}
  1779  	}
  1780  
  1781  	return result
  1782  }
  1783  
  1784  // applyProvisioners executes the provisioners for a resource.
  1785  func (n *NodeAbstractResourceInstance) applyProvisioners(ctx EvalContext, state *states.ResourceInstanceObject, when configs.ProvisionerWhen, provs []*configs.Provisioner) tfdiags.Diagnostics {
  1786  	var diags tfdiags.Diagnostics
  1787  
  1788  	// this self is only used for destroy provisioner evaluation, and must
  1789  	// refer to the last known value of the resource.
  1790  	self := state.Value
  1791  
  1792  	var evalScope func(EvalContext, hcl.Body, cty.Value, *configschema.Block) (cty.Value, tfdiags.Diagnostics)
  1793  	switch when {
  1794  	case configs.ProvisionerWhenDestroy:
  1795  		evalScope = n.evalDestroyProvisionerConfig
  1796  	default:
  1797  		evalScope = n.evalProvisionerConfig
  1798  	}
  1799  
  1800  	// If there's a connection block defined directly inside the resource block
  1801  	// then it'll serve as a base connection configuration for all of the
  1802  	// provisioners.
  1803  	var baseConn hcl.Body
  1804  	if n.Config.Managed != nil && n.Config.Managed.Connection != nil {
  1805  		baseConn = n.Config.Managed.Connection.Config
  1806  	}
  1807  
  1808  	for _, prov := range provs {
  1809  		log.Printf("[TRACE] applyProvisioners: provisioning %s with %q", n.Addr, prov.Type)
  1810  
  1811  		// Get the provisioner
  1812  		provisioner, err := ctx.Provisioner(prov.Type)
  1813  		if err != nil {
  1814  			return diags.Append(err)
  1815  		}
  1816  
  1817  		schema, err := ctx.ProvisionerSchema(prov.Type)
  1818  		if err != nil {
  1819  			// This error probably won't be a great diagnostic, but in practice
  1820  			// we typically catch this problem long before we get here, so
  1821  			// it should be rare to return via this codepath.
  1822  			diags = diags.Append(err)
  1823  			return diags
  1824  		}
  1825  
  1826  		config, configDiags := evalScope(ctx, prov.Config, self, schema)
  1827  		diags = diags.Append(configDiags)
  1828  		if diags.HasErrors() {
  1829  			return diags
  1830  		}
  1831  
  1832  		// If the provisioner block contains a connection block of its own then
  1833  		// it can override the base connection configuration, if any.
  1834  		var localConn hcl.Body
  1835  		if prov.Connection != nil {
  1836  			localConn = prov.Connection.Config
  1837  		}
  1838  
  1839  		var connBody hcl.Body
  1840  		switch {
  1841  		case baseConn != nil && localConn != nil:
  1842  			// Our standard merging logic applies here, similar to what we do
  1843  			// with _override.tf configuration files: arguments from the
  1844  			// base connection block will be masked by any arguments of the
  1845  			// same name in the local connection block.
  1846  			connBody = configs.MergeBodies(baseConn, localConn)
  1847  		case baseConn != nil:
  1848  			connBody = baseConn
  1849  		case localConn != nil:
  1850  			connBody = localConn
  1851  		}
  1852  
  1853  		// start with an empty connInfo
  1854  		connInfo := cty.NullVal(connectionBlockSupersetSchema.ImpliedType())
  1855  
  1856  		if connBody != nil {
  1857  			var connInfoDiags tfdiags.Diagnostics
  1858  			connInfo, connInfoDiags = evalScope(ctx, connBody, self, connectionBlockSupersetSchema)
  1859  			diags = diags.Append(connInfoDiags)
  1860  			if diags.HasErrors() {
  1861  				return diags
  1862  			}
  1863  		}
  1864  
  1865  		{
  1866  			// Call pre hook
  1867  			err := ctx.Hook(func(h Hook) (HookAction, error) {
  1868  				return h.PreProvisionInstanceStep(n.Addr, prov.Type)
  1869  			})
  1870  			if err != nil {
  1871  				return diags.Append(err)
  1872  			}
  1873  		}
  1874  
  1875  		// The output function
  1876  		outputFn := func(msg string) {
  1877  			ctx.Hook(func(h Hook) (HookAction, error) {
  1878  				h.ProvisionOutput(n.Addr, prov.Type, msg)
  1879  				return HookActionContinue, nil
  1880  			})
  1881  		}
  1882  
  1883  		// If our config or connection info contains any marked values, ensure
  1884  		// those are stripped out before sending to the provisioner. Unlike
  1885  		// resources, we have no need to capture the marked paths and reapply
  1886  		// later.
  1887  		unmarkedConfig, configMarks := config.UnmarkDeep()
  1888  		unmarkedConnInfo, _ := connInfo.UnmarkDeep()
  1889  
  1890  		// Marks on the config might result in leaking sensitive values through
  1891  		// provisioner logging, so we conservatively suppress all output in
  1892  		// this case. This should not apply to connection info values, which
  1893  		// provisioners ought not to be logging anyway.
  1894  		if len(configMarks) > 0 {
  1895  			outputFn = func(msg string) {
  1896  				ctx.Hook(func(h Hook) (HookAction, error) {
  1897  					h.ProvisionOutput(n.Addr, prov.Type, "(output suppressed due to sensitive value in config)")
  1898  					return HookActionContinue, nil
  1899  				})
  1900  			}
  1901  		}
  1902  
  1903  		output := CallbackUIOutput{OutputFn: outputFn}
  1904  		resp := provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{
  1905  			Config:     unmarkedConfig,
  1906  			Connection: unmarkedConnInfo,
  1907  			UIOutput:   &output,
  1908  		})
  1909  		applyDiags := resp.Diagnostics.InConfigBody(prov.Config, n.Addr.String())
  1910  
  1911  		// Call post hook
  1912  		hookErr := ctx.Hook(func(h Hook) (HookAction, error) {
  1913  			return h.PostProvisionInstanceStep(n.Addr, prov.Type, applyDiags.Err())
  1914  		})
  1915  
  1916  		switch prov.OnFailure {
  1917  		case configs.ProvisionerOnFailureContinue:
  1918  			if applyDiags.HasErrors() {
  1919  				log.Printf("[WARN] Errors while provisioning %s with %q, but continuing as requested in configuration", n.Addr, prov.Type)
  1920  			} else {
  1921  				// Maybe there are warnings that we still want to see
  1922  				diags = diags.Append(applyDiags)
  1923  			}
  1924  		default:
  1925  			diags = diags.Append(applyDiags)
  1926  			if applyDiags.HasErrors() {
  1927  				log.Printf("[WARN] Errors while provisioning %s with %q, so aborting", n.Addr, prov.Type)
  1928  				return diags
  1929  			}
  1930  		}
  1931  
  1932  		// Deal with the hook
  1933  		if hookErr != nil {
  1934  			return diags.Append(hookErr)
  1935  		}
  1936  	}
  1937  
  1938  	return diags
  1939  }
  1940  
  1941  func (n *NodeAbstractResourceInstance) evalProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) {
  1942  	var diags tfdiags.Diagnostics
  1943  
  1944  	forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx)
  1945  	diags = diags.Append(forEachDiags)
  1946  
  1947  	keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
  1948  
  1949  	config, _, configDiags := ctx.EvaluateBlock(body, schema, n.ResourceInstanceAddr().Resource, keyData)
  1950  	diags = diags.Append(configDiags)
  1951  
  1952  	return config, diags
  1953  }
  1954  
  1955  // during destroy a provisioner can only evaluate within the scope of the parent resource
  1956  func (n *NodeAbstractResourceInstance) evalDestroyProvisionerConfig(ctx EvalContext, body hcl.Body, self cty.Value, schema *configschema.Block) (cty.Value, tfdiags.Diagnostics) {
  1957  	var diags tfdiags.Diagnostics
  1958  
  1959  	// For a destroy-time provisioner forEach is intentionally nil here,
  1960  	// which EvalDataForInstanceKey responds to by not populating EachValue
  1961  	// in its result. That's okay because each.value is prohibited for
  1962  	// destroy-time provisioners.
  1963  	keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, nil)
  1964  
  1965  	evalScope := ctx.EvaluationScope(n.ResourceInstanceAddr().Resource, keyData)
  1966  	config, evalDiags := evalScope.EvalSelfBlock(body, self, schema, keyData)
  1967  	diags = diags.Append(evalDiags)
  1968  
  1969  	return config, diags
  1970  }
  1971  
  1972  // apply accepts an applyConfig, instead of using n.Config, so destroy plans can
  1973  // send a nil config. Most of the errors generated in apply are returned as
  1974  // diagnostics, but if provider.ApplyResourceChange itself fails, that error is
  1975  // returned as an error and nil diags are returned.
  1976  func (n *NodeAbstractResourceInstance) apply(
  1977  	ctx EvalContext,
  1978  	state *states.ResourceInstanceObject,
  1979  	change *plans.ResourceInstanceChange,
  1980  	applyConfig *configs.Resource,
  1981  	createBeforeDestroy bool) (*states.ResourceInstanceObject, tfdiags.Diagnostics) {
  1982  
  1983  	var diags tfdiags.Diagnostics
  1984  	if state == nil {
  1985  		state = &states.ResourceInstanceObject{}
  1986  	}
  1987  
  1988  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
  1989  	if err != nil {
  1990  		return nil, diags.Append(err)
  1991  	}
  1992  	schema, _ := providerSchema.SchemaForResourceType(n.Addr.Resource.Resource.Mode, n.Addr.Resource.Resource.Type)
  1993  	if schema == nil {
  1994  		// Should be caught during validation, so we don't bother with a pretty error here
  1995  		diags = diags.Append(fmt.Errorf("provider does not support resource type %q", n.Addr.Resource.Resource.Type))
  1996  		return nil, diags
  1997  	}
  1998  
  1999  	log.Printf("[INFO] Starting apply for %s", n.Addr)
  2000  
  2001  	configVal := cty.NullVal(cty.DynamicPseudoType)
  2002  	if applyConfig != nil {
  2003  		var configDiags tfdiags.Diagnostics
  2004  		forEach, _ := evaluateForEachExpression(applyConfig.ForEach, ctx)
  2005  		keyData := EvalDataForInstanceKey(n.ResourceInstanceAddr().Resource.Key, forEach)
  2006  		configVal, _, configDiags = ctx.EvaluateBlock(applyConfig.Config, schema, nil, keyData)
  2007  		diags = diags.Append(configDiags)
  2008  		if configDiags.HasErrors() {
  2009  			return nil, diags
  2010  		}
  2011  	}
  2012  
  2013  	if !configVal.IsWhollyKnown() {
  2014  		diags = diags.Append(fmt.Errorf(
  2015  			"configuration for %s still contains unknown values during apply (this is a bug in Terraform; please report it!)",
  2016  			n.Addr,
  2017  		))
  2018  		return nil, diags
  2019  	}
  2020  
  2021  	metaConfigVal, metaDiags := n.providerMetas(ctx)
  2022  	diags = diags.Append(metaDiags)
  2023  	if diags.HasErrors() {
  2024  		return nil, diags
  2025  	}
  2026  
  2027  	log.Printf("[DEBUG] %s: applying the planned %s change", n.Addr, change.Action)
  2028  
  2029  	// If our config, Before or After value contain any marked values,
  2030  	// ensure those are stripped out before sending
  2031  	// this to the provider
  2032  	unmarkedConfigVal, _ := configVal.UnmarkDeep()
  2033  	unmarkedBefore, beforePaths := change.Before.UnmarkDeepWithPaths()
  2034  	unmarkedAfter, afterPaths := change.After.UnmarkDeepWithPaths()
  2035  
  2036  	// If we have an Update action, our before and after values are equal,
  2037  	// and only differ on their sensitivity, the newVal is the after val
  2038  	// and we should not communicate with the provider. We do need to update
  2039  	// the state with this new value, to ensure the sensitivity change is
  2040  	// persisted.
  2041  	eqV := unmarkedBefore.Equals(unmarkedAfter)
  2042  	eq := eqV.IsKnown() && eqV.True()
  2043  	if change.Action == plans.Update && eq && !marksEqual(beforePaths, afterPaths) {
  2044  		// Copy the previous state, changing only the value
  2045  		newState := &states.ResourceInstanceObject{
  2046  			CreateBeforeDestroy: state.CreateBeforeDestroy,
  2047  			Dependencies:        state.Dependencies,
  2048  			Private:             state.Private,
  2049  			Status:              state.Status,
  2050  			Value:               change.After,
  2051  		}
  2052  		return newState, diags
  2053  	}
  2054  
  2055  	resp := provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{
  2056  		TypeName:       n.Addr.Resource.Resource.Type,
  2057  		PriorState:     unmarkedBefore,
  2058  		Config:         unmarkedConfigVal,
  2059  		PlannedState:   unmarkedAfter,
  2060  		PlannedPrivate: change.Private,
  2061  		ProviderMeta:   metaConfigVal,
  2062  	})
  2063  	applyDiags := resp.Diagnostics
  2064  	if applyConfig != nil {
  2065  		applyDiags = applyDiags.InConfigBody(applyConfig.Config, n.Addr.String())
  2066  	}
  2067  	diags = diags.Append(applyDiags)
  2068  
  2069  	// Even if there are errors in the returned diagnostics, the provider may
  2070  	// have returned a _partial_ state for an object that already exists but
  2071  	// failed to fully configure, and so the remaining code must always run
  2072  	// to completion but must be defensive against the new value being
  2073  	// incomplete.
  2074  	newVal := resp.NewState
  2075  
  2076  	// If we have paths to mark, mark those on this new value
  2077  	if len(afterPaths) > 0 {
  2078  		newVal = newVal.MarkWithPaths(afterPaths)
  2079  	}
  2080  
  2081  	if newVal == cty.NilVal {
  2082  		// Providers are supposed to return a partial new value even when errors
  2083  		// occur, but sometimes they don't and so in that case we'll patch that up
  2084  		// by just using the prior state, so we'll at least keep track of the
  2085  		// object for the user to retry.
  2086  		newVal = change.Before
  2087  
  2088  		// As a special case, we'll set the new value to null if it looks like
  2089  		// we were trying to execute a delete, because the provider in this case
  2090  		// probably left the newVal unset intending it to be interpreted as "null".
  2091  		if change.After.IsNull() {
  2092  			newVal = cty.NullVal(schema.ImpliedType())
  2093  		}
  2094  
  2095  		if !diags.HasErrors() {
  2096  			diags = diags.Append(tfdiags.Sourceless(
  2097  				tfdiags.Error,
  2098  				"Provider produced invalid object",
  2099  				fmt.Sprintf(
  2100  					"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.",
  2101  					n.ResolvedProvider.String(), n.Addr.String(),
  2102  				),
  2103  			))
  2104  		}
  2105  	}
  2106  
  2107  	var conformDiags tfdiags.Diagnostics
  2108  	for _, err := range newVal.Type().TestConformance(schema.ImpliedType()) {
  2109  		conformDiags = conformDiags.Append(tfdiags.Sourceless(
  2110  			tfdiags.Error,
  2111  			"Provider produced invalid object",
  2112  			fmt.Sprintf(
  2113  				"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.",
  2114  				n.ResolvedProvider.String(), tfdiags.FormatErrorPrefixed(err, n.Addr.String()),
  2115  			),
  2116  		))
  2117  	}
  2118  	diags = diags.Append(conformDiags)
  2119  	if conformDiags.HasErrors() {
  2120  		// Bail early in this particular case, because an object that doesn't
  2121  		// conform to the schema can't be saved in the state anyway -- the
  2122  		// serializer will reject it.
  2123  		return nil, diags
  2124  	}
  2125  
  2126  	// After this point we have a type-conforming result object and so we
  2127  	// must always run to completion to ensure it can be saved. If n.Error
  2128  	// is set then we must not return a non-nil error, in order to allow
  2129  	// evaluation to continue to a later point where our state object will
  2130  	// be saved.
  2131  
  2132  	// By this point there must not be any unknown values remaining in our
  2133  	// object, because we've applied the change and we can't save unknowns
  2134  	// in our persistent state. If any are present then we will indicate an
  2135  	// error (which is always a bug in the provider) but we will also replace
  2136  	// them with nulls so that we can successfully save the portions of the
  2137  	// returned value that are known.
  2138  	if !newVal.IsWhollyKnown() {
  2139  		// To generate better error messages, we'll go for a walk through the
  2140  		// value and make a separate diagnostic for each unknown value we
  2141  		// find.
  2142  		cty.Walk(newVal, func(path cty.Path, val cty.Value) (bool, error) {
  2143  			if !val.IsKnown() {
  2144  				pathStr := tfdiags.FormatCtyPath(path)
  2145  				diags = diags.Append(tfdiags.Sourceless(
  2146  					tfdiags.Error,
  2147  					"Provider returned invalid result object after apply",
  2148  					fmt.Sprintf(
  2149  						"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.",
  2150  						n.Addr, pathStr,
  2151  					),
  2152  				))
  2153  			}
  2154  			return true, nil
  2155  		})
  2156  
  2157  		// NOTE: This operation can potentially be lossy if there are multiple
  2158  		// elements in a set that differ only by unknown values: after
  2159  		// replacing with null these will be merged together into a single set
  2160  		// element. Since we can only get here in the presence of a provider
  2161  		// bug, we accept this because storing a result here is always a
  2162  		// best-effort sort of thing.
  2163  		newVal = cty.UnknownAsNull(newVal)
  2164  	}
  2165  
  2166  	if change.Action != plans.Delete && !diags.HasErrors() {
  2167  		// Only values that were marked as unknown in the planned value are allowed
  2168  		// to change during the apply operation. (We do this after the unknown-ness
  2169  		// check above so that we also catch anything that became unknown after
  2170  		// being known during plan.)
  2171  		//
  2172  		// If we are returning other errors anyway then we'll give this
  2173  		// a pass since the other errors are usually the explanation for
  2174  		// this one and so it's more helpful to let the user focus on the
  2175  		// root cause rather than distract with this extra problem.
  2176  		if errs := objchange.AssertObjectCompatible(schema, change.After, newVal); len(errs) > 0 {
  2177  			if resp.LegacyTypeSystem {
  2178  				// The shimming of the old type system in the legacy SDK is not precise
  2179  				// enough to pass this consistency check, so we'll give it a pass here,
  2180  				// but we will generate a warning about it so that we are more likely
  2181  				// to notice in the logs if an inconsistency beyond the type system
  2182  				// leads to a downstream provider failure.
  2183  				var buf strings.Builder
  2184  				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)
  2185  				for _, err := range errs {
  2186  					fmt.Fprintf(&buf, "\n      - %s", tfdiags.FormatError(err))
  2187  				}
  2188  				log.Print(buf.String())
  2189  
  2190  				// The sort of inconsistency we won't catch here is if a known value
  2191  				// in the plan is changed during apply. That can cause downstream
  2192  				// problems because a dependent resource would make its own plan based
  2193  				// on the planned value, and thus get a different result during the
  2194  				// apply phase. This will usually lead to a "Provider produced invalid plan"
  2195  				// error that incorrectly blames the downstream resource for the change.
  2196  
  2197  			} else {
  2198  				for _, err := range errs {
  2199  					diags = diags.Append(tfdiags.Sourceless(
  2200  						tfdiags.Error,
  2201  						"Provider produced inconsistent result after apply",
  2202  						fmt.Sprintf(
  2203  							"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.",
  2204  							n.Addr, n.ResolvedProvider.String(), tfdiags.FormatError(err),
  2205  						),
  2206  					))
  2207  				}
  2208  			}
  2209  		}
  2210  	}
  2211  
  2212  	// If a provider returns a null or non-null object at the wrong time then
  2213  	// we still want to save that but it often causes some confusing behaviors
  2214  	// where it seems like Terraform is failing to take any action at all,
  2215  	// so we'll generate some errors to draw attention to it.
  2216  	if !diags.HasErrors() {
  2217  		if change.Action == plans.Delete && !newVal.IsNull() {
  2218  			diags = diags.Append(tfdiags.Sourceless(
  2219  				tfdiags.Error,
  2220  				"Provider returned invalid result object after apply",
  2221  				fmt.Sprintf(
  2222  					"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.",
  2223  					change.Action, n.Addr,
  2224  				),
  2225  			))
  2226  		}
  2227  		if change.Action != plans.Delete && newVal.IsNull() {
  2228  			diags = diags.Append(tfdiags.Sourceless(
  2229  				tfdiags.Error,
  2230  				"Provider returned invalid result object after apply",
  2231  				fmt.Sprintf(
  2232  					"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.",
  2233  					change.Action, n.Addr,
  2234  				),
  2235  			))
  2236  		}
  2237  	}
  2238  
  2239  	switch {
  2240  	case diags.HasErrors() && newVal.IsNull():
  2241  		// Sometimes providers return a null value when an operation fails for
  2242  		// some reason, but we'd rather keep the prior state so that the error
  2243  		// can be corrected on a subsequent run. We must only do this for null
  2244  		// new value though, or else we may discard partial updates the
  2245  		// provider was able to complete. Otherwise, we'll continue using the
  2246  		// prior state as the new value, making this effectively a no-op.  If
  2247  		// the item really _has_ been deleted then our next refresh will detect
  2248  		// that and fix it up.
  2249  		return state.DeepCopy(), diags
  2250  
  2251  	case diags.HasErrors() && !newVal.IsNull():
  2252  		// if we have an error, make sure we restore the object status in the new state
  2253  		newState := &states.ResourceInstanceObject{
  2254  			Status:              state.Status,
  2255  			Value:               newVal,
  2256  			Private:             resp.Private,
  2257  			CreateBeforeDestroy: createBeforeDestroy,
  2258  		}
  2259  
  2260  		// if the resource was being deleted, the dependencies are not going to
  2261  		// be recalculated and we need to restore those as well.
  2262  		if change.Action == plans.Delete {
  2263  			newState.Dependencies = state.Dependencies
  2264  		}
  2265  
  2266  		return newState, diags
  2267  
  2268  	case !newVal.IsNull():
  2269  		// Non error case with a new state
  2270  		newState := &states.ResourceInstanceObject{
  2271  			Status:              states.ObjectReady,
  2272  			Value:               newVal,
  2273  			Private:             resp.Private,
  2274  			CreateBeforeDestroy: createBeforeDestroy,
  2275  		}
  2276  		return newState, diags
  2277  
  2278  	default:
  2279  		// Non error case, were the object was deleted
  2280  		return nil, diags
  2281  	}
  2282  }
  2283  
  2284  func (n *NodeAbstractResourceInstance) prevRunAddr(ctx EvalContext) addrs.AbsResourceInstance {
  2285  	return resourceInstancePrevRunAddr(ctx, n.Addr)
  2286  }
  2287  
  2288  func resourceInstancePrevRunAddr(ctx EvalContext, currentAddr addrs.AbsResourceInstance) addrs.AbsResourceInstance {
  2289  	table := ctx.MoveResults()
  2290  	return table.OldAddr(currentAddr)
  2291  }