github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/durgaform/node_resource_abstract_instance.go (about)

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