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