github.com/opentofu/opentofu@v1.7.1/internal/tofu/node_resource_abstract_instance.go (about)

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