1package terraform
2
3import (
4	"bytes"
5	"encoding/json"
6	"errors"
7	"fmt"
8	"log"
9	"reflect"
10	"runtime"
11	"sort"
12	"strings"
13	"sync"
14	"sync/atomic"
15	"testing"
16	"time"
17
18	"github.com/davecgh/go-spew/spew"
19	"github.com/go-test/deep"
20	"github.com/google/go-cmp/cmp"
21	"github.com/hashicorp/terraform/internal/addrs"
22	"github.com/hashicorp/terraform/internal/configs"
23	"github.com/hashicorp/terraform/internal/configs/configschema"
24	"github.com/hashicorp/terraform/internal/configs/hcl2shim"
25	"github.com/hashicorp/terraform/internal/plans"
26	"github.com/hashicorp/terraform/internal/providers"
27	"github.com/hashicorp/terraform/internal/provisioners"
28	"github.com/hashicorp/terraform/internal/states"
29	"github.com/hashicorp/terraform/internal/tfdiags"
30	"github.com/zclconf/go-cty/cty"
31	"github.com/zclconf/go-cty/cty/gocty"
32)
33
34func TestContext2Apply_basic(t *testing.T) {
35	m := testModule(t, "apply-good")
36	p := testProvider("aws")
37	p.PlanResourceChangeFn = testDiffFn
38	p.ApplyResourceChangeFn = testApplyFn
39	ctx := testContext2(t, &ContextOpts{
40		Config: m,
41		Providers: map[addrs.Provider]providers.Factory{
42			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
43		},
44	})
45
46	if _, diags := ctx.Plan(); diags.HasErrors() {
47		t.Fatalf("plan errors: %s", diags.Err())
48	}
49
50	state, diags := ctx.Apply()
51	if diags.HasErrors() {
52		t.Fatalf("diags: %s", diags.Err())
53	}
54
55	mod := state.RootModule()
56	if len(mod.Resources) < 2 {
57		t.Fatalf("bad: %#v", mod.Resources)
58	}
59
60	actual := strings.TrimSpace(state.String())
61	expected := strings.TrimSpace(testTerraformApplyStr)
62	if actual != expected {
63		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
64	}
65}
66
67func TestContext2Apply_unstable(t *testing.T) {
68	// This tests behavior when the configuration contains an unstable value,
69	// such as the result of uuid() or timestamp(), where each call produces
70	// a different result.
71	//
72	// This is an important case to test because we need to ensure that
73	// we don't re-call the function during the apply phase: the value should
74	// be fixed during plan
75
76	m := testModule(t, "apply-unstable")
77	p := testProvider("test")
78	p.PlanResourceChangeFn = testDiffFn
79	ctx := testContext2(t, &ContextOpts{
80		Config: m,
81		Providers: map[addrs.Provider]providers.Factory{
82			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
83		},
84	})
85
86	plan, diags := ctx.Plan()
87	if diags.HasErrors() {
88		t.Fatalf("unexpected error during Plan: %s", diags.Err())
89	}
90
91	addr := addrs.Resource{
92		Mode: addrs.ManagedResourceMode,
93		Type: "test_resource",
94		Name: "foo",
95	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
96	schema := p.GetProviderSchemaResponse.ResourceTypes["test_resource"].Block
97	rds := plan.Changes.ResourceInstance(addr)
98	rd, err := rds.Decode(schema.ImpliedType())
99	if err != nil {
100		t.Fatal(err)
101	}
102	if rd.After.GetAttr("random").IsKnown() {
103		t.Fatalf("Attribute 'random' has known value %#v; should be unknown in plan", rd.After.GetAttr("random"))
104	}
105
106	state, diags := ctx.Apply()
107	if diags.HasErrors() {
108		t.Fatalf("unexpected error during Apply: %s", diags.Err())
109	}
110
111	mod := state.Module(addr.Module)
112	rss := state.ResourceInstance(addr)
113
114	if len(mod.Resources) != 1 {
115		t.Fatalf("wrong number of resources %d; want 1", len(mod.Resources))
116	}
117
118	rs, err := rss.Current.Decode(schema.ImpliedType())
119	if err != nil {
120		t.Fatalf("decode error: %v", err)
121	}
122	got := rs.Value.GetAttr("random")
123	if !got.IsKnown() {
124		t.Fatalf("random is still unknown after apply")
125	}
126	if got, want := len(got.AsString()), 36; got != want {
127		t.Fatalf("random string has wrong length %d; want %d", got, want)
128	}
129}
130
131func TestContext2Apply_escape(t *testing.T) {
132	m := testModule(t, "apply-escape")
133	p := testProvider("aws")
134	p.PlanResourceChangeFn = testDiffFn
135	p.ApplyResourceChangeFn = testApplyFn
136	ctx := testContext2(t, &ContextOpts{
137		Config: m,
138		Providers: map[addrs.Provider]providers.Factory{
139			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
140		},
141	})
142
143	if _, diags := ctx.Plan(); diags.HasErrors() {
144		t.Fatalf("plan errors: %s", diags.Err())
145	}
146
147	state, diags := ctx.Apply()
148	if diags.HasErrors() {
149		t.Fatalf("diags: %s", diags.Err())
150	}
151
152	checkStateString(t, state, `
153aws_instance.bar:
154  ID = foo
155  provider = provider["registry.terraform.io/hashicorp/aws"]
156  foo = "bar"
157  type = aws_instance
158`)
159}
160
161func TestContext2Apply_resourceCountOneList(t *testing.T) {
162	m := testModule(t, "apply-resource-count-one-list")
163	p := testProvider("null")
164	p.PlanResourceChangeFn = testDiffFn
165	p.ApplyResourceChangeFn = testApplyFn
166	ctx := testContext2(t, &ContextOpts{
167		Config: m,
168		Providers: map[addrs.Provider]providers.Factory{
169			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
170		},
171	})
172
173	if _, diags := ctx.Plan(); diags.HasErrors() {
174		t.Fatalf("plan errors: %s", diags.Err())
175	}
176
177	state, diags := ctx.Apply()
178	assertNoDiagnostics(t, diags)
179
180	got := strings.TrimSpace(state.String())
181	want := strings.TrimSpace(`null_resource.foo.0:
182  ID = foo
183  provider = provider["registry.terraform.io/hashicorp/null"]
184
185Outputs:
186
187test = [foo]`)
188	if got != want {
189		t.Fatalf("got:\n%s\n\nwant:\n%s\n", got, want)
190	}
191}
192func TestContext2Apply_resourceCountZeroList(t *testing.T) {
193	m := testModule(t, "apply-resource-count-zero-list")
194	p := testProvider("null")
195	p.PlanResourceChangeFn = testDiffFn
196	ctx := testContext2(t, &ContextOpts{
197		Config: m,
198		Providers: map[addrs.Provider]providers.Factory{
199			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
200		},
201	})
202
203	if _, diags := ctx.Plan(); diags.HasErrors() {
204		t.Fatalf("plan errors: %s", diags.Err())
205	}
206
207	state, diags := ctx.Apply()
208	if diags.HasErrors() {
209		t.Fatalf("diags: %s", diags.Err())
210	}
211
212	got := strings.TrimSpace(state.String())
213	want := strings.TrimSpace(`<no state>
214Outputs:
215
216test = []`)
217	if got != want {
218		t.Fatalf("wrong state\n\ngot:\n%s\n\nwant:\n%s\n", got, want)
219	}
220}
221
222func TestContext2Apply_resourceDependsOnModule(t *testing.T) {
223	m := testModule(t, "apply-resource-depends-on-module")
224	p := testProvider("aws")
225	p.PlanResourceChangeFn = testDiffFn
226
227	// verify the apply happens in the correct order
228	var mu sync.Mutex
229	var order []string
230
231	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
232		ami := req.PlannedState.GetAttr("ami").AsString()
233		switch ami {
234		case "child":
235
236			// make the child slower than the parent
237			time.Sleep(50 * time.Millisecond)
238
239			mu.Lock()
240			order = append(order, "child")
241			mu.Unlock()
242		case "parent":
243			mu.Lock()
244			order = append(order, "parent")
245			mu.Unlock()
246		}
247
248		return testApplyFn(req)
249	}
250
251	ctx := testContext2(t, &ContextOpts{
252		Config: m,
253		Providers: map[addrs.Provider]providers.Factory{
254			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
255		},
256	})
257
258	if _, diags := ctx.Plan(); diags.HasErrors() {
259		t.Fatalf("plan errors: %s", diags.Err())
260	}
261
262	state, diags := ctx.Apply()
263	if diags.HasErrors() {
264		t.Fatalf("diags: %s", diags.Err())
265	}
266
267	if !reflect.DeepEqual(order, []string{"child", "parent"}) {
268		t.Fatal("resources applied out of order")
269	}
270
271	checkStateString(t, state, testTerraformApplyResourceDependsOnModuleStr)
272}
273
274// Test that without a config, the Dependencies in the state are enough
275// to maintain proper ordering.
276func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) {
277	m := testModule(t, "apply-resource-depends-on-module-empty")
278	p := testProvider("aws")
279	p.PlanResourceChangeFn = testDiffFn
280
281	state := states.NewState()
282	root := state.EnsureModule(addrs.RootModuleInstance)
283	root.SetResourceInstanceCurrent(
284		mustResourceInstanceAddr("aws_instance.a").Resource,
285		&states.ResourceInstanceObjectSrc{
286			Status:       states.ObjectReady,
287			AttrsJSON:    []byte(`{"id":"parent"}`),
288			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.aws_instance.child")},
289		},
290		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
291	)
292	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
293	child.SetResourceInstanceCurrent(
294		mustResourceInstanceAddr("aws_instance.child").Resource,
295		&states.ResourceInstanceObjectSrc{
296			Status:    states.ObjectReady,
297			AttrsJSON: []byte(`{"id":"child"}`),
298		},
299		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
300	)
301
302	{
303		// verify the apply happens in the correct order
304		var mu sync.Mutex
305		var order []string
306
307		p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
308			id := req.PriorState.GetAttr("id")
309			if id.IsKnown() && id.AsString() == "parent" {
310				// make the dep slower than the parent
311				time.Sleep(50 * time.Millisecond)
312
313				mu.Lock()
314				order = append(order, "child")
315				mu.Unlock()
316			} else {
317				mu.Lock()
318				order = append(order, "parent")
319				mu.Unlock()
320			}
321
322			return testApplyFn(req)
323		}
324
325		ctx := testContext2(t, &ContextOpts{
326			Config: m,
327			Providers: map[addrs.Provider]providers.Factory{
328				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
329			},
330			State: state,
331		})
332
333		if _, diags := ctx.Plan(); diags.HasErrors() {
334			t.Fatalf("diags: %s", diags.Err())
335		}
336
337		state, diags := ctx.Apply()
338		assertNoErrors(t, diags)
339
340		if !reflect.DeepEqual(order, []string{"child", "parent"}) {
341			t.Fatal("resources applied out of order")
342		}
343
344		checkStateString(t, state, "<no state>")
345	}
346}
347
348func TestContext2Apply_resourceDependsOnModuleDestroy(t *testing.T) {
349	m := testModule(t, "apply-resource-depends-on-module")
350	p := testProvider("aws")
351	p.PlanResourceChangeFn = testDiffFn
352
353	var globalState *states.State
354	{
355		ctx := testContext2(t, &ContextOpts{
356			Config: m,
357			Providers: map[addrs.Provider]providers.Factory{
358				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
359			},
360		})
361
362		if _, diags := ctx.Plan(); diags.HasErrors() {
363			t.Fatalf("diags: %s", diags.Err())
364		}
365
366		state, diags := ctx.Apply()
367		if diags.HasErrors() {
368			t.Fatalf("diags: %s", diags.Err())
369		}
370
371		globalState = state
372	}
373
374	{
375		// Wait for the dependency, sleep, and verify the graph never
376		// called a child.
377		var called int32
378		var checked bool
379		p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
380			ami := req.PriorState.GetAttr("ami").AsString()
381			if ami == "parent" {
382				checked = true
383
384				// Sleep to allow parallel execution
385				time.Sleep(50 * time.Millisecond)
386
387				// Verify that called is 0 (dep not called)
388				if atomic.LoadInt32(&called) != 0 {
389					resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("module child should not be called"))
390					return resp
391				}
392			}
393
394			atomic.AddInt32(&called, 1)
395			return testApplyFn(req)
396		}
397
398		ctx := testContext2(t, &ContextOpts{
399			Config: m,
400			Providers: map[addrs.Provider]providers.Factory{
401				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
402			},
403			State:    globalState,
404			PlanMode: plans.DestroyMode,
405		})
406
407		if _, diags := ctx.Plan(); diags.HasErrors() {
408			t.Fatalf("diags: %s", diags.Err())
409		}
410
411		state, diags := ctx.Apply()
412		if diags.HasErrors() {
413			t.Fatalf("diags: %s", diags.Err())
414		}
415
416		if !checked {
417			t.Fatal("should check")
418		}
419
420		checkStateString(t, state, `<no state>`)
421	}
422}
423
424func TestContext2Apply_resourceDependsOnModuleGrandchild(t *testing.T) {
425	m := testModule(t, "apply-resource-depends-on-module-deep")
426	p := testProvider("aws")
427	p.PlanResourceChangeFn = testDiffFn
428
429	{
430		// Wait for the dependency, sleep, and verify the graph never
431		// called a child.
432		var called int32
433		var checked bool
434		p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
435			planned := req.PlannedState.AsValueMap()
436			if ami, ok := planned["ami"]; ok && ami.AsString() == "grandchild" {
437				checked = true
438
439				// Sleep to allow parallel execution
440				time.Sleep(50 * time.Millisecond)
441
442				// Verify that called is 0 (dep not called)
443				if atomic.LoadInt32(&called) != 0 {
444					resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("aws_instance.a should not be called"))
445					return resp
446				}
447			}
448
449			atomic.AddInt32(&called, 1)
450			return testApplyFn(req)
451		}
452
453		ctx := testContext2(t, &ContextOpts{
454			Config: m,
455			Providers: map[addrs.Provider]providers.Factory{
456				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
457			},
458		})
459
460		if _, diags := ctx.Plan(); diags.HasErrors() {
461			t.Fatalf("diags: %s", diags.Err())
462		}
463
464		state, diags := ctx.Apply()
465		if diags.HasErrors() {
466			t.Fatalf("diags: %s", diags.Err())
467		}
468
469		if !checked {
470			t.Fatal("should check")
471		}
472
473		checkStateString(t, state, testTerraformApplyResourceDependsOnModuleDeepStr)
474	}
475}
476
477func TestContext2Apply_resourceDependsOnModuleInModule(t *testing.T) {
478	m := testModule(t, "apply-resource-depends-on-module-in-module")
479	p := testProvider("aws")
480	p.PlanResourceChangeFn = testDiffFn
481
482	{
483		// Wait for the dependency, sleep, and verify the graph never
484		// called a child.
485		var called int32
486		var checked bool
487		p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
488			planned := req.PlannedState.AsValueMap()
489			if ami, ok := planned["ami"]; ok && ami.AsString() == "grandchild" {
490				checked = true
491
492				// Sleep to allow parallel execution
493				time.Sleep(50 * time.Millisecond)
494
495				// Verify that called is 0 (dep not called)
496				if atomic.LoadInt32(&called) != 0 {
497					resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("something else was applied before grandchild; grandchild should be first"))
498					return resp
499				}
500			}
501
502			atomic.AddInt32(&called, 1)
503			return testApplyFn(req)
504		}
505
506		ctx := testContext2(t, &ContextOpts{
507			Config: m,
508			Providers: map[addrs.Provider]providers.Factory{
509				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
510			},
511		})
512
513		if _, diags := ctx.Plan(); diags.HasErrors() {
514			t.Fatalf("diags: %s", diags.Err())
515		}
516
517		state, diags := ctx.Apply()
518		if diags.HasErrors() {
519			t.Fatalf("diags: %s", diags.Err())
520		}
521
522		if !checked {
523			t.Fatal("should check")
524		}
525
526		checkStateString(t, state, testTerraformApplyResourceDependsOnModuleInModuleStr)
527	}
528}
529
530func TestContext2Apply_mapVarBetweenModules(t *testing.T) {
531	m := testModule(t, "apply-map-var-through-module")
532	p := testProvider("null")
533	p.PlanResourceChangeFn = testDiffFn
534	p.ApplyResourceChangeFn = testApplyFn
535	ctx := testContext2(t, &ContextOpts{
536		Config: m,
537		Providers: map[addrs.Provider]providers.Factory{
538			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
539		},
540	})
541
542	if _, diags := ctx.Plan(); diags.HasErrors() {
543		t.Fatalf("plan errors: %s", diags.Err())
544	}
545
546	state, diags := ctx.Apply()
547	if diags.HasErrors() {
548		t.Fatalf("diags: %s", diags.Err())
549	}
550
551	actual := strings.TrimSpace(state.String())
552	expected := strings.TrimSpace(`<no state>
553Outputs:
554
555amis_from_module = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }
556
557module.test:
558  null_resource.noop:
559    ID = foo
560    provider = provider["registry.terraform.io/hashicorp/null"]
561
562  Outputs:
563
564  amis_out = {eu-west-1:ami-789012 eu-west-2:ami-989484 us-west-1:ami-123456 us-west-2:ami-456789 }`)
565	if actual != expected {
566		t.Fatalf("expected: \n%s\n\ngot: \n%s\n", expected, actual)
567	}
568}
569
570func TestContext2Apply_refCount(t *testing.T) {
571	m := testModule(t, "apply-ref-count")
572	p := testProvider("aws")
573	p.PlanResourceChangeFn = testDiffFn
574	p.ApplyResourceChangeFn = testApplyFn
575	ctx := testContext2(t, &ContextOpts{
576		Config: m,
577		Providers: map[addrs.Provider]providers.Factory{
578			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
579		},
580	})
581
582	if _, diags := ctx.Plan(); diags.HasErrors() {
583		t.Fatalf("plan errors: %s", diags.Err())
584	}
585
586	state, diags := ctx.Apply()
587	if diags.HasErrors() {
588		t.Fatalf("diags: %s", diags.Err())
589	}
590
591	mod := state.RootModule()
592	if len(mod.Resources) < 2 {
593		t.Fatalf("bad: %#v", mod.Resources)
594	}
595
596	actual := strings.TrimSpace(state.String())
597	expected := strings.TrimSpace(testTerraformApplyRefCountStr)
598	if actual != expected {
599		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
600	}
601}
602
603func TestContext2Apply_providerAlias(t *testing.T) {
604	m := testModule(t, "apply-provider-alias")
605	p := testProvider("aws")
606	p.PlanResourceChangeFn = testDiffFn
607	p.ApplyResourceChangeFn = testApplyFn
608	ctx := testContext2(t, &ContextOpts{
609		Config: m,
610		Providers: map[addrs.Provider]providers.Factory{
611			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
612		},
613	})
614
615	if _, diags := ctx.Plan(); diags.HasErrors() {
616		t.Fatalf("plan errors: %s", diags.Err())
617	}
618
619	state, diags := ctx.Apply()
620	if diags.HasErrors() {
621		t.Fatalf("diags: %s", diags.Err())
622	}
623
624	mod := state.RootModule()
625	if len(mod.Resources) < 2 {
626		t.Fatalf("bad: %#v", mod.Resources)
627	}
628
629	actual := strings.TrimSpace(state.String())
630	expected := strings.TrimSpace(testTerraformApplyProviderAliasStr)
631	if actual != expected {
632		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
633	}
634}
635
636// Two providers that are configured should both be configured prior to apply
637func TestContext2Apply_providerAliasConfigure(t *testing.T) {
638	m := testModule(t, "apply-provider-alias-configure")
639
640	p2 := testProvider("another")
641	p2.ApplyResourceChangeFn = testApplyFn
642	p2.PlanResourceChangeFn = testDiffFn
643
644	ctx := testContext2(t, &ContextOpts{
645		Config: m,
646		Providers: map[addrs.Provider]providers.Factory{
647			addrs.NewDefaultProvider("another"): testProviderFuncFixed(p2),
648		},
649	})
650
651	if p, diags := ctx.Plan(); diags.HasErrors() {
652		t.Fatalf("diags: %s", diags.Err())
653	} else {
654		t.Logf(legacyDiffComparisonString(p.Changes))
655	}
656
657	// Configure to record calls AFTER Plan above
658	var configCount int32
659	p2.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
660		atomic.AddInt32(&configCount, 1)
661
662		foo := req.Config.GetAttr("foo").AsString()
663		if foo != "bar" {
664			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("foo: %#v", foo))
665		}
666
667		return
668	}
669
670	state, diags := ctx.Apply()
671	if diags.HasErrors() {
672		t.Fatalf("diags: %s", diags.Err())
673	}
674
675	if configCount != 2 {
676		t.Fatalf("provider config expected 2 calls, got: %d", configCount)
677	}
678
679	actual := strings.TrimSpace(state.String())
680	expected := strings.TrimSpace(testTerraformApplyProviderAliasConfigStr)
681	if actual != expected {
682		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
683	}
684}
685
686// GH-2870
687func TestContext2Apply_providerWarning(t *testing.T) {
688	m := testModule(t, "apply-provider-warning")
689	p := testProvider("aws")
690	p.PlanResourceChangeFn = testDiffFn
691	p.ApplyResourceChangeFn = testApplyFn
692	p.ValidateResourceConfigFn = func(req providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) {
693		resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("just a warning"))
694		return
695	}
696	ctx := testContext2(t, &ContextOpts{
697		Config: m,
698		Providers: map[addrs.Provider]providers.Factory{
699			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
700		},
701	})
702
703	if _, diags := ctx.Plan(); diags.HasErrors() {
704		t.Fatalf("plan errors: %s", diags.Err())
705	}
706
707	state, diags := ctx.Apply()
708	if diags.HasErrors() {
709		t.Fatalf("diags: %s", diags.Err())
710	}
711
712	actual := strings.TrimSpace(state.String())
713	expected := strings.TrimSpace(`
714aws_instance.foo:
715  ID = foo
716  provider = provider["registry.terraform.io/hashicorp/aws"]
717  type = aws_instance
718	`)
719	if actual != expected {
720		t.Fatalf("got: \n%s\n\nexpected:\n%s", actual, expected)
721	}
722
723	if !p.ConfigureProviderCalled {
724		t.Fatalf("provider Configure() was never called!")
725	}
726}
727
728func TestContext2Apply_emptyModule(t *testing.T) {
729	// A module with only outputs (no resources)
730	m := testModule(t, "apply-empty-module")
731	p := testProvider("aws")
732	p.PlanResourceChangeFn = testDiffFn
733	ctx := testContext2(t, &ContextOpts{
734		Config: m,
735		Providers: map[addrs.Provider]providers.Factory{
736			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
737		},
738	})
739
740	if _, diags := ctx.Plan(); diags.HasErrors() {
741		t.Fatalf("plan errors: %s", diags.Err())
742	}
743
744	state, diags := ctx.Apply()
745	if diags.HasErrors() {
746		t.Fatalf("diags: %s", diags.Err())
747	}
748
749	actual := strings.TrimSpace(state.String())
750	actual = strings.Replace(actual, "  ", "", -1)
751	expected := strings.TrimSpace(testTerraformApplyEmptyModuleStr)
752	if actual != expected {
753		t.Fatalf("bad: \n%s\nexpect:\n%s", actual, expected)
754	}
755}
756
757func TestContext2Apply_createBeforeDestroy(t *testing.T) {
758	m := testModule(t, "apply-good-create-before")
759	p := testProvider("aws")
760	p.PlanResourceChangeFn = testDiffFn
761	p.ApplyResourceChangeFn = testApplyFn
762	state := states.NewState()
763	root := state.EnsureModule(addrs.RootModuleInstance)
764	root.SetResourceInstanceCurrent(
765		mustResourceInstanceAddr("aws_instance.bar").Resource,
766		&states.ResourceInstanceObjectSrc{
767			Status:    states.ObjectReady,
768			AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`),
769		},
770		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
771	)
772	ctx := testContext2(t, &ContextOpts{
773		Config: m,
774		Providers: map[addrs.Provider]providers.Factory{
775			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
776		},
777		State: state,
778	})
779
780	if p, diags := ctx.Plan(); diags.HasErrors() {
781		t.Fatalf("diags: %s", diags.Err())
782	} else {
783		t.Logf(legacyDiffComparisonString(p.Changes))
784	}
785
786	state, diags := ctx.Apply()
787	if diags.HasErrors() {
788		t.Fatalf("diags: %s", diags.Err())
789	}
790
791	mod := state.RootModule()
792	if got, want := len(mod.Resources), 1; got != want {
793		t.Logf("state:\n%s", state)
794		t.Fatalf("wrong number of resources %d; want %d", got, want)
795	}
796
797	actual := strings.TrimSpace(state.String())
798	expected := strings.TrimSpace(testTerraformApplyCreateBeforeStr)
799	if actual != expected {
800		t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual)
801	}
802}
803
804func TestContext2Apply_createBeforeDestroyUpdate(t *testing.T) {
805	m := testModule(t, "apply-good-create-before-update")
806	p := testProvider("aws")
807	p.PlanResourceChangeFn = testDiffFn
808
809	// signal that resource foo has started applying
810	fooChan := make(chan struct{})
811
812	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
813		id := req.PriorState.GetAttr("id").AsString()
814		switch id {
815		case "bar":
816			select {
817			case <-fooChan:
818				resp.Diagnostics = resp.Diagnostics.Append(errors.New("bar must be updated before foo is destroyed"))
819				return resp
820			case <-time.After(100 * time.Millisecond):
821				// wait a moment to ensure that foo is not going to be destroyed first
822			}
823		case "foo":
824			close(fooChan)
825		}
826
827		return testApplyFn(req)
828	}
829
830	state := states.NewState()
831	root := state.EnsureModule(addrs.RootModuleInstance)
832	fooAddr := mustResourceInstanceAddr("aws_instance.foo")
833	root.SetResourceInstanceCurrent(
834		fooAddr.Resource,
835		&states.ResourceInstanceObjectSrc{
836			Status:              states.ObjectReady,
837			AttrsJSON:           []byte(`{"id":"foo","foo":"bar"}`),
838			CreateBeforeDestroy: true,
839		},
840		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
841	)
842	root.SetResourceInstanceCurrent(
843		mustResourceInstanceAddr("aws_instance.bar").Resource,
844		&states.ResourceInstanceObjectSrc{
845			Status:              states.ObjectReady,
846			AttrsJSON:           []byte(`{"id":"bar","foo":"bar"}`),
847			CreateBeforeDestroy: true,
848			Dependencies:        []addrs.ConfigResource{fooAddr.ContainingResource().Config()},
849		},
850		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
851	)
852
853	ctx := testContext2(t, &ContextOpts{
854		Config: m,
855		Providers: map[addrs.Provider]providers.Factory{
856			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
857		},
858		State: state,
859	})
860
861	if p, diags := ctx.Plan(); diags.HasErrors() {
862		t.Fatalf("diags: %s", diags.Err())
863	} else {
864		t.Logf(legacyDiffComparisonString(p.Changes))
865	}
866
867	state, diags := ctx.Apply()
868	if diags.HasErrors() {
869		t.Fatalf("diags: %s", diags.Err())
870	}
871
872	mod := state.RootModule()
873	if len(mod.Resources) != 1 {
874		t.Fatalf("bad: %s", state)
875	}
876
877	actual := strings.TrimSpace(state.String())
878	expected := strings.TrimSpace(testTerraformApplyCreateBeforeUpdateStr)
879	if actual != expected {
880		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
881	}
882}
883
884// This tests that when a CBD resource depends on a non-CBD resource,
885// we can still properly apply changes that require new for both.
886func TestContext2Apply_createBeforeDestroy_dependsNonCBD(t *testing.T) {
887	m := testModule(t, "apply-cbd-depends-non-cbd")
888	p := testProvider("aws")
889	p.PlanResourceChangeFn = testDiffFn
890	p.ApplyResourceChangeFn = testApplyFn
891
892	state := states.NewState()
893	root := state.EnsureModule(addrs.RootModuleInstance)
894	root.SetResourceInstanceCurrent(
895		mustResourceInstanceAddr("aws_instance.bar").Resource,
896		&states.ResourceInstanceObjectSrc{
897			Status:    states.ObjectReady,
898			AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`),
899		},
900		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
901	)
902	root.SetResourceInstanceCurrent(
903		mustResourceInstanceAddr("aws_instance.foo").Resource,
904		&states.ResourceInstanceObjectSrc{
905			Status:    states.ObjectReady,
906			AttrsJSON: []byte(`{"id":"foo", "require_new": "abc"}`),
907		},
908		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
909	)
910
911	ctx := testContext2(t, &ContextOpts{
912		Config: m,
913		Providers: map[addrs.Provider]providers.Factory{
914			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
915		},
916		State: state,
917	})
918
919	if p, diags := ctx.Plan(); diags.HasErrors() {
920		t.Fatalf("diags: %s", diags.Err())
921	} else {
922		t.Logf(legacyDiffComparisonString(p.Changes))
923	}
924
925	state, diags := ctx.Apply()
926	if diags.HasErrors() {
927		t.Fatalf("diags: %s", diags.Err())
928	}
929
930	checkStateString(t, state, `
931aws_instance.bar:
932  ID = foo
933  provider = provider["registry.terraform.io/hashicorp/aws"]
934  require_new = yes
935  type = aws_instance
936  value = foo
937
938  Dependencies:
939    aws_instance.foo
940aws_instance.foo:
941  ID = foo
942  provider = provider["registry.terraform.io/hashicorp/aws"]
943  require_new = yes
944  type = aws_instance
945	`)
946}
947
948func TestContext2Apply_createBeforeDestroy_hook(t *testing.T) {
949	h := new(MockHook)
950	m := testModule(t, "apply-good-create-before")
951	p := testProvider("aws")
952	p.PlanResourceChangeFn = testDiffFn
953	p.ApplyResourceChangeFn = testApplyFn
954	state := states.NewState()
955	root := state.EnsureModule(addrs.RootModuleInstance)
956	root.SetResourceInstanceCurrent(
957		mustResourceInstanceAddr("aws_instance.bar").Resource,
958		&states.ResourceInstanceObjectSrc{
959			Status:    states.ObjectReady,
960			AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`),
961		},
962		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
963	)
964
965	var actual []cty.Value
966	var actualLock sync.Mutex
967	h.PostApplyFn = func(addr addrs.AbsResourceInstance, gen states.Generation, sv cty.Value, e error) (HookAction, error) {
968		actualLock.Lock()
969
970		defer actualLock.Unlock()
971		actual = append(actual, sv)
972		return HookActionContinue, nil
973	}
974
975	ctx := testContext2(t, &ContextOpts{
976		Config: m,
977		Hooks:  []Hook{h},
978		Providers: map[addrs.Provider]providers.Factory{
979			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
980		},
981		State: state,
982	})
983
984	if p, diags := ctx.Plan(); diags.HasErrors() {
985		t.Fatalf("diags: %s", diags.Err())
986	} else {
987		t.Logf(legacyDiffComparisonString(p.Changes))
988	}
989
990	if _, diags := ctx.Apply(); diags.HasErrors() {
991		t.Fatalf("apply errors: %s", diags.Err())
992	}
993
994	expected := []cty.Value{
995		cty.ObjectVal(map[string]cty.Value{
996			"id":          cty.StringVal("foo"),
997			"require_new": cty.StringVal("xyz"),
998			"type":        cty.StringVal("aws_instance"),
999		}),
1000		cty.NullVal(cty.DynamicPseudoType),
1001	}
1002
1003	cmpOpt := cmp.Transformer("ctyshim", hcl2shim.ConfigValueFromHCL2)
1004	if !cmp.Equal(actual, expected, cmpOpt) {
1005		t.Fatalf("wrong state snapshot sequence\n%s", cmp.Diff(expected, actual, cmpOpt))
1006	}
1007}
1008
1009// Test that we can perform an apply with CBD in a count with deposed instances.
1010func TestContext2Apply_createBeforeDestroy_deposedCount(t *testing.T) {
1011	m := testModule(t, "apply-cbd-count")
1012	p := testProvider("aws")
1013	p.PlanResourceChangeFn = testDiffFn
1014	p.ApplyResourceChangeFn = testApplyFn
1015
1016	state := states.NewState()
1017	root := state.EnsureModule(addrs.RootModuleInstance)
1018	root.SetResourceInstanceCurrent(
1019		mustResourceInstanceAddr("aws_instance.bar[0]").Resource,
1020		&states.ResourceInstanceObjectSrc{
1021			Status:    states.ObjectTainted,
1022			AttrsJSON: []byte(`{"id":"bar"}`),
1023		},
1024		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1025	)
1026	root.SetResourceInstanceDeposed(
1027		mustResourceInstanceAddr("aws_instance.bar[0]").Resource,
1028		states.NewDeposedKey(),
1029		&states.ResourceInstanceObjectSrc{
1030			Status:    states.ObjectTainted,
1031			AttrsJSON: []byte(`{"id":"foo"}`),
1032		},
1033		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1034	)
1035	root.SetResourceInstanceCurrent(
1036		mustResourceInstanceAddr("aws_instance.bar[1]").Resource,
1037		&states.ResourceInstanceObjectSrc{
1038			Status:    states.ObjectTainted,
1039			AttrsJSON: []byte(`{"id":"bar"}`),
1040		},
1041		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1042	)
1043	root.SetResourceInstanceDeposed(
1044		mustResourceInstanceAddr("aws_instance.bar[1]").Resource,
1045		states.NewDeposedKey(),
1046		&states.ResourceInstanceObjectSrc{
1047			Status:    states.ObjectTainted,
1048			AttrsJSON: []byte(`{"id":"bar"}`),
1049		},
1050		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1051	)
1052
1053	ctx := testContext2(t, &ContextOpts{
1054		Config: m,
1055		Providers: map[addrs.Provider]providers.Factory{
1056			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1057		},
1058		State: state,
1059	})
1060
1061	if p, diags := ctx.Plan(); diags.HasErrors() {
1062		t.Fatalf("diags: %s", diags.Err())
1063	} else {
1064		t.Logf(legacyDiffComparisonString(p.Changes))
1065	}
1066
1067	state, diags := ctx.Apply()
1068	if diags.HasErrors() {
1069		t.Fatalf("diags: %s", diags.Err())
1070	}
1071
1072	checkStateString(t, state, `
1073aws_instance.bar.0:
1074  ID = foo
1075  provider = provider["registry.terraform.io/hashicorp/aws"]
1076  foo = bar
1077  type = aws_instance
1078aws_instance.bar.1:
1079  ID = foo
1080  provider = provider["registry.terraform.io/hashicorp/aws"]
1081  foo = bar
1082  type = aws_instance
1083	`)
1084}
1085
1086// Test that when we have a deposed instance but a good primary, we still
1087// destroy the deposed instance.
1088func TestContext2Apply_createBeforeDestroy_deposedOnly(t *testing.T) {
1089	m := testModule(t, "apply-cbd-deposed-only")
1090	p := testProvider("aws")
1091	p.PlanResourceChangeFn = testDiffFn
1092	p.ApplyResourceChangeFn = testApplyFn
1093
1094	state := states.NewState()
1095	root := state.EnsureModule(addrs.RootModuleInstance)
1096	root.SetResourceInstanceCurrent(
1097		mustResourceInstanceAddr("aws_instance.bar").Resource,
1098		&states.ResourceInstanceObjectSrc{
1099			Status:    states.ObjectReady,
1100			AttrsJSON: []byte(`{"id":"bar"}`),
1101		},
1102		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1103	)
1104	root.SetResourceInstanceDeposed(
1105		mustResourceInstanceAddr("aws_instance.bar").Resource,
1106		states.NewDeposedKey(),
1107		&states.ResourceInstanceObjectSrc{
1108			Status:    states.ObjectTainted,
1109			AttrsJSON: []byte(`{"id":"foo"}`),
1110		},
1111		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1112	)
1113
1114	ctx := testContext2(t, &ContextOpts{
1115		Config: m,
1116		Providers: map[addrs.Provider]providers.Factory{
1117			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1118		},
1119		State: state,
1120	})
1121
1122	if p, diags := ctx.Plan(); diags.HasErrors() {
1123		t.Fatalf("diags: %s", diags.Err())
1124	} else {
1125		t.Logf(legacyDiffComparisonString(p.Changes))
1126	}
1127
1128	state, diags := ctx.Apply()
1129	if diags.HasErrors() {
1130		t.Fatalf("diags: %s", diags.Err())
1131	}
1132
1133	checkStateString(t, state, `
1134aws_instance.bar:
1135  ID = bar
1136  provider = provider["registry.terraform.io/hashicorp/aws"]
1137  type = aws_instance
1138	`)
1139}
1140
1141func TestContext2Apply_destroyComputed(t *testing.T) {
1142	m := testModule(t, "apply-destroy-computed")
1143	p := testProvider("aws")
1144	p.PlanResourceChangeFn = testDiffFn
1145	state := states.NewState()
1146	root := state.EnsureModule(addrs.RootModuleInstance)
1147	root.SetResourceInstanceCurrent(
1148		mustResourceInstanceAddr("aws_instance.foo").Resource,
1149		&states.ResourceInstanceObjectSrc{
1150			Status:    states.ObjectReady,
1151			AttrsJSON: []byte(`{"id":"foo", "output": "value"}`),
1152		},
1153		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1154	)
1155	ctx := testContext2(t, &ContextOpts{
1156		Config: m,
1157		Providers: map[addrs.Provider]providers.Factory{
1158			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1159		},
1160		State:    state,
1161		PlanMode: plans.DestroyMode,
1162	})
1163
1164	if p, diags := ctx.Plan(); diags.HasErrors() {
1165		logDiagnostics(t, diags)
1166		t.Fatal("plan failed")
1167	} else {
1168		t.Logf("plan:\n\n%s", legacyDiffComparisonString(p.Changes))
1169	}
1170
1171	if _, diags := ctx.Apply(); diags.HasErrors() {
1172		logDiagnostics(t, diags)
1173		t.Fatal("apply failed")
1174	}
1175}
1176
1177// Test that the destroy operation uses depends_on as a source of ordering.
1178func TestContext2Apply_destroyDependsOn(t *testing.T) {
1179	// It is possible for this to be racy, so we loop a number of times
1180	// just to check.
1181	for i := 0; i < 10; i++ {
1182		testContext2Apply_destroyDependsOn(t)
1183	}
1184}
1185
1186func testContext2Apply_destroyDependsOn(t *testing.T) {
1187	m := testModule(t, "apply-destroy-depends-on")
1188	p := testProvider("aws")
1189	p.PlanResourceChangeFn = testDiffFn
1190
1191	state := states.NewState()
1192	root := state.EnsureModule(addrs.RootModuleInstance)
1193	root.SetResourceInstanceCurrent(
1194		mustResourceInstanceAddr("aws_instance.bar").Resource,
1195		&states.ResourceInstanceObjectSrc{
1196			Status:    states.ObjectReady,
1197			AttrsJSON: []byte(`{"id":"bar"}`),
1198		},
1199		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1200	)
1201	root.SetResourceInstanceCurrent(
1202		mustResourceInstanceAddr("aws_instance.foo").Resource,
1203		&states.ResourceInstanceObjectSrc{
1204			Status:       states.ObjectReady,
1205			AttrsJSON:    []byte(`{"id":"foo"}`),
1206			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.bar")},
1207		},
1208		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1209	)
1210
1211	// Record the order we see Apply
1212	var actual []string
1213	var actualLock sync.Mutex
1214	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
1215		actualLock.Lock()
1216		defer actualLock.Unlock()
1217		id := req.PriorState.GetAttr("id").AsString()
1218		actual = append(actual, id)
1219
1220		return testApplyFn(req)
1221	}
1222
1223	ctx := testContext2(t, &ContextOpts{
1224		Config: m,
1225		Providers: map[addrs.Provider]providers.Factory{
1226			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1227		},
1228		State:       state,
1229		PlanMode:    plans.DestroyMode,
1230		Parallelism: 1, // To check ordering
1231	})
1232
1233	if _, diags := ctx.Plan(); diags.HasErrors() {
1234		t.Fatalf("plan errors: %s", diags.Err())
1235	}
1236
1237	if _, diags := ctx.Apply(); diags.HasErrors() {
1238		t.Fatalf("apply errors: %s", diags.Err())
1239	}
1240
1241	expected := []string{"foo", "bar"}
1242	if !reflect.DeepEqual(actual, expected) {
1243		t.Fatalf("wrong order\ngot:  %#v\nwant: %#v", actual, expected)
1244	}
1245}
1246
1247// Test that destroy ordering is correct with dependencies only
1248// in the state.
1249func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
1250	newState := states.NewState()
1251	root := newState.EnsureModule(addrs.RootModuleInstance)
1252	root.SetResourceInstanceCurrent(
1253		addrs.Resource{
1254			Mode: addrs.ManagedResourceMode,
1255			Type: "aws_instance",
1256			Name: "foo",
1257		}.Instance(addrs.NoKey),
1258		&states.ResourceInstanceObjectSrc{
1259			Status:       states.ObjectReady,
1260			AttrsJSON:    []byte(`{"id":"foo"}`),
1261			Dependencies: []addrs.ConfigResource{},
1262		},
1263		addrs.AbsProviderConfig{
1264			Provider: addrs.NewDefaultProvider("aws"),
1265			Module:   addrs.RootModule,
1266		},
1267	)
1268	root.SetResourceInstanceCurrent(
1269		addrs.Resource{
1270			Mode: addrs.ManagedResourceMode,
1271			Type: "aws_instance",
1272			Name: "bar",
1273		}.Instance(addrs.NoKey),
1274		&states.ResourceInstanceObjectSrc{
1275			Status:    states.ObjectReady,
1276			AttrsJSON: []byte(`{"id":"bar"}`),
1277			Dependencies: []addrs.ConfigResource{
1278				addrs.ConfigResource{
1279					Resource: addrs.Resource{
1280						Mode: addrs.ManagedResourceMode,
1281						Type: "aws_instance",
1282						Name: "foo",
1283					},
1284					Module: root.Addr.Module(),
1285				},
1286			},
1287		},
1288		addrs.AbsProviderConfig{
1289			Provider: addrs.NewDefaultProvider("aws"),
1290			Module:   addrs.RootModule,
1291		},
1292	)
1293
1294	// It is possible for this to be racy, so we loop a number of times
1295	// just to check.
1296	for i := 0; i < 10; i++ {
1297		t.Run("new", func(t *testing.T) {
1298			testContext2Apply_destroyDependsOnStateOnly(t, newState)
1299		})
1300	}
1301}
1302
1303func testContext2Apply_destroyDependsOnStateOnly(t *testing.T, state *states.State) {
1304	state = state.DeepCopy()
1305	m := testModule(t, "empty")
1306	p := testProvider("aws")
1307	p.PlanResourceChangeFn = testDiffFn
1308	// Record the order we see Apply
1309	var actual []string
1310	var actualLock sync.Mutex
1311	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
1312		actualLock.Lock()
1313		defer actualLock.Unlock()
1314		id := req.PriorState.GetAttr("id").AsString()
1315		actual = append(actual, id)
1316		return testApplyFn(req)
1317	}
1318
1319	ctx := testContext2(t, &ContextOpts{
1320		Config: m,
1321		Providers: map[addrs.Provider]providers.Factory{
1322			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1323		},
1324		State:       state,
1325		PlanMode:    plans.DestroyMode,
1326		Parallelism: 1, // To check ordering
1327	})
1328
1329	if _, diags := ctx.Plan(); diags.HasErrors() {
1330		t.Fatalf("plan errors: %s", diags.Err())
1331	}
1332
1333	if _, diags := ctx.Apply(); diags.HasErrors() {
1334		t.Fatalf("apply errors: %s", diags.Err())
1335	}
1336
1337	expected := []string{"bar", "foo"}
1338	if !reflect.DeepEqual(actual, expected) {
1339		t.Fatalf("wrong order\ngot:  %#v\nwant: %#v", actual, expected)
1340	}
1341}
1342
1343// Test that destroy ordering is correct with dependencies only
1344// in the state within a module (GH-11749)
1345func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
1346	newState := states.NewState()
1347	child := newState.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
1348	child.SetResourceInstanceCurrent(
1349		addrs.Resource{
1350			Mode: addrs.ManagedResourceMode,
1351			Type: "aws_instance",
1352			Name: "foo",
1353		}.Instance(addrs.NoKey),
1354		&states.ResourceInstanceObjectSrc{
1355			Status:       states.ObjectReady,
1356			AttrsJSON:    []byte(`{"id":"foo"}`),
1357			Dependencies: []addrs.ConfigResource{},
1358		},
1359		addrs.AbsProviderConfig{
1360			Provider: addrs.NewDefaultProvider("aws"),
1361			Module:   addrs.RootModule,
1362		},
1363	)
1364	child.SetResourceInstanceCurrent(
1365		addrs.Resource{
1366			Mode: addrs.ManagedResourceMode,
1367			Type: "aws_instance",
1368			Name: "bar",
1369		}.Instance(addrs.NoKey),
1370		&states.ResourceInstanceObjectSrc{
1371			Status:    states.ObjectReady,
1372			AttrsJSON: []byte(`{"id":"bar"}`),
1373			Dependencies: []addrs.ConfigResource{
1374				addrs.ConfigResource{
1375					Resource: addrs.Resource{
1376						Mode: addrs.ManagedResourceMode,
1377						Type: "aws_instance",
1378						Name: "foo",
1379					},
1380					Module: child.Addr.Module(),
1381				},
1382			},
1383		},
1384		addrs.AbsProviderConfig{
1385			Provider: addrs.NewDefaultProvider("aws"),
1386			Module:   addrs.RootModule,
1387		},
1388	)
1389
1390	// It is possible for this to be racy, so we loop a number of times
1391	// just to check.
1392	for i := 0; i < 10; i++ {
1393		t.Run("new", func(t *testing.T) {
1394			testContext2Apply_destroyDependsOnStateOnlyModule(t, newState)
1395		})
1396	}
1397}
1398
1399func testContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T, state *states.State) {
1400	state = state.DeepCopy()
1401	m := testModule(t, "empty")
1402	p := testProvider("aws")
1403	p.PlanResourceChangeFn = testDiffFn
1404
1405	// Record the order we see Apply
1406	var actual []string
1407	var actualLock sync.Mutex
1408	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
1409		actualLock.Lock()
1410		defer actualLock.Unlock()
1411		id := req.PriorState.GetAttr("id").AsString()
1412		actual = append(actual, id)
1413		return testApplyFn(req)
1414	}
1415
1416	ctx := testContext2(t, &ContextOpts{
1417		Config: m,
1418		Providers: map[addrs.Provider]providers.Factory{
1419			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1420		},
1421		State:       state,
1422		PlanMode:    plans.DestroyMode,
1423		Parallelism: 1, // To check ordering
1424	})
1425
1426	if _, diags := ctx.Plan(); diags.HasErrors() {
1427		t.Fatalf("plan errors: %s", diags.Err())
1428	}
1429
1430	if _, diags := ctx.Apply(); diags.HasErrors() {
1431		t.Fatalf("apply errors: %s", diags.Err())
1432	}
1433
1434	expected := []string{"bar", "foo"}
1435	if !reflect.DeepEqual(actual, expected) {
1436		t.Fatalf("wrong order\ngot:  %#v\nwant: %#v", actual, expected)
1437	}
1438}
1439
1440func TestContext2Apply_dataBasic(t *testing.T) {
1441	m := testModule(t, "apply-data-basic")
1442	p := testProvider("null")
1443	p.PlanResourceChangeFn = testDiffFn
1444	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
1445		State: cty.ObjectVal(map[string]cty.Value{
1446			"id":  cty.StringVal("yo"),
1447			"foo": cty.NullVal(cty.String),
1448		}),
1449	}
1450
1451	ctx := testContext2(t, &ContextOpts{
1452		Config: m,
1453		Providers: map[addrs.Provider]providers.Factory{
1454			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
1455		},
1456	})
1457
1458	if p, diags := ctx.Plan(); diags.HasErrors() {
1459		t.Fatalf("diags: %s", diags.Err())
1460	} else {
1461		t.Logf(legacyDiffComparisonString(p.Changes))
1462	}
1463
1464	state, diags := ctx.Apply()
1465	assertNoErrors(t, diags)
1466
1467	actual := strings.TrimSpace(state.String())
1468	expected := strings.TrimSpace(testTerraformApplyDataBasicStr)
1469	if actual != expected {
1470		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
1471	}
1472}
1473
1474func TestContext2Apply_destroyData(t *testing.T) {
1475	m := testModule(t, "apply-destroy-data-resource")
1476	p := testProvider("null")
1477	p.PlanResourceChangeFn = testDiffFn
1478	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
1479		return providers.ReadDataSourceResponse{
1480			State: req.Config,
1481		}
1482	}
1483
1484	state := states.NewState()
1485	root := state.EnsureModule(addrs.RootModuleInstance)
1486	root.SetResourceInstanceCurrent(
1487		mustResourceInstanceAddr("data.null_data_source.testing").Resource,
1488		&states.ResourceInstanceObjectSrc{
1489			Status:    states.ObjectReady,
1490			AttrsJSON: []byte(`{"id":"-"}`),
1491		},
1492		mustProviderConfig(`provider["registry.terraform.io/hashicorp/null"]`),
1493	)
1494
1495	hook := &testHook{}
1496	ctx := testContext2(t, &ContextOpts{
1497		Config: m,
1498		Providers: map[addrs.Provider]providers.Factory{
1499			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
1500		},
1501		State:    state,
1502		PlanMode: plans.DestroyMode,
1503		Hooks:    []Hook{hook},
1504	})
1505
1506	if p, diags := ctx.Plan(); diags.HasErrors() {
1507		t.Fatalf("diags: %s", diags.Err())
1508	} else {
1509		t.Logf(legacyDiffComparisonString(p.Changes))
1510	}
1511
1512	newState, diags := ctx.Apply()
1513	if diags.HasErrors() {
1514		t.Fatalf("diags: %s", diags.Err())
1515	}
1516
1517	if got := len(newState.Modules); got != 1 {
1518		t.Fatalf("state has %d modules after destroy; want 1", got)
1519	}
1520
1521	if got := len(newState.RootModule().Resources); got != 0 {
1522		t.Fatalf("state has %d resources after destroy; want 0", got)
1523	}
1524
1525	wantHookCalls := []*testHookCall{
1526		{"PreDiff", "data.null_data_source.testing"},
1527		{"PostDiff", "data.null_data_source.testing"},
1528		{"PreDiff", "data.null_data_source.testing"},
1529		{"PostDiff", "data.null_data_source.testing"},
1530		{"PostStateUpdate", ""},
1531	}
1532	if !reflect.DeepEqual(hook.Calls, wantHookCalls) {
1533		t.Errorf("wrong hook calls\ngot: %swant: %s", spew.Sdump(hook.Calls), spew.Sdump(wantHookCalls))
1534	}
1535}
1536
1537// https://github.com/hashicorp/terraform/pull/5096
1538func TestContext2Apply_destroySkipsCBD(t *testing.T) {
1539	// Config contains CBD resource depending on non-CBD resource, which triggers
1540	// a cycle if they are both replaced, but should _not_ trigger a cycle when
1541	// just doing a `terraform destroy`.
1542	m := testModule(t, "apply-destroy-cbd")
1543	p := testProvider("aws")
1544	p.PlanResourceChangeFn = testDiffFn
1545	state := states.NewState()
1546	root := state.EnsureModule(addrs.RootModuleInstance)
1547	root.SetResourceInstanceCurrent(
1548		mustResourceInstanceAddr("aws_instance.foo").Resource,
1549		&states.ResourceInstanceObjectSrc{
1550			Status:    states.ObjectReady,
1551			AttrsJSON: []byte(`{"id":"foo"}`),
1552		},
1553		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1554	)
1555	root.SetResourceInstanceCurrent(
1556		mustResourceInstanceAddr("aws_instance.bar").Resource,
1557		&states.ResourceInstanceObjectSrc{
1558			Status:    states.ObjectReady,
1559			AttrsJSON: []byte(`{"id":"foo"}`),
1560		},
1561		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1562	)
1563
1564	ctx := testContext2(t, &ContextOpts{
1565		Config: m,
1566		Providers: map[addrs.Provider]providers.Factory{
1567			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1568		},
1569		State:    state,
1570		PlanMode: plans.DestroyMode,
1571	})
1572
1573	if p, diags := ctx.Plan(); diags.HasErrors() {
1574		t.Fatalf("diags: %s", diags.Err())
1575	} else {
1576		t.Logf(legacyDiffComparisonString(p.Changes))
1577	}
1578
1579	if _, diags := ctx.Apply(); diags.HasErrors() {
1580		t.Fatalf("apply errors: %s", diags.Err())
1581	}
1582}
1583
1584func TestContext2Apply_destroyModuleVarProviderConfig(t *testing.T) {
1585	m := testModule(t, "apply-destroy-mod-var-provider-config")
1586	p := testProvider("aws")
1587	p.PlanResourceChangeFn = testDiffFn
1588	state := states.NewState()
1589	root := state.EnsureModule(addrs.RootModuleInstance)
1590	root.SetResourceInstanceCurrent(
1591		mustResourceInstanceAddr("aws_instance.foo").Resource,
1592		&states.ResourceInstanceObjectSrc{
1593			Status:    states.ObjectReady,
1594			AttrsJSON: []byte(`{"id":"foo"}`),
1595		},
1596		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1597	)
1598	ctx := testContext2(t, &ContextOpts{
1599		Config: m,
1600		Providers: map[addrs.Provider]providers.Factory{
1601			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1602		},
1603		State:    state,
1604		PlanMode: plans.DestroyMode,
1605	})
1606
1607	if _, diags := ctx.Plan(); diags.HasErrors() {
1608		t.Fatalf("plan errors: %s", diags.Err())
1609	}
1610
1611	_, diags := ctx.Apply()
1612	if diags.HasErrors() {
1613		t.Fatalf("diags: %s", diags.Err())
1614	}
1615}
1616
1617func TestContext2Apply_destroyCrossProviders(t *testing.T) {
1618	m := testModule(t, "apply-destroy-cross-providers")
1619
1620	p_aws := testProvider("aws")
1621	p_aws.ApplyResourceChangeFn = testApplyFn
1622	p_aws.PlanResourceChangeFn = testDiffFn
1623	p_aws.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
1624		ResourceTypes: map[string]*configschema.Block{
1625			"aws_instance": {
1626				Attributes: map[string]*configschema.Attribute{
1627					"id": {
1628						Type:     cty.String,
1629						Computed: true,
1630					},
1631				},
1632			},
1633			"aws_vpc": {
1634				Attributes: map[string]*configschema.Attribute{
1635					"id": {
1636						Type:     cty.String,
1637						Computed: true,
1638					},
1639					"value": {
1640						Type:     cty.String,
1641						Optional: true,
1642					},
1643				},
1644			},
1645		},
1646	})
1647
1648	providers := map[addrs.Provider]providers.Factory{
1649		addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p_aws),
1650	}
1651
1652	ctx := getContextForApply_destroyCrossProviders(t, m, providers)
1653
1654	if _, diags := ctx.Plan(); diags.HasErrors() {
1655		logDiagnostics(t, diags)
1656		t.Fatal("plan failed")
1657	}
1658
1659	if _, diags := ctx.Apply(); diags.HasErrors() {
1660		logDiagnostics(t, diags)
1661		t.Fatal("apply failed")
1662	}
1663}
1664
1665func getContextForApply_destroyCrossProviders(t *testing.T, m *configs.Config, providerFactories map[addrs.Provider]providers.Factory) *Context {
1666	state := states.NewState()
1667	root := state.EnsureModule(addrs.RootModuleInstance)
1668	root.SetResourceInstanceCurrent(
1669		mustResourceInstanceAddr("aws_instance.shared").Resource,
1670		&states.ResourceInstanceObjectSrc{
1671			Status:    states.ObjectReady,
1672			AttrsJSON: []byte(`{"id":"test"}`),
1673		},
1674		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1675	)
1676	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
1677	child.SetResourceInstanceCurrent(
1678		mustResourceInstanceAddr("aws_vpc.bar").Resource,
1679		&states.ResourceInstanceObjectSrc{
1680			Status:    states.ObjectReady,
1681			AttrsJSON: []byte(`{"id": "vpc-aaabbb12", "value":"test"}`),
1682		},
1683		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
1684	)
1685
1686	ctx := testContext2(t, &ContextOpts{
1687		Config:    m,
1688		Providers: providerFactories,
1689		State:     state,
1690		PlanMode:  plans.DestroyMode,
1691	})
1692
1693	return ctx
1694}
1695
1696func TestContext2Apply_minimal(t *testing.T) {
1697	m := testModule(t, "apply-minimal")
1698	p := testProvider("aws")
1699	p.PlanResourceChangeFn = testDiffFn
1700	p.ApplyResourceChangeFn = testApplyFn
1701	ctx := testContext2(t, &ContextOpts{
1702		Config: m,
1703		Providers: map[addrs.Provider]providers.Factory{
1704			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1705		},
1706	})
1707
1708	if _, diags := ctx.Plan(); diags.HasErrors() {
1709		t.Fatalf("plan errors: %s", diags.Err())
1710	}
1711
1712	state, diags := ctx.Apply()
1713	if diags.HasErrors() {
1714		t.Fatalf("diags: %s", diags.Err())
1715	}
1716
1717	actual := strings.TrimSpace(state.String())
1718	expected := strings.TrimSpace(testTerraformApplyMinimalStr)
1719	if actual != expected {
1720		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
1721	}
1722}
1723
1724func TestContext2Apply_cancel(t *testing.T) {
1725	stopped := false
1726
1727	m := testModule(t, "apply-cancel")
1728	p := testProvider("aws")
1729	ctx := testContext2(t, &ContextOpts{
1730		Config: m,
1731		Providers: map[addrs.Provider]providers.Factory{
1732			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1733		},
1734	})
1735
1736	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
1737		if !stopped {
1738			stopped = true
1739			go ctx.Stop()
1740
1741			for {
1742				if ctx.sh.Stopped() {
1743					break
1744				}
1745				time.Sleep(10 * time.Millisecond)
1746			}
1747		}
1748		return testApplyFn(req)
1749	}
1750	p.PlanResourceChangeFn = testDiffFn
1751
1752	if _, diags := ctx.Plan(); diags.HasErrors() {
1753		t.Fatalf("plan errors: %s", diags.Err())
1754	}
1755
1756	// Start the Apply in a goroutine
1757	var applyDiags tfdiags.Diagnostics
1758	stateCh := make(chan *states.State)
1759	go func() {
1760		state, diags := ctx.Apply()
1761		applyDiags = diags
1762
1763		stateCh <- state
1764	}()
1765
1766	state := <-stateCh
1767	// only expecting an early exit error
1768	if !applyDiags.HasErrors() {
1769		t.Fatal("expected early exit error")
1770	}
1771
1772	for _, d := range applyDiags {
1773		desc := d.Description()
1774		if desc.Summary != "execution halted" {
1775			t.Fatalf("unexpected error: %v", applyDiags.Err())
1776		}
1777	}
1778
1779	actual := strings.TrimSpace(state.String())
1780	expected := strings.TrimSpace(testTerraformApplyCancelStr)
1781	if actual != expected {
1782		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
1783	}
1784
1785	if !p.StopCalled {
1786		t.Fatal("stop should be called")
1787	}
1788}
1789
1790func TestContext2Apply_cancelBlock(t *testing.T) {
1791	m := testModule(t, "apply-cancel-block")
1792	p := testProvider("aws")
1793	ctx := testContext2(t, &ContextOpts{
1794		Config: m,
1795		Providers: map[addrs.Provider]providers.Factory{
1796			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1797		},
1798	})
1799
1800	applyCh := make(chan struct{})
1801	p.PlanResourceChangeFn = testDiffFn
1802	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
1803		close(applyCh)
1804
1805		for !ctx.sh.Stopped() {
1806			// Wait for stop to be called. We call Gosched here so that
1807			// the other goroutines can always be scheduled to set Stopped.
1808			runtime.Gosched()
1809		}
1810
1811		// Sleep
1812		time.Sleep(100 * time.Millisecond)
1813		return testApplyFn(req)
1814	}
1815
1816	if _, diags := ctx.Plan(); diags.HasErrors() {
1817		t.Fatalf("plan errors: %s", diags.Err())
1818	}
1819
1820	// Start the Apply in a goroutine
1821	var applyDiags tfdiags.Diagnostics
1822	stateCh := make(chan *states.State)
1823	go func() {
1824		state, diags := ctx.Apply()
1825		applyDiags = diags
1826
1827		stateCh <- state
1828	}()
1829
1830	stopDone := make(chan struct{})
1831	go func() {
1832		defer close(stopDone)
1833		<-applyCh
1834		ctx.Stop()
1835	}()
1836
1837	// Make sure that stop blocks
1838	select {
1839	case <-stopDone:
1840		t.Fatal("stop should block")
1841	case <-time.After(10 * time.Millisecond):
1842	}
1843
1844	// Wait for stop
1845	select {
1846	case <-stopDone:
1847	case <-time.After(500 * time.Millisecond):
1848		t.Fatal("stop should be done")
1849	}
1850
1851	// Wait for apply to complete
1852	state := <-stateCh
1853	// only expecting an early exit error
1854	if !applyDiags.HasErrors() {
1855		t.Fatal("expected early exit error")
1856	}
1857
1858	for _, d := range applyDiags {
1859		desc := d.Description()
1860		if desc.Summary != "execution halted" {
1861			t.Fatalf("unexpected error: %v", applyDiags.Err())
1862		}
1863	}
1864
1865	checkStateString(t, state, `
1866aws_instance.foo:
1867  ID = foo
1868  provider = provider["registry.terraform.io/hashicorp/aws"]
1869  num = 2
1870  type = aws_instance
1871	`)
1872}
1873
1874func TestContext2Apply_cancelProvisioner(t *testing.T) {
1875	m := testModule(t, "apply-cancel-provisioner")
1876	p := testProvider("aws")
1877	p.PlanResourceChangeFn = testDiffFn
1878	p.ApplyResourceChangeFn = testApplyFn
1879
1880	pr := testProvisioner()
1881	pr.GetSchemaResponse = provisioners.GetSchemaResponse{
1882		Provisioner: &configschema.Block{
1883			Attributes: map[string]*configschema.Attribute{
1884				"foo": {
1885					Type:     cty.String,
1886					Optional: true,
1887				},
1888			},
1889		},
1890	}
1891
1892	ctx := testContext2(t, &ContextOpts{
1893		Config: m,
1894		Providers: map[addrs.Provider]providers.Factory{
1895			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
1896		},
1897		Provisioners: map[string]provisioners.Factory{
1898			"shell": testProvisionerFuncFixed(pr),
1899		},
1900	})
1901
1902	prStopped := make(chan struct{})
1903	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
1904		// Start the stop process
1905		go ctx.Stop()
1906
1907		<-prStopped
1908		return
1909	}
1910	pr.StopFn = func() error {
1911		close(prStopped)
1912		return nil
1913	}
1914
1915	if _, diags := ctx.Plan(); diags.HasErrors() {
1916		t.Fatalf("plan errors: %s", diags.Err())
1917	}
1918
1919	// Start the Apply in a goroutine
1920	var applyDiags tfdiags.Diagnostics
1921	stateCh := make(chan *states.State)
1922	go func() {
1923		state, diags := ctx.Apply()
1924		applyDiags = diags
1925
1926		stateCh <- state
1927	}()
1928
1929	// Wait for completion
1930	state := <-stateCh
1931
1932	// we are expecting only an early exit error
1933	if !applyDiags.HasErrors() {
1934		t.Fatal("expected early exit error")
1935	}
1936
1937	for _, d := range applyDiags {
1938		desc := d.Description()
1939		if desc.Summary != "execution halted" {
1940			t.Fatalf("unexpected error: %v", applyDiags.Err())
1941		}
1942	}
1943
1944	checkStateString(t, state, `
1945aws_instance.foo: (tainted)
1946  ID = foo
1947  provider = provider["registry.terraform.io/hashicorp/aws"]
1948  num = 2
1949  type = aws_instance
1950	`)
1951
1952	if !pr.StopCalled {
1953		t.Fatal("stop should be called")
1954	}
1955}
1956
1957func TestContext2Apply_compute(t *testing.T) {
1958	m := testModule(t, "apply-compute")
1959	p := testProvider("aws")
1960	p.PlanResourceChangeFn = testDiffFn
1961	p.ApplyResourceChangeFn = testApplyFn
1962	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
1963		ResourceTypes: map[string]*configschema.Block{
1964			"aws_instance": {
1965				Attributes: map[string]*configschema.Attribute{
1966					"num": {
1967						Type:     cty.Number,
1968						Optional: true,
1969					},
1970					"compute": {
1971						Type:     cty.String,
1972						Optional: true,
1973					},
1974					"compute_value": {
1975						Type:     cty.String,
1976						Optional: true,
1977					},
1978					"foo": {
1979						Type:     cty.String,
1980						Optional: true,
1981					},
1982					"id": {
1983						Type:     cty.String,
1984						Computed: true,
1985					},
1986					"type": {
1987						Type:     cty.String,
1988						Computed: true,
1989					},
1990					"value": { // Populated from compute_value because compute = "value" in the config fixture
1991						Type:     cty.String,
1992						Computed: true,
1993					},
1994				},
1995			},
1996		},
1997	})
1998
1999	ctx := testContext2(t, &ContextOpts{
2000		Config: m,
2001		Providers: map[addrs.Provider]providers.Factory{
2002			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2003		},
2004	})
2005
2006	ctx.variables = InputValues{
2007		"value": &InputValue{
2008			Value:      cty.NumberIntVal(1),
2009			SourceType: ValueFromCaller,
2010		},
2011	}
2012
2013	if _, diags := ctx.Plan(); diags.HasErrors() {
2014		t.Fatalf("plan errors: %s", diags.Err())
2015	}
2016
2017	state, diags := ctx.Apply()
2018	if diags.HasErrors() {
2019		t.Fatalf("unexpected errors: %s", diags.Err())
2020	}
2021
2022	actual := strings.TrimSpace(state.String())
2023	expected := strings.TrimSpace(testTerraformApplyComputeStr)
2024	if actual != expected {
2025		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2026	}
2027}
2028
2029func TestContext2Apply_countDecrease(t *testing.T) {
2030	m := testModule(t, "apply-count-dec")
2031	p := testProvider("aws")
2032	p.PlanResourceChangeFn = testDiffFn
2033	p.ApplyResourceChangeFn = testApplyFn
2034	state := states.NewState()
2035	root := state.EnsureModule(addrs.RootModuleInstance)
2036	root.SetResourceInstanceCurrent(
2037		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
2038		&states.ResourceInstanceObjectSrc{
2039			Status:    states.ObjectReady,
2040			AttrsJSON: []byte(`{"id":"bar","foo": "foo","type": "aws_instance"}`),
2041		},
2042		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2043	)
2044	root.SetResourceInstanceCurrent(
2045		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
2046		&states.ResourceInstanceObjectSrc{
2047			Status:    states.ObjectReady,
2048			AttrsJSON: []byte(`{"id":"bar","foo": "foo","type": "aws_instance"}`),
2049		},
2050		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2051	)
2052	root.SetResourceInstanceCurrent(
2053		mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
2054		&states.ResourceInstanceObjectSrc{
2055			Status:    states.ObjectReady,
2056			AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`),
2057		},
2058		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2059	)
2060
2061	ctx := testContext2(t, &ContextOpts{
2062		Config: m,
2063		Providers: map[addrs.Provider]providers.Factory{
2064			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2065		},
2066		State: state,
2067	})
2068
2069	if _, diags := ctx.Plan(); diags.HasErrors() {
2070		logDiagnostics(t, diags)
2071		t.Fatal("plan failed")
2072	}
2073
2074	s, diags := ctx.Apply()
2075	assertNoErrors(t, diags)
2076
2077	actual := strings.TrimSpace(s.String())
2078	expected := strings.TrimSpace(testTerraformApplyCountDecStr)
2079	if actual != expected {
2080		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2081	}
2082}
2083
2084func TestContext2Apply_countDecreaseToOneX(t *testing.T) {
2085	m := testModule(t, "apply-count-dec-one")
2086	p := testProvider("aws")
2087	p.PlanResourceChangeFn = testDiffFn
2088	state := states.NewState()
2089	root := state.EnsureModule(addrs.RootModuleInstance)
2090	root.SetResourceInstanceCurrent(
2091		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
2092		&states.ResourceInstanceObjectSrc{
2093			Status:    states.ObjectReady,
2094			AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`),
2095		},
2096		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2097	)
2098	root.SetResourceInstanceCurrent(
2099		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
2100		&states.ResourceInstanceObjectSrc{
2101			Status:    states.ObjectReady,
2102			AttrsJSON: []byte(`{"id":"bar"}`),
2103		},
2104		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2105	)
2106	root.SetResourceInstanceCurrent(
2107		mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
2108		&states.ResourceInstanceObjectSrc{
2109			Status:    states.ObjectReady,
2110			AttrsJSON: []byte(`{"id":"bar"}`),
2111		},
2112		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2113	)
2114
2115	ctx := testContext2(t, &ContextOpts{
2116		Config: m,
2117		Providers: map[addrs.Provider]providers.Factory{
2118			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2119		},
2120		State: state,
2121	})
2122
2123	if _, diags := ctx.Plan(); diags.HasErrors() {
2124		t.Fatalf("plan errors: %s", diags.Err())
2125	}
2126
2127	s, diags := ctx.Apply()
2128	if diags.HasErrors() {
2129		t.Fatalf("diags: %s", diags.Err())
2130	}
2131
2132	actual := strings.TrimSpace(s.String())
2133	expected := strings.TrimSpace(testTerraformApplyCountDecToOneStr)
2134	if actual != expected {
2135		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2136	}
2137}
2138
2139// https://github.com/PeoplePerHour/terraform/pull/11
2140//
2141// This tests a case where both a "resource" and "resource.0" are in
2142// the state file, which apparently is a reasonable backwards compatibility
2143// concern found in the above 3rd party repo.
2144func TestContext2Apply_countDecreaseToOneCorrupted(t *testing.T) {
2145	m := testModule(t, "apply-count-dec-one")
2146	p := testProvider("aws")
2147	p.PlanResourceChangeFn = testDiffFn
2148	state := states.NewState()
2149	root := state.EnsureModule(addrs.RootModuleInstance)
2150	root.SetResourceInstanceCurrent(
2151		mustResourceInstanceAddr("aws_instance.foo").Resource,
2152		&states.ResourceInstanceObjectSrc{
2153			Status:    states.ObjectReady,
2154			AttrsJSON: []byte(`{"id":"bar", "foo": "foo", "type": "aws_instance"}`),
2155		},
2156		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2157	)
2158	root.SetResourceInstanceCurrent(
2159		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
2160		&states.ResourceInstanceObjectSrc{
2161			Status:    states.ObjectReady,
2162			AttrsJSON: []byte(`{"id":"baz", "type": "aws_instance"}`),
2163		},
2164		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2165	)
2166
2167	ctx := testContext2(t, &ContextOpts{
2168		Config: m,
2169		Providers: map[addrs.Provider]providers.Factory{
2170			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2171		},
2172		State: state,
2173	})
2174
2175	if p, diags := ctx.Plan(); diags.HasErrors() {
2176		t.Fatalf("diags: %s", diags.Err())
2177	} else {
2178		got := strings.TrimSpace(legacyPlanComparisonString(ctx.State(), p.Changes))
2179		want := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedPlanStr)
2180		if got != want {
2181			t.Fatalf("wrong plan result\ngot:\n%s\nwant:\n%s", got, want)
2182		}
2183	}
2184
2185	s, diags := ctx.Apply()
2186	if diags.HasErrors() {
2187		t.Fatalf("diags: %s", diags.Err())
2188	}
2189
2190	actual := strings.TrimSpace(s.String())
2191	expected := strings.TrimSpace(testTerraformApplyCountDecToOneCorruptedStr)
2192	if actual != expected {
2193		t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2194	}
2195}
2196
2197func TestContext2Apply_countTainted(t *testing.T) {
2198	m := testModule(t, "apply-count-tainted")
2199	p := testProvider("aws")
2200	p.PlanResourceChangeFn = testDiffFn
2201	p.ApplyResourceChangeFn = testApplyFn
2202	state := states.NewState()
2203	root := state.EnsureModule(addrs.RootModuleInstance)
2204	root.SetResourceInstanceCurrent(
2205		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
2206		&states.ResourceInstanceObjectSrc{
2207			Status:    states.ObjectTainted,
2208			AttrsJSON: []byte(`{"id":"bar", "type": "aws_instance", "foo": "foo"}`),
2209		},
2210		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2211	)
2212	ctx := testContext2(t, &ContextOpts{
2213		Config: m,
2214		Providers: map[addrs.Provider]providers.Factory{
2215			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2216		},
2217		State: state,
2218	})
2219
2220	{
2221		plan, diags := ctx.Plan()
2222		assertNoErrors(t, diags)
2223		got := strings.TrimSpace(legacyDiffComparisonString(plan.Changes))
2224		want := strings.TrimSpace(`
2225DESTROY/CREATE: aws_instance.foo[0]
2226  foo:  "foo" => "foo"
2227  id:   "bar" => "<computed>"
2228  type: "aws_instance" => "<computed>"
2229CREATE: aws_instance.foo[1]
2230  foo:  "" => "foo"
2231  id:   "" => "<computed>"
2232  type: "" => "<computed>"
2233`)
2234		if got != want {
2235			t.Fatalf("wrong plan\n\ngot:\n%s\n\nwant:\n%s", got, want)
2236		}
2237	}
2238
2239	s, diags := ctx.Apply()
2240	assertNoErrors(t, diags)
2241
2242	got := strings.TrimSpace(s.String())
2243	want := strings.TrimSpace(`
2244aws_instance.foo.0:
2245  ID = foo
2246  provider = provider["registry.terraform.io/hashicorp/aws"]
2247  foo = foo
2248  type = aws_instance
2249aws_instance.foo.1:
2250  ID = foo
2251  provider = provider["registry.terraform.io/hashicorp/aws"]
2252  foo = foo
2253  type = aws_instance
2254`)
2255	if got != want {
2256		t.Fatalf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", got, want)
2257	}
2258}
2259
2260func TestContext2Apply_countVariable(t *testing.T) {
2261	m := testModule(t, "apply-count-variable")
2262	p := testProvider("aws")
2263	p.PlanResourceChangeFn = testDiffFn
2264	p.ApplyResourceChangeFn = testApplyFn
2265	ctx := testContext2(t, &ContextOpts{
2266		Config: m,
2267		Providers: map[addrs.Provider]providers.Factory{
2268			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2269		},
2270	})
2271
2272	if _, diags := ctx.Plan(); diags.HasErrors() {
2273		t.Fatalf("plan errors: %s", diags.Err())
2274	}
2275
2276	state, diags := ctx.Apply()
2277	if diags.HasErrors() {
2278		t.Fatalf("diags: %s", diags.Err())
2279	}
2280
2281	actual := strings.TrimSpace(state.String())
2282	expected := strings.TrimSpace(testTerraformApplyCountVariableStr)
2283	if actual != expected {
2284		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2285	}
2286}
2287
2288func TestContext2Apply_countVariableRef(t *testing.T) {
2289	m := testModule(t, "apply-count-variable-ref")
2290	p := testProvider("aws")
2291	p.PlanResourceChangeFn = testDiffFn
2292	p.ApplyResourceChangeFn = testApplyFn
2293	ctx := testContext2(t, &ContextOpts{
2294		Config: m,
2295		Providers: map[addrs.Provider]providers.Factory{
2296			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2297		},
2298	})
2299
2300	if _, diags := ctx.Plan(); diags.HasErrors() {
2301		t.Fatalf("plan errors: %s", diags.Err())
2302	}
2303
2304	state, diags := ctx.Apply()
2305	if diags.HasErrors() {
2306		t.Fatalf("diags: %s", diags.Err())
2307	}
2308
2309	actual := strings.TrimSpace(state.String())
2310	expected := strings.TrimSpace(testTerraformApplyCountVariableRefStr)
2311	if actual != expected {
2312		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2313	}
2314}
2315
2316func TestContext2Apply_provisionerInterpCount(t *testing.T) {
2317	// This test ensures that a provisioner can interpolate a resource count
2318	// even though the provisioner expression is evaluated during the plan
2319	// walk. https://github.com/hashicorp/terraform/issues/16840
2320
2321	m, snap := testModuleWithSnapshot(t, "apply-provisioner-interp-count")
2322
2323	p := testProvider("aws")
2324	p.PlanResourceChangeFn = testDiffFn
2325
2326	pr := testProvisioner()
2327
2328	Providers := map[addrs.Provider]providers.Factory{
2329		addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2330	}
2331
2332	provisioners := map[string]provisioners.Factory{
2333		"local-exec": testProvisionerFuncFixed(pr),
2334	}
2335	ctx := testContext2(t, &ContextOpts{
2336		Config:       m,
2337		Providers:    Providers,
2338		Provisioners: provisioners,
2339	})
2340
2341	plan, diags := ctx.Plan()
2342	if diags.HasErrors() {
2343		t.Fatalf("plan failed unexpectedly: %s", diags.Err())
2344	}
2345
2346	// We'll marshal and unmarshal the plan here, to ensure that we have
2347	// a clean new context as would be created if we separately ran
2348	// terraform plan -out=tfplan && terraform apply tfplan
2349	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
2350	if err != nil {
2351		t.Fatal(err)
2352	}
2353	ctxOpts.Providers = Providers
2354	ctxOpts.Provisioners = provisioners
2355	ctx, diags = NewContext(ctxOpts)
2356	if diags.HasErrors() {
2357		t.Fatalf("failed to create context for plan: %s", diags.Err())
2358	}
2359
2360	// Applying the plan should now succeed
2361	_, diags = ctx.Apply()
2362	if diags.HasErrors() {
2363		t.Fatalf("apply failed unexpectedly: %s", diags.Err())
2364	}
2365
2366	// Verify apply was invoked
2367	if !pr.ProvisionResourceCalled {
2368		t.Fatalf("provisioner was not called")
2369	}
2370}
2371
2372func TestContext2Apply_foreachVariable(t *testing.T) {
2373	m := testModule(t, "plan-for-each-unknown-value")
2374	p := testProvider("aws")
2375	p.PlanResourceChangeFn = testDiffFn
2376	p.ApplyResourceChangeFn = testApplyFn
2377	ctx := testContext2(t, &ContextOpts{
2378		Config: m,
2379		Providers: map[addrs.Provider]providers.Factory{
2380			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2381		},
2382		Variables: InputValues{
2383			"foo": &InputValue{
2384				Value: cty.StringVal("hello"),
2385			},
2386		},
2387	})
2388
2389	if _, diags := ctx.Plan(); diags.HasErrors() {
2390		t.Fatalf("plan errors: %s", diags.Err())
2391	}
2392
2393	state, diags := ctx.Apply()
2394	if diags.HasErrors() {
2395		t.Fatalf("diags: %s", diags.Err())
2396	}
2397
2398	actual := strings.TrimSpace(state.String())
2399	expected := strings.TrimSpace(testTerraformApplyForEachVariableStr)
2400	if actual != expected {
2401		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2402	}
2403}
2404
2405func TestContext2Apply_moduleBasic(t *testing.T) {
2406	m := testModule(t, "apply-module")
2407	p := testProvider("aws")
2408	p.PlanResourceChangeFn = testDiffFn
2409	p.ApplyResourceChangeFn = testApplyFn
2410	ctx := testContext2(t, &ContextOpts{
2411		Config: m,
2412		Providers: map[addrs.Provider]providers.Factory{
2413			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2414		},
2415	})
2416
2417	if _, diags := ctx.Plan(); diags.HasErrors() {
2418		t.Fatalf("plan errors: %s", diags.Err())
2419	}
2420
2421	state, diags := ctx.Apply()
2422	if diags.HasErrors() {
2423		t.Fatalf("diags: %s", diags.Err())
2424	}
2425
2426	actual := strings.TrimSpace(state.String())
2427	expected := strings.TrimSpace(testTerraformApplyModuleStr)
2428	if actual != expected {
2429		t.Fatalf("bad, expected:\n%s\n\nactual:\n%s", expected, actual)
2430	}
2431}
2432
2433func TestContext2Apply_moduleDestroyOrder(t *testing.T) {
2434	m := testModule(t, "apply-module-destroy-order")
2435	p := testProvider("aws")
2436	p.PlanResourceChangeFn = testDiffFn
2437
2438	// Create a custom apply function to track the order they were destroyed
2439	var order []string
2440	var orderLock sync.Mutex
2441	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
2442		id := req.PriorState.GetAttr("id").AsString()
2443
2444		if id == "b" {
2445			// Pause briefly to make any race conditions more visible, since
2446			// missing edges here can cause undeterministic ordering.
2447			time.Sleep(100 * time.Millisecond)
2448		}
2449
2450		orderLock.Lock()
2451		defer orderLock.Unlock()
2452
2453		order = append(order, id)
2454		resp.NewState = req.PlannedState
2455		return resp
2456	}
2457
2458	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
2459		ResourceTypes: map[string]*configschema.Block{
2460			"aws_instance": {
2461				Attributes: map[string]*configschema.Attribute{
2462					"id":    {Type: cty.String, Required: true},
2463					"blah":  {Type: cty.String, Optional: true},
2464					"value": {Type: cty.String, Optional: true},
2465				},
2466			},
2467		},
2468	})
2469
2470	state := states.NewState()
2471	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
2472	child.SetResourceInstanceCurrent(
2473		mustResourceInstanceAddr("aws_instance.a").Resource,
2474		&states.ResourceInstanceObjectSrc{
2475			Status:    states.ObjectReady,
2476			AttrsJSON: []byte(`{"id":"a"}`),
2477		},
2478		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2479	)
2480	root := state.EnsureModule(addrs.RootModuleInstance)
2481	root.SetResourceInstanceCurrent(
2482		mustResourceInstanceAddr("aws_instance.b").Resource,
2483		&states.ResourceInstanceObjectSrc{
2484			Status:       states.ObjectReady,
2485			AttrsJSON:    []byte(`{"id":"b"}`),
2486			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.aws_instance.a")},
2487		},
2488		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2489	)
2490
2491	ctx := testContext2(t, &ContextOpts{
2492		Config: m,
2493		Providers: map[addrs.Provider]providers.Factory{
2494			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2495		},
2496		State:    state,
2497		PlanMode: plans.DestroyMode,
2498	})
2499
2500	if _, diags := ctx.Plan(); diags.HasErrors() {
2501		t.Fatalf("plan errors: %s", diags.Err())
2502	}
2503
2504	state, diags := ctx.Apply()
2505	if diags.HasErrors() {
2506		t.Fatalf("diags: %s", diags.Err())
2507	}
2508
2509	expected := []string{"b", "a"}
2510	if !reflect.DeepEqual(order, expected) {
2511		t.Errorf("wrong order\ngot: %#v\nwant: %#v", order, expected)
2512	}
2513
2514	{
2515		actual := strings.TrimSpace(state.String())
2516		expected := strings.TrimSpace(testTerraformApplyModuleDestroyOrderStr)
2517		if actual != expected {
2518			t.Errorf("wrong final state\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2519		}
2520	}
2521}
2522
2523func TestContext2Apply_moduleInheritAlias(t *testing.T) {
2524	m := testModule(t, "apply-module-provider-inherit-alias")
2525	p := testProvider("aws")
2526	p.PlanResourceChangeFn = testDiffFn
2527	p.ApplyResourceChangeFn = testApplyFn
2528
2529	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
2530		val := req.Config.GetAttr("value")
2531		if val.IsNull() {
2532			return
2533		}
2534
2535		root := req.Config.GetAttr("root")
2536		if !root.IsNull() {
2537			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("child should not get root"))
2538		}
2539
2540		return
2541	}
2542
2543	ctx := testContext2(t, &ContextOpts{
2544		Config: m,
2545		Providers: map[addrs.Provider]providers.Factory{
2546			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2547		},
2548	})
2549
2550	if _, diags := ctx.Plan(); diags.HasErrors() {
2551		t.Fatalf("plan errors: %s", diags.Err())
2552	}
2553
2554	state, diags := ctx.Apply()
2555	if diags.HasErrors() {
2556		t.Fatalf("diags: %s", diags.Err())
2557	}
2558
2559	checkStateString(t, state, `
2560<no state>
2561module.child:
2562  aws_instance.foo:
2563    ID = foo
2564    provider = provider["registry.terraform.io/hashicorp/aws"].eu
2565    type = aws_instance
2566	`)
2567}
2568
2569func TestContext2Apply_orphanResource(t *testing.T) {
2570	// This is a two-step test:
2571	// 1. Apply a configuration with resources that have count set.
2572	//    This should place the empty resource object in the state to record
2573	//    that each exists, and record any instances.
2574	// 2. Apply an empty configuration against the same state, which should
2575	//    then clean up both the instances and the containing resource objects.
2576	p := testProvider("test")
2577	p.PlanResourceChangeFn = testDiffFn
2578	p.ApplyResourceChangeFn = testApplyFn
2579	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
2580		ResourceTypes: map[string]*configschema.Block{
2581			"test_thing": {
2582				Attributes: map[string]*configschema.Attribute{
2583					"id":  {Type: cty.String, Computed: true},
2584					"foo": {Type: cty.String, Optional: true},
2585				},
2586			},
2587		},
2588	})
2589
2590	// Step 1: create the resources and instances
2591	m := testModule(t, "apply-orphan-resource")
2592	ctx := testContext2(t, &ContextOpts{
2593		Config: m,
2594		Providers: map[addrs.Provider]providers.Factory{
2595			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
2596		},
2597	})
2598	_, diags := ctx.Plan()
2599	assertNoErrors(t, diags)
2600	state, diags := ctx.Apply()
2601	assertNoErrors(t, diags)
2602
2603	// At this point both resources should be recorded in the state, along
2604	// with the single instance associated with test_thing.one.
2605	want := states.BuildState(func(s *states.SyncState) {
2606		providerAddr := addrs.AbsProviderConfig{
2607			Provider: addrs.NewDefaultProvider("test"),
2608			Module:   addrs.RootModule,
2609		}
2610		oneAddr := addrs.Resource{
2611			Mode: addrs.ManagedResourceMode,
2612			Type: "test_thing",
2613			Name: "one",
2614		}.Absolute(addrs.RootModuleInstance)
2615		s.SetResourceProvider(oneAddr, providerAddr)
2616		s.SetResourceInstanceCurrent(oneAddr.Instance(addrs.IntKey(0)), &states.ResourceInstanceObjectSrc{
2617			Status:    states.ObjectReady,
2618			AttrsJSON: []byte(`{"id":"foo"}`),
2619		}, providerAddr)
2620	})
2621
2622	if state.String() != want.String() {
2623		t.Fatalf("wrong state after step 1\n%s", cmp.Diff(want, state))
2624	}
2625
2626	// Step 2: update with an empty config, to destroy everything
2627	m = testModule(t, "empty")
2628	ctx = testContext2(t, &ContextOpts{
2629		Config: m,
2630		State:  state,
2631		Providers: map[addrs.Provider]providers.Factory{
2632			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
2633		},
2634	})
2635	_, diags = ctx.Plan()
2636	assertNoErrors(t, diags)
2637	state, diags = ctx.Apply()
2638	assertNoErrors(t, diags)
2639
2640	// The state should now be _totally_ empty, with just an empty root module
2641	// (since that always exists) and no resources at all.
2642	want = states.NewState()
2643	if !cmp.Equal(state, want) {
2644		t.Fatalf("wrong state after step 2\ngot: %swant: %s", spew.Sdump(state), spew.Sdump(want))
2645	}
2646
2647}
2648
2649func TestContext2Apply_moduleOrphanInheritAlias(t *testing.T) {
2650	m := testModule(t, "apply-module-provider-inherit-alias-orphan")
2651	p := testProvider("aws")
2652	p.PlanResourceChangeFn = testDiffFn
2653
2654	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
2655		val := req.Config.GetAttr("value")
2656		if val.IsNull() {
2657			return
2658		}
2659
2660		root := req.Config.GetAttr("root")
2661		if !root.IsNull() {
2662			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("child should not get root"))
2663		}
2664
2665		return
2666	}
2667
2668	// Create a state with an orphan module
2669	state := states.NewState()
2670	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
2671	child.SetResourceInstanceCurrent(
2672		mustResourceInstanceAddr("aws_instance.bar").Resource,
2673		&states.ResourceInstanceObjectSrc{
2674			Status:    states.ObjectReady,
2675			AttrsJSON: []byte(`{"id":"bar"}`),
2676		},
2677		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2678	)
2679
2680	ctx := testContext2(t, &ContextOpts{
2681		Config: m,
2682		State:  state,
2683		Providers: map[addrs.Provider]providers.Factory{
2684			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2685		},
2686	})
2687
2688	if _, diags := ctx.Plan(); diags.HasErrors() {
2689		t.Fatalf("plan errors: %s", diags.Err())
2690	}
2691
2692	state, diags := ctx.Apply()
2693	if diags.HasErrors() {
2694		t.Fatalf("diags: %s", diags.Err())
2695	}
2696
2697	if !p.ConfigureProviderCalled {
2698		t.Fatal("must call configure")
2699	}
2700
2701	checkStateString(t, state, "<no state>")
2702}
2703
2704func TestContext2Apply_moduleOrphanProvider(t *testing.T) {
2705	m := testModule(t, "apply-module-orphan-provider-inherit")
2706	p := testProvider("aws")
2707	p.PlanResourceChangeFn = testDiffFn
2708
2709	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
2710		val := req.Config.GetAttr("value")
2711		if val.IsNull() {
2712			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found"))
2713		}
2714
2715		return
2716	}
2717
2718	// Create a state with an orphan module
2719	state := states.NewState()
2720	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
2721	child.SetResourceInstanceCurrent(
2722		mustResourceInstanceAddr("aws_instance.bar").Resource,
2723		&states.ResourceInstanceObjectSrc{
2724			Status:    states.ObjectReady,
2725			AttrsJSON: []byte(`{"id":"bar"}`),
2726		},
2727		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2728	)
2729
2730	ctx := testContext2(t, &ContextOpts{
2731		Config: m,
2732		State:  state,
2733		Providers: map[addrs.Provider]providers.Factory{
2734			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2735		},
2736	})
2737
2738	if _, diags := ctx.Plan(); diags.HasErrors() {
2739		t.Fatalf("plan errors: %s", diags.Err())
2740	}
2741
2742	if _, diags := ctx.Apply(); diags.HasErrors() {
2743		t.Fatalf("apply errors: %s", diags.Err())
2744	}
2745}
2746
2747func TestContext2Apply_moduleOrphanGrandchildProvider(t *testing.T) {
2748	m := testModule(t, "apply-module-orphan-provider-inherit")
2749	p := testProvider("aws")
2750	p.PlanResourceChangeFn = testDiffFn
2751
2752	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
2753		val := req.Config.GetAttr("value")
2754		if val.IsNull() {
2755			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found"))
2756		}
2757
2758		return
2759	}
2760
2761	// Create a state with an orphan module that is nested (grandchild)
2762	state := states.NewState()
2763	child := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey).Child("child", addrs.NoKey))
2764	child.SetResourceInstanceCurrent(
2765		mustResourceInstanceAddr("aws_instance.bar").Resource,
2766		&states.ResourceInstanceObjectSrc{
2767			Status:    states.ObjectReady,
2768			AttrsJSON: []byte(`{"id":"bar"}`),
2769		},
2770		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2771	)
2772
2773	ctx := testContext2(t, &ContextOpts{
2774		Config: m,
2775		State:  state,
2776		Providers: map[addrs.Provider]providers.Factory{
2777			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2778		},
2779	})
2780
2781	if _, diags := ctx.Plan(); diags.HasErrors() {
2782		t.Fatalf("plan errors: %s", diags.Err())
2783	}
2784
2785	if _, diags := ctx.Apply(); diags.HasErrors() {
2786		t.Fatalf("apply errors: %s", diags.Err())
2787	}
2788}
2789
2790func TestContext2Apply_moduleGrandchildProvider(t *testing.T) {
2791	m := testModule(t, "apply-module-grandchild-provider-inherit")
2792	p := testProvider("aws")
2793	p.PlanResourceChangeFn = testDiffFn
2794
2795	var callLock sync.Mutex
2796	called := false
2797	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
2798		val := req.Config.GetAttr("value")
2799		if val.IsNull() {
2800			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found"))
2801		}
2802
2803		callLock.Lock()
2804		called = true
2805		callLock.Unlock()
2806
2807		return
2808	}
2809
2810	ctx := testContext2(t, &ContextOpts{
2811		Config: m,
2812		Providers: map[addrs.Provider]providers.Factory{
2813			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2814		},
2815	})
2816
2817	if _, diags := ctx.Plan(); diags.HasErrors() {
2818		t.Fatalf("plan errors: %s", diags.Err())
2819	}
2820
2821	if _, diags := ctx.Apply(); diags.HasErrors() {
2822		t.Fatalf("apply errors: %s", diags.Err())
2823	}
2824
2825	callLock.Lock()
2826	defer callLock.Unlock()
2827	if called != true {
2828		t.Fatalf("err: configure never called")
2829	}
2830}
2831
2832// This tests an issue where all the providers in a module but not
2833// in the root weren't being added to the root properly. In this test
2834// case: aws is explicitly added to root, but "test" should be added to.
2835// With the bug, it wasn't.
2836func TestContext2Apply_moduleOnlyProvider(t *testing.T) {
2837	m := testModule(t, "apply-module-only-provider")
2838	p := testProvider("aws")
2839	p.PlanResourceChangeFn = testDiffFn
2840	p.ApplyResourceChangeFn = testApplyFn
2841	pTest := testProvider("test")
2842	pTest.ApplyResourceChangeFn = testApplyFn
2843	pTest.PlanResourceChangeFn = testDiffFn
2844
2845	ctx := testContext2(t, &ContextOpts{
2846		Config: m,
2847		Providers: map[addrs.Provider]providers.Factory{
2848			addrs.NewDefaultProvider("aws"):  testProviderFuncFixed(p),
2849			addrs.NewDefaultProvider("test"): testProviderFuncFixed(pTest),
2850		},
2851	})
2852
2853	if _, diags := ctx.Plan(); diags.HasErrors() {
2854		t.Fatalf("plan errors: %s", diags.Err())
2855	}
2856
2857	state, diags := ctx.Apply()
2858	if diags.HasErrors() {
2859		t.Fatalf("diags: %s", diags.Err())
2860	}
2861
2862	actual := strings.TrimSpace(state.String())
2863	expected := strings.TrimSpace(testTerraformApplyModuleOnlyProviderStr)
2864	if actual != expected {
2865		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2866	}
2867}
2868
2869func TestContext2Apply_moduleProviderAlias(t *testing.T) {
2870	m := testModule(t, "apply-module-provider-alias")
2871	p := testProvider("aws")
2872	p.PlanResourceChangeFn = testDiffFn
2873	p.ApplyResourceChangeFn = testApplyFn
2874	ctx := testContext2(t, &ContextOpts{
2875		Config: m,
2876		Providers: map[addrs.Provider]providers.Factory{
2877			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2878		},
2879	})
2880
2881	if _, diags := ctx.Plan(); diags.HasErrors() {
2882		t.Fatalf("plan errors: %s", diags.Err())
2883	}
2884
2885	state, diags := ctx.Apply()
2886	if diags.HasErrors() {
2887		t.Fatalf("diags: %s", diags.Err())
2888	}
2889
2890	actual := strings.TrimSpace(state.String())
2891	expected := strings.TrimSpace(testTerraformApplyModuleProviderAliasStr)
2892	if actual != expected {
2893		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2894	}
2895}
2896
2897func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) {
2898	m := testModule(t, "apply-module-provider-alias")
2899	p := testProvider("aws")
2900	p.PlanResourceChangeFn = testDiffFn
2901	ctx := testContext2(t, &ContextOpts{
2902		Config: m,
2903		Providers: map[addrs.Provider]providers.Factory{
2904			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2905		},
2906		Targets: []addrs.Targetable{
2907			addrs.ConfigResource{
2908				Module: addrs.RootModule,
2909				Resource: addrs.Resource{
2910					Mode: addrs.ManagedResourceMode,
2911					Type: "nonexistent",
2912					Name: "thing",
2913				},
2914			},
2915		},
2916	})
2917
2918	if _, diags := ctx.Plan(); diags.HasErrors() {
2919		t.Fatalf("plan errors: %s", diags.Err())
2920	}
2921
2922	state, diags := ctx.Apply()
2923	if diags.HasErrors() {
2924		t.Fatalf("diags: %s", diags.Err())
2925	}
2926
2927	actual := strings.TrimSpace(state.String())
2928	expected := strings.TrimSpace(`
2929<no state>
2930	`)
2931	if actual != expected {
2932		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
2933	}
2934}
2935
2936func TestContext2Apply_moduleProviderCloseNested(t *testing.T) {
2937	m := testModule(t, "apply-module-provider-close-nested")
2938	p := testProvider("aws")
2939	p.PlanResourceChangeFn = testDiffFn
2940	state := states.NewState()
2941	root := state.EnsureModule(addrs.RootModuleInstance)
2942	root.SetResourceInstanceCurrent(
2943		mustResourceInstanceAddr("aws_instance.foo").Resource,
2944		&states.ResourceInstanceObjectSrc{
2945			Status:    states.ObjectReady,
2946			AttrsJSON: []byte(`{"id":"bar"}`),
2947		},
2948		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2949	)
2950
2951	ctx := testContext2(t, &ContextOpts{
2952		Config: m,
2953		Providers: map[addrs.Provider]providers.Factory{
2954			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2955		},
2956		State:    state,
2957		PlanMode: plans.DestroyMode,
2958	})
2959
2960	if _, diags := ctx.Plan(); diags.HasErrors() {
2961		t.Fatalf("plan errors: %s", diags.Err())
2962	}
2963
2964	if _, diags := ctx.Apply(); diags.HasErrors() {
2965		t.Fatalf("apply errors: %s", diags.Err())
2966	}
2967}
2968
2969// Tests that variables used as module vars that reference data that
2970// already exists in the state and requires no diff works properly. This
2971// fixes an issue faced where module variables were pruned because they were
2972// accessing "non-existent" resources (they existed, just not in the graph
2973// cause they weren't in the diff).
2974func TestContext2Apply_moduleVarRefExisting(t *testing.T) {
2975	m := testModule(t, "apply-ref-existing")
2976	p := testProvider("aws")
2977	p.PlanResourceChangeFn = testDiffFn
2978	p.ApplyResourceChangeFn = testApplyFn
2979	state := states.NewState()
2980	root := state.EnsureModule(addrs.RootModuleInstance)
2981	root.SetResourceInstanceCurrent(
2982		mustResourceInstanceAddr("aws_instance.foo").Resource,
2983		&states.ResourceInstanceObjectSrc{
2984			Status:    states.ObjectReady,
2985			AttrsJSON: []byte(`{"id":"foo","foo":"bar"}`),
2986		},
2987		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
2988	)
2989
2990	ctx := testContext2(t, &ContextOpts{
2991		Config: m,
2992		Providers: map[addrs.Provider]providers.Factory{
2993			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
2994		},
2995		State: state,
2996	})
2997
2998	if _, diags := ctx.Plan(); diags.HasErrors() {
2999		t.Fatalf("plan errors: %s", diags.Err())
3000	}
3001
3002	state, diags := ctx.Apply()
3003	if diags.HasErrors() {
3004		t.Fatalf("diags: %s", diags.Err())
3005	}
3006
3007	actual := strings.TrimSpace(state.String())
3008	expected := strings.TrimSpace(testTerraformApplyModuleVarRefExistingStr)
3009	if actual != expected {
3010		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
3011	}
3012}
3013
3014func TestContext2Apply_moduleVarResourceCount(t *testing.T) {
3015	m := testModule(t, "apply-module-var-resource-count")
3016	p := testProvider("aws")
3017	p.PlanResourceChangeFn = testDiffFn
3018	ctx := testContext2(t, &ContextOpts{
3019		Config: m,
3020		Providers: map[addrs.Provider]providers.Factory{
3021			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3022		},
3023		Variables: InputValues{
3024			"num": &InputValue{
3025				Value:      cty.NumberIntVal(2),
3026				SourceType: ValueFromCaller,
3027			},
3028		},
3029		PlanMode: plans.DestroyMode,
3030	})
3031
3032	if _, diags := ctx.Plan(); diags.HasErrors() {
3033		t.Fatalf("plan errors: %s", diags.Err())
3034	}
3035
3036	if _, diags := ctx.Apply(); diags.HasErrors() {
3037		t.Fatalf("apply errors: %s", diags.Err())
3038	}
3039
3040	ctx = testContext2(t, &ContextOpts{
3041		Config: m,
3042		Providers: map[addrs.Provider]providers.Factory{
3043			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3044		},
3045		Variables: InputValues{
3046			"num": &InputValue{
3047				Value:      cty.NumberIntVal(5),
3048				SourceType: ValueFromCaller,
3049			},
3050		},
3051	})
3052
3053	if _, diags := ctx.Plan(); diags.HasErrors() {
3054		t.Fatalf("plan errors: %s", diags.Err())
3055	}
3056
3057	if _, diags := ctx.Apply(); diags.HasErrors() {
3058		t.Fatalf("apply errors: %s", diags.Err())
3059	}
3060}
3061
3062// GH-819
3063func TestContext2Apply_moduleBool(t *testing.T) {
3064	m := testModule(t, "apply-module-bool")
3065	p := testProvider("aws")
3066	p.PlanResourceChangeFn = testDiffFn
3067	p.ApplyResourceChangeFn = testApplyFn
3068	ctx := testContext2(t, &ContextOpts{
3069		Config: m,
3070		Providers: map[addrs.Provider]providers.Factory{
3071			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3072		},
3073	})
3074
3075	if _, diags := ctx.Plan(); diags.HasErrors() {
3076		t.Fatalf("plan errors: %s", diags.Err())
3077	}
3078
3079	state, diags := ctx.Apply()
3080	if diags.HasErrors() {
3081		t.Fatalf("diags: %s", diags.Err())
3082	}
3083
3084	actual := strings.TrimSpace(state.String())
3085	expected := strings.TrimSpace(testTerraformApplyModuleBoolStr)
3086	if actual != expected {
3087		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
3088	}
3089}
3090
3091// Tests that a module can be targeted and everything is properly created.
3092// This adds to the plan test to also just verify that apply works.
3093func TestContext2Apply_moduleTarget(t *testing.T) {
3094	m := testModule(t, "plan-targeted-cross-module")
3095	p := testProvider("aws")
3096	p.PlanResourceChangeFn = testDiffFn
3097	p.ApplyResourceChangeFn = testApplyFn
3098	ctx := testContext2(t, &ContextOpts{
3099		Config: m,
3100		Providers: map[addrs.Provider]providers.Factory{
3101			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3102		},
3103		Targets: []addrs.Targetable{
3104			addrs.RootModuleInstance.Child("B", addrs.NoKey),
3105		},
3106	})
3107
3108	if _, diags := ctx.Plan(); diags.HasErrors() {
3109		t.Fatalf("plan errors: %s", diags.Err())
3110	}
3111
3112	state, diags := ctx.Apply()
3113	if diags.HasErrors() {
3114		t.Fatalf("diags: %s", diags.Err())
3115	}
3116
3117	checkStateString(t, state, `
3118<no state>
3119module.A:
3120  aws_instance.foo:
3121    ID = foo
3122    provider = provider["registry.terraform.io/hashicorp/aws"]
3123    foo = bar
3124    type = aws_instance
3125
3126  Outputs:
3127
3128  value = foo
3129module.B:
3130  aws_instance.bar:
3131    ID = foo
3132    provider = provider["registry.terraform.io/hashicorp/aws"]
3133    foo = foo
3134    type = aws_instance
3135
3136    Dependencies:
3137      module.A.aws_instance.foo
3138	`)
3139}
3140
3141func TestContext2Apply_multiProvider(t *testing.T) {
3142	m := testModule(t, "apply-multi-provider")
3143	p := testProvider("aws")
3144	p.PlanResourceChangeFn = testDiffFn
3145	p.ApplyResourceChangeFn = testApplyFn
3146
3147	pDO := testProvider("do")
3148	pDO.ApplyResourceChangeFn = testApplyFn
3149	pDO.PlanResourceChangeFn = testDiffFn
3150
3151	ctx := testContext2(t, &ContextOpts{
3152		Config: m,
3153		Providers: map[addrs.Provider]providers.Factory{
3154			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3155			addrs.NewDefaultProvider("do"):  testProviderFuncFixed(pDO),
3156		},
3157	})
3158
3159	if _, diags := ctx.Plan(); diags.HasErrors() {
3160		t.Fatalf("plan errors: %s", diags.Err())
3161	}
3162
3163	state, diags := ctx.Apply()
3164	if diags.HasErrors() {
3165		t.Fatalf("diags: %s", diags.Err())
3166	}
3167
3168	mod := state.RootModule()
3169	if len(mod.Resources) < 2 {
3170		t.Fatalf("bad: %#v", mod.Resources)
3171	}
3172
3173	actual := strings.TrimSpace(state.String())
3174	expected := strings.TrimSpace(testTerraformApplyMultiProviderStr)
3175	if actual != expected {
3176		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
3177	}
3178}
3179
3180func TestContext2Apply_multiProviderDestroy(t *testing.T) {
3181	m := testModule(t, "apply-multi-provider-destroy")
3182	p := testProvider("aws")
3183	p.PlanResourceChangeFn = testDiffFn
3184	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
3185		Provider: &configschema.Block{
3186			Attributes: map[string]*configschema.Attribute{
3187				"addr": {Type: cty.String, Optional: true},
3188			},
3189		},
3190		ResourceTypes: map[string]*configschema.Block{
3191			"aws_instance": {
3192				Attributes: map[string]*configschema.Attribute{
3193					"id":  {Type: cty.String, Computed: true},
3194					"foo": {Type: cty.String, Optional: true},
3195				},
3196			},
3197		},
3198	})
3199
3200	p2 := testProvider("vault")
3201	p2.ApplyResourceChangeFn = testApplyFn
3202	p2.PlanResourceChangeFn = testDiffFn
3203	p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
3204		ResourceTypes: map[string]*configschema.Block{
3205			"vault_instance": {
3206				Attributes: map[string]*configschema.Attribute{
3207					"id": {Type: cty.String, Computed: true},
3208				},
3209			},
3210		},
3211	})
3212
3213	var state *states.State
3214
3215	// First, create the instances
3216	{
3217		ctx := testContext2(t, &ContextOpts{
3218			Config: m,
3219			Providers: map[addrs.Provider]providers.Factory{
3220				addrs.NewDefaultProvider("aws"):   testProviderFuncFixed(p),
3221				addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2),
3222			},
3223		})
3224
3225		if _, diags := ctx.Plan(); diags.HasErrors() {
3226			t.Fatalf("errors during create plan: %s", diags.Err())
3227		}
3228
3229		s, diags := ctx.Apply()
3230		if diags.HasErrors() {
3231			t.Fatalf("errors during create apply: %s", diags.Err())
3232		}
3233
3234		state = s
3235	}
3236
3237	// Destroy them
3238	{
3239		// Verify that aws_instance.bar is destroyed first
3240		var checked bool
3241		var called int32
3242		var lock sync.Mutex
3243		applyFn := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
3244			lock.Lock()
3245			defer lock.Unlock()
3246
3247			if req.TypeName == "aws_instance" {
3248				checked = true
3249
3250				// Sleep to allow parallel execution
3251				time.Sleep(50 * time.Millisecond)
3252
3253				// Verify that called is 0 (dep not called)
3254				if atomic.LoadInt32(&called) != 0 {
3255					resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called"))
3256					return resp
3257				}
3258			}
3259
3260			atomic.AddInt32(&called, 1)
3261			return testApplyFn(req)
3262		}
3263
3264		// Set the apply functions
3265		p.ApplyResourceChangeFn = applyFn
3266		p2.ApplyResourceChangeFn = applyFn
3267
3268		ctx := testContext2(t, &ContextOpts{
3269			PlanMode: plans.DestroyMode,
3270			State:    state,
3271			Config:   m,
3272			Providers: map[addrs.Provider]providers.Factory{
3273				addrs.NewDefaultProvider("aws"):   testProviderFuncFixed(p),
3274				addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2),
3275			},
3276		})
3277
3278		if _, diags := ctx.Plan(); diags.HasErrors() {
3279			t.Fatalf("errors during destroy plan: %s", diags.Err())
3280		}
3281
3282		s, diags := ctx.Apply()
3283		if diags.HasErrors() {
3284			t.Fatalf("errors during destroy apply: %s", diags.Err())
3285		}
3286
3287		if !checked {
3288			t.Fatal("should be checked")
3289		}
3290
3291		state = s
3292	}
3293
3294	checkStateString(t, state, `<no state>`)
3295}
3296
3297// This is like the multiProviderDestroy test except it tests that
3298// dependent resources within a child module that inherit provider
3299// configuration are still destroyed first.
3300func TestContext2Apply_multiProviderDestroyChild(t *testing.T) {
3301	m := testModule(t, "apply-multi-provider-destroy-child")
3302	p := testProvider("aws")
3303	p.PlanResourceChangeFn = testDiffFn
3304	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
3305		Provider: &configschema.Block{
3306			Attributes: map[string]*configschema.Attribute{
3307				"value": {Type: cty.String, Optional: true},
3308			},
3309		},
3310		ResourceTypes: map[string]*configschema.Block{
3311			"aws_instance": {
3312				Attributes: map[string]*configschema.Attribute{
3313					"id":  {Type: cty.String, Computed: true},
3314					"foo": {Type: cty.String, Optional: true},
3315				},
3316			},
3317		},
3318	})
3319
3320	p2 := testProvider("vault")
3321	p2.ApplyResourceChangeFn = testApplyFn
3322	p2.PlanResourceChangeFn = testDiffFn
3323	p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
3324		Provider: &configschema.Block{},
3325		ResourceTypes: map[string]*configschema.Block{
3326			"vault_instance": {
3327				Attributes: map[string]*configschema.Attribute{
3328					"id": {Type: cty.String, Computed: true},
3329				},
3330			},
3331		},
3332	})
3333
3334	var state *states.State
3335
3336	// First, create the instances
3337	{
3338		ctx := testContext2(t, &ContextOpts{
3339			Config: m,
3340			Providers: map[addrs.Provider]providers.Factory{
3341				addrs.NewDefaultProvider("aws"):   testProviderFuncFixed(p),
3342				addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2),
3343			},
3344		})
3345
3346		if _, diags := ctx.Plan(); diags.HasErrors() {
3347			t.Fatalf("diags: %s", diags.Err())
3348		}
3349
3350		s, diags := ctx.Apply()
3351		if diags.HasErrors() {
3352			t.Fatalf("diags: %s", diags.Err())
3353		}
3354
3355		state = s
3356	}
3357
3358	// Destroy them
3359	{
3360		// Verify that aws_instance.bar is destroyed first
3361		var checked bool
3362		var called int32
3363		var lock sync.Mutex
3364		applyFn := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
3365			lock.Lock()
3366			defer lock.Unlock()
3367
3368			if req.TypeName == "aws_instance" {
3369				checked = true
3370
3371				// Sleep to allow parallel execution
3372				time.Sleep(50 * time.Millisecond)
3373
3374				// Verify that called is 0 (dep not called)
3375				if atomic.LoadInt32(&called) != 0 {
3376					resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called"))
3377					return resp
3378				}
3379			}
3380
3381			atomic.AddInt32(&called, 1)
3382			return testApplyFn(req)
3383		}
3384
3385		// Set the apply functions
3386		p.ApplyResourceChangeFn = applyFn
3387		p2.ApplyResourceChangeFn = applyFn
3388
3389		ctx := testContext2(t, &ContextOpts{
3390			PlanMode: plans.DestroyMode,
3391			State:    state,
3392			Config:   m,
3393			Providers: map[addrs.Provider]providers.Factory{
3394				addrs.NewDefaultProvider("aws"):   testProviderFuncFixed(p),
3395				addrs.NewDefaultProvider("vault"): testProviderFuncFixed(p2),
3396			},
3397		})
3398
3399		if _, diags := ctx.Plan(); diags.HasErrors() {
3400			t.Fatalf("diags: %s", diags.Err())
3401		}
3402
3403		s, diags := ctx.Apply()
3404		if diags.HasErrors() {
3405			t.Fatalf("diags: %s", diags.Err())
3406		}
3407
3408		if !checked {
3409			t.Fatal("should be checked")
3410		}
3411
3412		state = s
3413	}
3414
3415	checkStateString(t, state, `
3416<no state>
3417`)
3418}
3419
3420func TestContext2Apply_multiVar(t *testing.T) {
3421	m := testModule(t, "apply-multi-var")
3422	p := testProvider("aws")
3423	p.PlanResourceChangeFn = testDiffFn
3424
3425	// First, apply with a count of 3
3426	ctx := testContext2(t, &ContextOpts{
3427		Config: m,
3428		Providers: map[addrs.Provider]providers.Factory{
3429			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3430		},
3431		Variables: InputValues{
3432			"num": &InputValue{
3433				Value:      cty.NumberIntVal(3),
3434				SourceType: ValueFromCaller,
3435			},
3436		},
3437	})
3438
3439	if _, diags := ctx.Plan(); diags.HasErrors() {
3440		t.Fatalf("plan errors: %s", diags.Err())
3441	}
3442
3443	state, diags := ctx.Apply()
3444	if diags.HasErrors() {
3445		t.Fatalf("diags: %s", diags.Err())
3446	}
3447
3448	actual := state.RootModule().OutputValues["output"]
3449	expected := cty.StringVal("bar0,bar1,bar2")
3450	if actual == nil || actual.Value != expected {
3451		t.Fatalf("wrong value\ngot:  %#v\nwant: %#v", actual.Value, expected)
3452	}
3453
3454	t.Logf("Initial state: %s", state.String())
3455
3456	// Apply again, reduce the count to 1
3457	{
3458		ctx := testContext2(t, &ContextOpts{
3459			Config: m,
3460			State:  state,
3461			Providers: map[addrs.Provider]providers.Factory{
3462				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3463			},
3464			Variables: InputValues{
3465				"num": &InputValue{
3466					Value:      cty.NumberIntVal(1),
3467					SourceType: ValueFromCaller,
3468				},
3469			},
3470		})
3471
3472		if _, diags := ctx.Plan(); diags.HasErrors() {
3473			t.Fatalf("diags: %s", diags.Err())
3474		}
3475
3476		state, diags := ctx.Apply()
3477		if diags.HasErrors() {
3478			t.Fatalf("diags: %s", diags.Err())
3479		}
3480
3481		t.Logf("End state: %s", state.String())
3482
3483		actual := state.RootModule().OutputValues["output"]
3484		if actual == nil {
3485			t.Fatal("missing output")
3486		}
3487
3488		expected := cty.StringVal("bar0")
3489		if actual.Value != expected {
3490			t.Fatalf("wrong value\ngot:  %#v\nwant: %#v", actual.Value, expected)
3491		}
3492	}
3493}
3494
3495// This is a holistic test of multi-var (aka "splat variable") handling
3496// across several different Terraform subsystems. This is here because
3497// historically there were quirky differences in handling across different
3498// parts of Terraform and so here we want to assert the expected behavior and
3499// ensure that it remains consistent in future.
3500func TestContext2Apply_multiVarComprehensive(t *testing.T) {
3501	m := testModule(t, "apply-multi-var-comprehensive")
3502	p := testProvider("test")
3503
3504	configs := map[string]cty.Value{}
3505	var configsLock sync.Mutex
3506
3507	p.ApplyResourceChangeFn = testApplyFn
3508	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
3509		proposed := req.ProposedNewState
3510		configsLock.Lock()
3511		defer configsLock.Unlock()
3512		key := proposed.GetAttr("key").AsString()
3513		// This test was originally written using the legacy p.PlanResourceChangeFn interface,
3514		// and so the assertions below expect an old-style ResourceConfig, which
3515		// we'll construct via our shim for now to avoid rewriting all of the
3516		// assertions.
3517		configs[key] = req.ProposedNewState
3518
3519		retVals := make(map[string]cty.Value)
3520		for it := proposed.ElementIterator(); it.Next(); {
3521			idxVal, val := it.Element()
3522			idx := idxVal.AsString()
3523
3524			switch idx {
3525			case "id":
3526				retVals[idx] = cty.UnknownVal(cty.String)
3527			case "name":
3528				retVals[idx] = cty.StringVal(key)
3529			default:
3530				retVals[idx] = val
3531			}
3532		}
3533
3534		return providers.PlanResourceChangeResponse{
3535			PlannedState: cty.ObjectVal(retVals),
3536		}
3537	}
3538
3539	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
3540		ResourceTypes: map[string]*configschema.Block{
3541			"test_thing": {
3542				Attributes: map[string]*configschema.Attribute{
3543					"key": {Type: cty.String, Required: true},
3544
3545					"source_id":              {Type: cty.String, Optional: true},
3546					"source_name":            {Type: cty.String, Optional: true},
3547					"first_source_id":        {Type: cty.String, Optional: true},
3548					"first_source_name":      {Type: cty.String, Optional: true},
3549					"source_ids":             {Type: cty.List(cty.String), Optional: true},
3550					"source_names":           {Type: cty.List(cty.String), Optional: true},
3551					"source_ids_from_func":   {Type: cty.List(cty.String), Optional: true},
3552					"source_names_from_func": {Type: cty.List(cty.String), Optional: true},
3553					"source_ids_wrapped":     {Type: cty.List(cty.List(cty.String)), Optional: true},
3554					"source_names_wrapped":   {Type: cty.List(cty.List(cty.String)), Optional: true},
3555
3556					"id":   {Type: cty.String, Computed: true},
3557					"name": {Type: cty.String, Computed: true},
3558				},
3559			},
3560		},
3561	})
3562
3563	// First, apply with a count of 3
3564	ctx := testContext2(t, &ContextOpts{
3565		Config: m,
3566		Providers: map[addrs.Provider]providers.Factory{
3567			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
3568		},
3569		Variables: InputValues{
3570			"num": &InputValue{
3571				Value:      cty.NumberIntVal(3),
3572				SourceType: ValueFromCaller,
3573			},
3574		},
3575	})
3576
3577	if _, diags := ctx.Plan(); diags.HasErrors() {
3578		logDiagnostics(t, diags)
3579		t.Fatalf("errors during plan")
3580	}
3581
3582	checkConfig := func(key string, want cty.Value) {
3583		configsLock.Lock()
3584		defer configsLock.Unlock()
3585
3586		got, ok := configs[key]
3587		if !ok {
3588			t.Errorf("no config recorded for %s; expected a configuration", key)
3589			return
3590		}
3591
3592		t.Run("config for "+key, func(t *testing.T) {
3593			for _, problem := range deep.Equal(got, want) {
3594				t.Errorf(problem)
3595			}
3596		})
3597	}
3598
3599	checkConfig("multi_count_var.0", cty.ObjectVal(map[string]cty.Value{
3600		"source_id":   cty.UnknownVal(cty.String),
3601		"source_name": cty.StringVal("source.0"),
3602	}))
3603	checkConfig("multi_count_var.2", cty.ObjectVal(map[string]cty.Value{
3604		"source_id":   cty.UnknownVal(cty.String),
3605		"source_name": cty.StringVal("source.2"),
3606	}))
3607	checkConfig("multi_count_derived.0", cty.ObjectVal(map[string]cty.Value{
3608		"source_id":   cty.UnknownVal(cty.String),
3609		"source_name": cty.StringVal("source.0"),
3610	}))
3611	checkConfig("multi_count_derived.2", cty.ObjectVal(map[string]cty.Value{
3612		"source_id":   cty.UnknownVal(cty.String),
3613		"source_name": cty.StringVal("source.2"),
3614	}))
3615	checkConfig("whole_splat", cty.ObjectVal(map[string]cty.Value{
3616		"source_ids": cty.ListVal([]cty.Value{
3617			cty.UnknownVal(cty.String),
3618			cty.UnknownVal(cty.String),
3619			cty.UnknownVal(cty.String),
3620		}),
3621		"source_names": cty.ListVal([]cty.Value{
3622			cty.StringVal("source.0"),
3623			cty.StringVal("source.1"),
3624			cty.StringVal("source.2"),
3625		}),
3626		"source_ids_from_func": cty.UnknownVal(cty.String),
3627		"source_names_from_func": cty.ListVal([]cty.Value{
3628			cty.StringVal("source.0"),
3629			cty.StringVal("source.1"),
3630			cty.StringVal("source.2"),
3631		}),
3632		"source_ids_wrapped": cty.ListVal([]cty.Value{
3633			cty.ListVal([]cty.Value{
3634				cty.UnknownVal(cty.String),
3635				cty.UnknownVal(cty.String),
3636				cty.UnknownVal(cty.String),
3637			}),
3638		}),
3639		"source_names_wrapped": cty.ListVal([]cty.Value{
3640			cty.ListVal([]cty.Value{
3641				cty.StringVal("source.0"),
3642				cty.StringVal("source.1"),
3643				cty.StringVal("source.2"),
3644			}),
3645		}),
3646		"first_source_id":   cty.UnknownVal(cty.String),
3647		"first_source_name": cty.StringVal("source.0"),
3648	}))
3649	checkConfig("child.whole_splat", cty.ObjectVal(map[string]cty.Value{
3650		"source_ids": cty.ListVal([]cty.Value{
3651			cty.UnknownVal(cty.String),
3652			cty.UnknownVal(cty.String),
3653			cty.UnknownVal(cty.String),
3654		}),
3655		"source_names": cty.ListVal([]cty.Value{
3656			cty.StringVal("source.0"),
3657			cty.StringVal("source.1"),
3658			cty.StringVal("source.2"),
3659		}),
3660		"source_ids_wrapped": cty.ListVal([]cty.Value{
3661			cty.ListVal([]cty.Value{
3662				cty.UnknownVal(cty.String),
3663				cty.UnknownVal(cty.String),
3664				cty.UnknownVal(cty.String),
3665			}),
3666		}),
3667		"source_names_wrapped": cty.ListVal([]cty.Value{
3668			cty.ListVal([]cty.Value{
3669				cty.StringVal("source.0"),
3670				cty.StringVal("source.1"),
3671				cty.StringVal("source.2"),
3672			}),
3673		}),
3674	}))
3675
3676	t.Run("apply", func(t *testing.T) {
3677		state, diags := ctx.Apply()
3678		if diags.HasErrors() {
3679			t.Fatalf("error during apply: %s", diags.Err())
3680		}
3681
3682		want := map[string]interface{}{
3683			"source_ids": []interface{}{"foo", "foo", "foo"},
3684			"source_names": []interface{}{
3685				"source.0",
3686				"source.1",
3687				"source.2",
3688			},
3689		}
3690		got := map[string]interface{}{}
3691		for k, s := range state.RootModule().OutputValues {
3692			got[k] = hcl2shim.ConfigValueFromHCL2(s.Value)
3693		}
3694		if !reflect.DeepEqual(got, want) {
3695			t.Errorf(
3696				"wrong outputs\ngot:  %s\nwant: %s",
3697				spew.Sdump(got), spew.Sdump(want),
3698			)
3699		}
3700	})
3701}
3702
3703// Test that multi-var (splat) access is ordered by count, not by
3704// value.
3705func TestContext2Apply_multiVarOrder(t *testing.T) {
3706	m := testModule(t, "apply-multi-var-order")
3707	p := testProvider("aws")
3708	p.PlanResourceChangeFn = testDiffFn
3709
3710	// First, apply with a count of 3
3711	ctx := testContext2(t, &ContextOpts{
3712		Config: m,
3713		Providers: map[addrs.Provider]providers.Factory{
3714			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3715		},
3716	})
3717
3718	if _, diags := ctx.Plan(); diags.HasErrors() {
3719		t.Fatalf("plan errors: %s", diags.Err())
3720	}
3721
3722	state, diags := ctx.Apply()
3723	if diags.HasErrors() {
3724		t.Fatalf("diags: %s", diags.Err())
3725	}
3726
3727	t.Logf("State: %s", state.String())
3728
3729	actual := state.RootModule().OutputValues["should-be-11"]
3730	expected := cty.StringVal("index-11")
3731	if actual == nil || actual.Value != expected {
3732		t.Fatalf("wrong value\ngot:  %#v\nwant: %#v", actual.Value, expected)
3733	}
3734}
3735
3736// Test that multi-var (splat) access is ordered by count, not by
3737// value, through interpolations.
3738func TestContext2Apply_multiVarOrderInterp(t *testing.T) {
3739	m := testModule(t, "apply-multi-var-order-interp")
3740	p := testProvider("aws")
3741	p.PlanResourceChangeFn = testDiffFn
3742
3743	// First, apply with a count of 3
3744	ctx := testContext2(t, &ContextOpts{
3745		Config: m,
3746		Providers: map[addrs.Provider]providers.Factory{
3747			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3748		},
3749	})
3750
3751	if _, diags := ctx.Plan(); diags.HasErrors() {
3752		t.Fatalf("plan errors: %s", diags.Err())
3753	}
3754
3755	state, diags := ctx.Apply()
3756	if diags.HasErrors() {
3757		t.Fatalf("diags: %s", diags.Err())
3758	}
3759
3760	t.Logf("State: %s", state.String())
3761
3762	actual := state.RootModule().OutputValues["should-be-11"]
3763	expected := cty.StringVal("baz-index-11")
3764	if actual == nil || actual.Value != expected {
3765		t.Fatalf("wrong value\ngot:  %#v\nwant: %#v", actual.Value, expected)
3766	}
3767}
3768
3769// Based on GH-10440 where a graph edge wasn't properly being created
3770// between a modified resource and a count instance being destroyed.
3771func TestContext2Apply_multiVarCountDec(t *testing.T) {
3772	var s *states.State
3773
3774	// First create resources. Nothing sneaky here.
3775	{
3776		m := testModule(t, "apply-multi-var-count-dec")
3777		p := testProvider("aws")
3778		p.PlanResourceChangeFn = testDiffFn
3779		p.ApplyResourceChangeFn = testApplyFn
3780		ctx := testContext2(t, &ContextOpts{
3781			Config: m,
3782			Providers: map[addrs.Provider]providers.Factory{
3783				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3784			},
3785			Variables: InputValues{
3786				"num": &InputValue{
3787					Value:      cty.NumberIntVal(2),
3788					SourceType: ValueFromCaller,
3789				},
3790			},
3791		})
3792
3793		log.Print("\n========\nStep 1 Plan\n========")
3794		if _, diags := ctx.Plan(); diags.HasErrors() {
3795			t.Fatalf("diags: %s", diags.Err())
3796		}
3797
3798		log.Print("\n========\nStep 1 Apply\n========")
3799		state, diags := ctx.Apply()
3800		if diags.HasErrors() {
3801			t.Fatalf("diags: %s", diags.Err())
3802		}
3803
3804		t.Logf("Step 1 state:\n%s", state)
3805
3806		s = state
3807	}
3808
3809	// Decrease the count by 1 and verify that everything happens in the
3810	// right order.
3811	m := testModule(t, "apply-multi-var-count-dec")
3812	p := testProvider("aws")
3813	p.PlanResourceChangeFn = testDiffFn
3814
3815	// Verify that aws_instance.bar is modified first and nothing
3816	// else happens at the same time.
3817	{
3818		var checked bool
3819		var called int32
3820		var lock sync.Mutex
3821		p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
3822			lock.Lock()
3823			defer lock.Unlock()
3824
3825			if !req.PlannedState.IsNull() {
3826				s := req.PlannedState.AsValueMap()
3827				if ami, ok := s["ami"]; ok && !ami.IsNull() && ami.AsString() == "special" {
3828					checked = true
3829
3830					// Sleep to allow parallel execution
3831					time.Sleep(50 * time.Millisecond)
3832
3833					// Verify that called is 0 (dep not called)
3834					if atomic.LoadInt32(&called) != 1 {
3835						resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("nothing else should be called"))
3836						return
3837					}
3838				}
3839			}
3840			atomic.AddInt32(&called, 1)
3841			return testApplyFn(req)
3842		}
3843
3844		ctx := testContext2(t, &ContextOpts{
3845			State:  s,
3846			Config: m,
3847			Providers: map[addrs.Provider]providers.Factory{
3848				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3849			},
3850			Variables: InputValues{
3851				"num": &InputValue{
3852					Value:      cty.NumberIntVal(1),
3853					SourceType: ValueFromCaller,
3854				},
3855			},
3856		})
3857
3858		log.Print("\n========\nStep 2 Plan\n========")
3859		plan, diags := ctx.Plan()
3860		if diags.HasErrors() {
3861			t.Fatalf("plan errors: %s", diags.Err())
3862		}
3863
3864		t.Logf("Step 2 plan:\n%s", legacyDiffComparisonString(plan.Changes))
3865
3866		log.Print("\n========\nStep 2 Apply\n========")
3867		_, diags = ctx.Apply()
3868		if diags.HasErrors() {
3869			t.Fatalf("apply errors: %s", diags.Err())
3870		}
3871
3872		if !checked {
3873			t.Error("apply never called")
3874		}
3875	}
3876}
3877
3878// Test that we can resolve a multi-var (splat) for the first resource
3879// created in a non-root module, which happens when the module state doesn't
3880// exist yet.
3881// https://github.com/hashicorp/terraform/issues/14438
3882func TestContext2Apply_multiVarMissingState(t *testing.T) {
3883	m := testModule(t, "apply-multi-var-missing-state")
3884	p := testProvider("test")
3885	p.PlanResourceChangeFn = testDiffFn
3886	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
3887		ResourceTypes: map[string]*configschema.Block{
3888			"test_thing": {
3889				Attributes: map[string]*configschema.Attribute{
3890					"a_ids": {Type: cty.String, Optional: true},
3891					"id":    {Type: cty.String, Computed: true},
3892				},
3893			},
3894		},
3895	})
3896
3897	// First, apply with a count of 3
3898	ctx := testContext2(t, &ContextOpts{
3899		Config: m,
3900		Providers: map[addrs.Provider]providers.Factory{
3901			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
3902		},
3903	})
3904
3905	if _, diags := ctx.Plan(); diags.HasErrors() {
3906		t.Fatalf("plan failed: %s", diags.Err())
3907	}
3908
3909	// Before the relevant bug was fixed, Tdiagsaform would panic during apply.
3910	if _, diags := ctx.Apply(); diags.HasErrors() {
3911		t.Fatalf("apply failed: %s", diags.Err())
3912	}
3913
3914	// If we get here with no errors or panics then our test was successful.
3915}
3916
3917func TestContext2Apply_outputOrphan(t *testing.T) {
3918	m := testModule(t, "apply-output-orphan")
3919	p := testProvider("aws")
3920	p.PlanResourceChangeFn = testDiffFn
3921
3922	state := states.NewState()
3923	root := state.EnsureModule(addrs.RootModuleInstance)
3924	root.SetOutputValue("foo", cty.StringVal("bar"), false)
3925	root.SetOutputValue("bar", cty.StringVal("baz"), false)
3926
3927	ctx := testContext2(t, &ContextOpts{
3928		Config: m,
3929		Providers: map[addrs.Provider]providers.Factory{
3930			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3931		},
3932		State: state,
3933	})
3934
3935	if _, diags := ctx.Plan(); diags.HasErrors() {
3936		t.Fatalf("plan errors: %s", diags.Err())
3937	}
3938
3939	state, diags := ctx.Apply()
3940	if diags.HasErrors() {
3941		t.Fatalf("diags: %s", diags.Err())
3942	}
3943
3944	actual := strings.TrimSpace(state.String())
3945	expected := strings.TrimSpace(testTerraformApplyOutputOrphanStr)
3946	if actual != expected {
3947		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
3948	}
3949}
3950
3951func TestContext2Apply_outputOrphanModule(t *testing.T) {
3952	m := testModule(t, "apply-output-orphan-module")
3953	p := testProvider("aws")
3954	p.PlanResourceChangeFn = testDiffFn
3955
3956	state := states.NewState()
3957
3958	ctx := testContext2(t, &ContextOpts{
3959		Config: m,
3960		Providers: map[addrs.Provider]providers.Factory{
3961			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3962		},
3963		State: state,
3964	})
3965
3966	if _, diags := ctx.Plan(); diags.HasErrors() {
3967		t.Fatalf("plan errors: %s", diags.Err())
3968	}
3969
3970	s, diags := ctx.Apply()
3971	if diags.HasErrors() {
3972		t.Fatalf("diags: %s", diags.Err())
3973	}
3974
3975	actual := strings.TrimSpace(s.String())
3976	expected := strings.TrimSpace(testTerraformApplyOutputOrphanModuleStr)
3977	if actual != expected {
3978		t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
3979	}
3980
3981	// now apply with no module in the config, which should remove the
3982	// remaining output
3983	ctx = testContext2(t, &ContextOpts{
3984		Config: configs.NewEmptyConfig(),
3985		Providers: map[addrs.Provider]providers.Factory{
3986			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
3987		},
3988		State: state.DeepCopy(),
3989	})
3990
3991	if _, diags := ctx.Plan(); diags.HasErrors() {
3992		t.Fatalf("plan errors: %s", diags.Err())
3993	}
3994
3995	state, diags = ctx.Apply()
3996	if diags.HasErrors() {
3997		t.Fatalf("diags: %s", diags.Err())
3998	}
3999
4000	if !state.Empty() {
4001		t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(state))
4002	}
4003}
4004
4005func TestContext2Apply_providerComputedVar(t *testing.T) {
4006	m := testModule(t, "apply-provider-computed")
4007	p := testProvider("aws")
4008	p.PlanResourceChangeFn = testDiffFn
4009
4010	pTest := testProvider("test")
4011	pTest.ApplyResourceChangeFn = testApplyFn
4012	pTest.PlanResourceChangeFn = testDiffFn
4013
4014	ctx := testContext2(t, &ContextOpts{
4015		Config: m,
4016		Providers: map[addrs.Provider]providers.Factory{
4017			addrs.NewDefaultProvider("aws"):  testProviderFuncFixed(p),
4018			addrs.NewDefaultProvider("test"): testProviderFuncFixed(pTest),
4019		},
4020	})
4021
4022	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
4023		val := req.Config.GetAttr("value")
4024		if val.IsNull() {
4025			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found"))
4026			return
4027		}
4028		return
4029	}
4030
4031	if _, diags := ctx.Plan(); diags.HasErrors() {
4032		t.Fatalf("plan errors: %s", diags.Err())
4033	}
4034
4035	if _, diags := ctx.Apply(); diags.HasErrors() {
4036		t.Fatalf("apply errors: %s", diags.Err())
4037	}
4038}
4039
4040func TestContext2Apply_providerConfigureDisabled(t *testing.T) {
4041	m := testModule(t, "apply-provider-configure-disabled")
4042	p := testProvider("aws")
4043	p.PlanResourceChangeFn = testDiffFn
4044
4045	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
4046		val := req.Config.GetAttr("value")
4047		if val.IsNull() {
4048			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("value is not found"))
4049		}
4050
4051		return
4052	}
4053
4054	ctx := testContext2(t, &ContextOpts{
4055		Config: m,
4056		Providers: map[addrs.Provider]providers.Factory{
4057			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4058		},
4059	})
4060
4061	if _, diags := ctx.Plan(); diags.HasErrors() {
4062		t.Fatalf("plan errors: %s", diags.Err())
4063	}
4064
4065	if _, diags := ctx.Apply(); diags.HasErrors() {
4066		t.Fatalf("apply errors: %s", diags.Err())
4067	}
4068
4069	if !p.ConfigureProviderCalled {
4070		t.Fatal("configure never called")
4071	}
4072}
4073
4074func TestContext2Apply_provisionerModule(t *testing.T) {
4075	m := testModule(t, "apply-provisioner-module")
4076
4077	p := testProvider("aws")
4078	p.PlanResourceChangeFn = testDiffFn
4079	p.ApplyResourceChangeFn = testApplyFn
4080
4081	pr := testProvisioner()
4082	pr.GetSchemaResponse = provisioners.GetSchemaResponse{
4083		Provisioner: &configschema.Block{
4084			Attributes: map[string]*configschema.Attribute{
4085				"foo": {Type: cty.String, Optional: true},
4086			},
4087		},
4088	}
4089
4090	ctx := testContext2(t, &ContextOpts{
4091		Config: m,
4092		Providers: map[addrs.Provider]providers.Factory{
4093			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4094		},
4095		Provisioners: map[string]provisioners.Factory{
4096			"shell": testProvisionerFuncFixed(pr),
4097		},
4098	})
4099
4100	if _, diags := ctx.Plan(); diags.HasErrors() {
4101		t.Fatalf("plan errors: %s", diags.Err())
4102	}
4103
4104	state, diags := ctx.Apply()
4105	if diags.HasErrors() {
4106		t.Fatalf("diags: %s", diags.Err())
4107	}
4108
4109	actual := strings.TrimSpace(state.String())
4110	expected := strings.TrimSpace(testTerraformApplyProvisionerModuleStr)
4111	if actual != expected {
4112		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
4113	}
4114
4115	// Verify apply was invoked
4116	if !pr.ProvisionResourceCalled {
4117		t.Fatalf("provisioner not invoked")
4118	}
4119}
4120
4121func TestContext2Apply_Provisioner_compute(t *testing.T) {
4122	m := testModule(t, "apply-provisioner-compute")
4123	p := testProvider("aws")
4124	pr := testProvisioner()
4125	p.PlanResourceChangeFn = testDiffFn
4126	p.ApplyResourceChangeFn = testApplyFn
4127	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4128
4129		val := req.Config.GetAttr("command").AsString()
4130		if val != "computed_value" {
4131			t.Fatalf("bad value for foo: %q", val)
4132		}
4133		req.UIOutput.Output(fmt.Sprintf("Executing: %q", val))
4134
4135		return
4136	}
4137	h := new(MockHook)
4138	ctx := testContext2(t, &ContextOpts{
4139		Config: m,
4140		Hooks:  []Hook{h},
4141		Providers: map[addrs.Provider]providers.Factory{
4142			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4143		},
4144		Provisioners: map[string]provisioners.Factory{
4145			"shell": testProvisionerFuncFixed(pr),
4146		},
4147		Variables: InputValues{
4148			"value": &InputValue{
4149				Value:      cty.NumberIntVal(1),
4150				SourceType: ValueFromCaller,
4151			},
4152		},
4153	})
4154
4155	if _, diags := ctx.Plan(); diags.HasErrors() {
4156		t.Fatalf("plan errors: %s", diags.Err())
4157	}
4158
4159	state, diags := ctx.Apply()
4160	if diags.HasErrors() {
4161		t.Fatalf("diags: %s", diags.Err())
4162	}
4163
4164	actual := strings.TrimSpace(state.String())
4165	expected := strings.TrimSpace(testTerraformApplyProvisionerStr)
4166	if actual != expected {
4167		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
4168	}
4169
4170	// Verify apply was invoked
4171	if !pr.ProvisionResourceCalled {
4172		t.Fatalf("provisioner not invoked")
4173	}
4174
4175	// Verify output was rendered
4176	if !h.ProvisionOutputCalled {
4177		t.Fatalf("ProvisionOutput hook not called")
4178	}
4179	if got, want := h.ProvisionOutputMessage, `Executing: "computed_value"`; got != want {
4180		t.Errorf("expected output to be %q, but was %q", want, got)
4181	}
4182}
4183
4184func TestContext2Apply_provisionerCreateFail(t *testing.T) {
4185	m := testModule(t, "apply-provisioner-fail-create")
4186	p := testProvider("aws")
4187	pr := testProvisioner()
4188	p.PlanResourceChangeFn = testDiffFn
4189
4190	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
4191		resp := testApplyFn(req)
4192		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error"))
4193
4194		return resp
4195	}
4196
4197	ctx := testContext2(t, &ContextOpts{
4198		Config: m,
4199		Providers: map[addrs.Provider]providers.Factory{
4200			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4201		},
4202		Provisioners: map[string]provisioners.Factory{
4203			"shell": testProvisionerFuncFixed(pr),
4204		},
4205	})
4206
4207	if _, diags := ctx.Plan(); diags.HasErrors() {
4208		t.Fatalf("plan errors: %s", diags.Err())
4209	}
4210
4211	state, diags := ctx.Apply()
4212	if diags == nil {
4213		t.Fatal("should error")
4214	}
4215
4216	got := strings.TrimSpace(state.String())
4217	want := strings.TrimSpace(testTerraformApplyProvisionerFailCreateStr)
4218	if got != want {
4219		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want)
4220	}
4221}
4222
4223func TestContext2Apply_provisionerCreateFailNoId(t *testing.T) {
4224	m := testModule(t, "apply-provisioner-fail-create")
4225	p := testProvider("aws")
4226	pr := testProvisioner()
4227	p.PlanResourceChangeFn = testDiffFn
4228
4229	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4230		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error"))
4231		return
4232	}
4233
4234	ctx := testContext2(t, &ContextOpts{
4235		Config: m,
4236		Providers: map[addrs.Provider]providers.Factory{
4237			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4238		},
4239		Provisioners: map[string]provisioners.Factory{
4240			"shell": testProvisionerFuncFixed(pr),
4241		},
4242	})
4243
4244	if _, diags := ctx.Plan(); diags.HasErrors() {
4245		t.Fatalf("plan errors: %s", diags.Err())
4246	}
4247
4248	state, diags := ctx.Apply()
4249	if diags == nil {
4250		t.Fatal("should error")
4251	}
4252
4253	actual := strings.TrimSpace(state.String())
4254	expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateNoIdStr)
4255	if actual != expected {
4256		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
4257	}
4258}
4259
4260func TestContext2Apply_provisionerFail(t *testing.T) {
4261	m := testModule(t, "apply-provisioner-fail")
4262	p := testProvider("aws")
4263	p.PlanResourceChangeFn = testDiffFn
4264	p.ApplyResourceChangeFn = testApplyFn
4265	pr := testProvisioner()
4266	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4267		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION"))
4268		return
4269	}
4270
4271	ctx := testContext2(t, &ContextOpts{
4272		Config: m,
4273		Providers: map[addrs.Provider]providers.Factory{
4274			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4275		},
4276		Provisioners: map[string]provisioners.Factory{
4277			"shell": testProvisionerFuncFixed(pr),
4278		},
4279	})
4280
4281	if _, diags := ctx.Plan(); diags.HasErrors() {
4282		t.Fatalf("plan errors: %s", diags.Err())
4283	}
4284
4285	state, diags := ctx.Apply()
4286	if diags == nil {
4287		t.Fatal("should error")
4288	}
4289
4290	actual := strings.TrimSpace(state.String())
4291	expected := strings.TrimSpace(testTerraformApplyProvisionerFailStr)
4292	if actual != expected {
4293		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
4294	}
4295}
4296
4297func TestContext2Apply_provisionerFail_createBeforeDestroy(t *testing.T) {
4298	m := testModule(t, "apply-provisioner-fail-create-before")
4299	p := testProvider("aws")
4300	pr := testProvisioner()
4301	p.PlanResourceChangeFn = testDiffFn
4302	p.ApplyResourceChangeFn = testApplyFn
4303	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4304		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("EXPLOSION"))
4305		return
4306	}
4307
4308	state := states.NewState()
4309	root := state.EnsureModule(addrs.RootModuleInstance)
4310	root.SetResourceInstanceCurrent(
4311		mustResourceInstanceAddr("aws_instance.bar").Resource,
4312		&states.ResourceInstanceObjectSrc{
4313			Status:    states.ObjectReady,
4314			AttrsJSON: []byte(`{"id":"bar","require_new":"abc"}`),
4315		},
4316		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4317	)
4318
4319	ctx := testContext2(t, &ContextOpts{
4320		Config: m,
4321		Providers: map[addrs.Provider]providers.Factory{
4322			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4323		},
4324		Provisioners: map[string]provisioners.Factory{
4325			"shell": testProvisionerFuncFixed(pr),
4326		},
4327		State: state,
4328	})
4329
4330	if _, diags := ctx.Plan(); diags.HasErrors() {
4331		t.Fatalf("plan errors: %s", diags.Err())
4332	}
4333
4334	state, diags := ctx.Apply()
4335	if diags == nil {
4336		t.Fatal("should error")
4337	}
4338
4339	actual := strings.TrimSpace(state.String())
4340	expected := strings.TrimSpace(testTerraformApplyProvisionerFailCreateBeforeDestroyStr)
4341	if actual != expected {
4342		t.Fatalf("expected:\n%s\n:got\n%s", expected, actual)
4343	}
4344}
4345
4346func TestContext2Apply_error_createBeforeDestroy(t *testing.T) {
4347	m := testModule(t, "apply-error-create-before")
4348	p := testProvider("aws")
4349
4350	state := states.NewState()
4351	root := state.EnsureModule(addrs.RootModuleInstance)
4352	root.SetResourceInstanceCurrent(
4353		mustResourceInstanceAddr("aws_instance.bar").Resource,
4354		&states.ResourceInstanceObjectSrc{
4355			Status:    states.ObjectReady,
4356			AttrsJSON: []byte(`{"id":"bar", "require_new": "abc","type":"aws_instance"}`),
4357		},
4358		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4359	)
4360
4361	ctx := testContext2(t, &ContextOpts{
4362		Config: m,
4363		Providers: map[addrs.Provider]providers.Factory{
4364			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4365		},
4366		State: state,
4367	})
4368	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4369		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("placeholder error from ApplyFn"))
4370		return
4371	}
4372	p.PlanResourceChangeFn = testDiffFn
4373
4374	if _, diags := ctx.Plan(); diags.HasErrors() {
4375		t.Fatalf("plan errors: %s", diags.Err())
4376	}
4377
4378	state, diags := ctx.Apply()
4379	if diags == nil {
4380		t.Fatal("should have error")
4381	}
4382	if got, want := diags.Err().Error(), "placeholder error from ApplyFn"; got != want {
4383		// We're looking for our artificial error from ApplyFn above, whose
4384		// message is literally "placeholder error from ApplyFn".
4385		t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
4386	}
4387
4388	actual := strings.TrimSpace(state.String())
4389	expected := strings.TrimSpace(testTerraformApplyErrorCreateBeforeDestroyStr)
4390	if actual != expected {
4391		t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected)
4392	}
4393}
4394
4395func TestContext2Apply_errorDestroy_createBeforeDestroy(t *testing.T) {
4396	m := testModule(t, "apply-error-create-before")
4397	p := testProvider("aws")
4398
4399	state := states.NewState()
4400	root := state.EnsureModule(addrs.RootModuleInstance)
4401	root.SetResourceInstanceCurrent(
4402		mustResourceInstanceAddr("aws_instance.bar").Resource,
4403		&states.ResourceInstanceObjectSrc{
4404			Status:    states.ObjectReady,
4405			AttrsJSON: []byte(`{"id":"bar", "require_new": "abc"}`),
4406		},
4407		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4408	)
4409
4410	ctx := testContext2(t, &ContextOpts{
4411		Config: m,
4412		Providers: map[addrs.Provider]providers.Factory{
4413			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4414		},
4415		State: state,
4416	})
4417	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4418		// Fail the destroy!
4419		if req.PlannedState.IsNull() {
4420			resp.NewState = req.PriorState
4421			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error"))
4422			return
4423		}
4424
4425		return testApplyFn(req)
4426	}
4427	p.PlanResourceChangeFn = testDiffFn
4428
4429	if _, diags := ctx.Plan(); diags.HasErrors() {
4430		t.Fatalf("plan errors: %s", diags.Err())
4431	}
4432
4433	state, diags := ctx.Apply()
4434	if diags == nil {
4435		t.Fatal("should have error")
4436	}
4437
4438	actual := strings.TrimSpace(state.String())
4439	expected := strings.TrimSpace(testTerraformApplyErrorDestroyCreateBeforeDestroyStr)
4440	if actual != expected {
4441		t.Fatalf("bad: actual:\n%s\n\nexpected:\n%s", actual, expected)
4442	}
4443}
4444
4445func TestContext2Apply_multiDepose_createBeforeDestroy(t *testing.T) {
4446	m := testModule(t, "apply-multi-depose-create-before-destroy")
4447	p := testProvider("aws")
4448	ps := map[addrs.Provider]providers.Factory{addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p)}
4449	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
4450		ResourceTypes: map[string]*configschema.Block{
4451			"aws_instance": {
4452				Attributes: map[string]*configschema.Attribute{
4453					"require_new": {Type: cty.String, Optional: true},
4454					"id":          {Type: cty.String, Computed: true},
4455				},
4456			},
4457		},
4458	})
4459
4460	state := states.NewState()
4461	root := state.EnsureModule(addrs.RootModuleInstance)
4462	root.SetResourceInstanceCurrent(
4463		mustResourceInstanceAddr("aws_instance.web").Resource,
4464		&states.ResourceInstanceObjectSrc{
4465			Status:    states.ObjectReady,
4466			AttrsJSON: []byte(`{"id":"foo"}`),
4467		},
4468		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4469	)
4470
4471	p.PlanResourceChangeFn = testDiffFn
4472
4473	ctx := testContext2(t, &ContextOpts{
4474		Config:    m,
4475		Providers: ps,
4476		State:     state,
4477		Variables: InputValues{
4478			"require_new": &InputValue{
4479				Value: cty.StringVal("yes"),
4480			},
4481		},
4482	})
4483	createdInstanceId := "bar"
4484	// Create works
4485	createFunc := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4486		s := req.PlannedState.AsValueMap()
4487		s["id"] = cty.StringVal(createdInstanceId)
4488		resp.NewState = cty.ObjectVal(s)
4489		return
4490	}
4491
4492	// Destroy starts broken
4493	destroyFunc := func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4494		resp.NewState = req.PriorState
4495		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("destroy failed"))
4496		return
4497	}
4498	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4499		if req.PlannedState.IsNull() {
4500			return destroyFunc(req)
4501		} else {
4502			return createFunc(req)
4503		}
4504	}
4505
4506	if _, diags := ctx.Plan(); diags.HasErrors() {
4507		t.Fatalf("plan errors: %s", diags.Err())
4508	}
4509
4510	// Destroy is broken, so even though CBD successfully replaces the instance,
4511	// we'll have to save the Deposed instance to destroy later
4512	state, diags := ctx.Apply()
4513	if diags == nil {
4514		t.Fatal("should have error")
4515	}
4516
4517	checkStateString(t, state, `
4518aws_instance.web: (1 deposed)
4519  ID = bar
4520  provider = provider["registry.terraform.io/hashicorp/aws"]
4521  require_new = yes
4522  Deposed ID 1 = foo
4523	`)
4524
4525	createdInstanceId = "baz"
4526	ctx = testContext2(t, &ContextOpts{
4527		Config:    m,
4528		Providers: ps,
4529		State:     state,
4530		Variables: InputValues{
4531			"require_new": &InputValue{
4532				Value: cty.StringVal("baz"),
4533			},
4534		},
4535	})
4536
4537	if _, diags := ctx.Plan(); diags.HasErrors() {
4538		t.Fatalf("plan errors: %s", diags.Err())
4539	}
4540
4541	// We're replacing the primary instance once again. Destroy is _still_
4542	// broken, so the Deposed list gets longer
4543	state, diags = ctx.Apply()
4544	if diags == nil {
4545		t.Fatal("should have error")
4546	}
4547
4548	// For this one we can't rely on checkStateString because its result is
4549	// not deterministic when multiple deposed objects are present. Instead,
4550	// we will probe the state object directly.
4551	{
4552		is := state.RootModule().Resources["aws_instance.web"].Instances[addrs.NoKey]
4553		if is.Current == nil {
4554			t.Fatalf("no current object for aws_instance web; should have one")
4555		}
4556		if !bytes.Contains(is.Current.AttrsJSON, []byte("baz")) {
4557			t.Fatalf("incorrect current object attrs %s; want id=baz", is.Current.AttrsJSON)
4558		}
4559		if got, want := len(is.Deposed), 2; got != want {
4560			t.Fatalf("wrong number of deposed instances %d; want %d", got, want)
4561		}
4562		var foos, bars int
4563		for _, obj := range is.Deposed {
4564			if bytes.Contains(obj.AttrsJSON, []byte("foo")) {
4565				foos++
4566			}
4567			if bytes.Contains(obj.AttrsJSON, []byte("bar")) {
4568				bars++
4569			}
4570		}
4571		if got, want := foos, 1; got != want {
4572			t.Fatalf("wrong number of deposed instances with id=foo %d; want %d", got, want)
4573		}
4574		if got, want := bars, 1; got != want {
4575			t.Fatalf("wrong number of deposed instances with id=bar %d; want %d", got, want)
4576		}
4577	}
4578
4579	// Destroy partially fixed!
4580	destroyFunc = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4581		s := req.PriorState.AsValueMap()
4582		id := s["id"].AsString()
4583		if id == "foo" || id == "baz" {
4584			resp.NewState = cty.NullVal(req.PriorState.Type())
4585		} else {
4586			resp.NewState = req.PriorState
4587			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("destroy partially failed"))
4588		}
4589		return
4590	}
4591
4592	createdInstanceId = "qux"
4593	ctx = testContext2(t, &ContextOpts{
4594		Config:    m,
4595		Providers: ps,
4596		State:     state,
4597		Variables: InputValues{
4598			"require_new": &InputValue{
4599				Value: cty.StringVal("qux"),
4600			},
4601		},
4602	})
4603	if _, diags := ctx.Plan(); diags.HasErrors() {
4604		t.Fatalf("plan errors: %s", diags.Err())
4605	}
4606
4607	state, diags = ctx.Apply()
4608	// Expect error because 1/2 of Deposed destroys failed
4609	if diags == nil {
4610		t.Fatal("should have error")
4611	}
4612
4613	// foo and baz are now gone, bar sticks around
4614	checkStateString(t, state, `
4615aws_instance.web: (1 deposed)
4616  ID = qux
4617  provider = provider["registry.terraform.io/hashicorp/aws"]
4618  require_new = qux
4619  Deposed ID 1 = bar
4620	`)
4621
4622	// Destroy working fully!
4623	destroyFunc = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
4624		resp.NewState = cty.NullVal(req.PriorState.Type())
4625		return
4626	}
4627
4628	createdInstanceId = "quux"
4629	ctx = testContext2(t, &ContextOpts{
4630		Config:    m,
4631		Providers: ps,
4632		State:     state,
4633		Variables: InputValues{
4634			"require_new": &InputValue{
4635				Value: cty.StringVal("quux"),
4636			},
4637		},
4638	})
4639	if _, diags := ctx.Plan(); diags.HasErrors() {
4640		t.Fatalf("plan errors: %s", diags.Err())
4641	}
4642	state, diags = ctx.Apply()
4643	if diags.HasErrors() {
4644		t.Fatal("should not have error:", diags.Err())
4645	}
4646
4647	// And finally the state is clean
4648	checkStateString(t, state, `
4649aws_instance.web:
4650  ID = quux
4651  provider = provider["registry.terraform.io/hashicorp/aws"]
4652  require_new = quux
4653	`)
4654}
4655
4656// Verify that a normal provisioner with on_failure "continue" set won't
4657// taint the resource and continues executing.
4658func TestContext2Apply_provisionerFailContinue(t *testing.T) {
4659	m := testModule(t, "apply-provisioner-fail-continue")
4660	p := testProvider("aws")
4661	pr := testProvisioner()
4662	p.PlanResourceChangeFn = testDiffFn
4663	p.ApplyResourceChangeFn = testApplyFn
4664
4665	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4666		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error"))
4667		return
4668	}
4669
4670	ctx := testContext2(t, &ContextOpts{
4671		Config: m,
4672		Providers: map[addrs.Provider]providers.Factory{
4673			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4674		},
4675		Provisioners: map[string]provisioners.Factory{
4676			"shell": testProvisionerFuncFixed(pr),
4677		},
4678	})
4679
4680	if _, diags := ctx.Plan(); diags.HasErrors() {
4681		t.Fatalf("plan errors: %s", diags.Err())
4682	}
4683
4684	state, diags := ctx.Apply()
4685	if diags.HasErrors() {
4686		t.Fatalf("diags: %s", diags.Err())
4687	}
4688
4689	checkStateString(t, state, `
4690aws_instance.foo:
4691  ID = foo
4692  provider = provider["registry.terraform.io/hashicorp/aws"]
4693  foo = bar
4694  type = aws_instance
4695  `)
4696
4697	// Verify apply was invoked
4698	if !pr.ProvisionResourceCalled {
4699		t.Fatalf("provisioner not invoked")
4700	}
4701}
4702
4703// Verify that a normal provisioner with on_failure "continue" records
4704// the error with the hook.
4705func TestContext2Apply_provisionerFailContinueHook(t *testing.T) {
4706	h := new(MockHook)
4707	m := testModule(t, "apply-provisioner-fail-continue")
4708	p := testProvider("aws")
4709	pr := testProvisioner()
4710	p.PlanResourceChangeFn = testDiffFn
4711	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4712		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error"))
4713		return
4714	}
4715
4716	ctx := testContext2(t, &ContextOpts{
4717		Config: m,
4718		Hooks:  []Hook{h},
4719		Providers: map[addrs.Provider]providers.Factory{
4720			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4721		},
4722		Provisioners: map[string]provisioners.Factory{
4723			"shell": testProvisionerFuncFixed(pr),
4724		},
4725	})
4726
4727	if _, diags := ctx.Plan(); diags.HasErrors() {
4728		t.Fatalf("plan errors: %s", diags.Err())
4729	}
4730
4731	if _, diags := ctx.Apply(); diags.HasErrors() {
4732		t.Fatalf("apply errors: %s", diags.Err())
4733	}
4734
4735	if !h.PostProvisionInstanceStepCalled {
4736		t.Fatal("PostProvisionInstanceStep not called")
4737	}
4738	if h.PostProvisionInstanceStepErrorArg == nil {
4739		t.Fatal("should have error")
4740	}
4741}
4742
4743func TestContext2Apply_provisionerDestroy(t *testing.T) {
4744	m := testModule(t, "apply-provisioner-destroy")
4745	p := testProvider("aws")
4746	pr := testProvisioner()
4747	p.PlanResourceChangeFn = testDiffFn
4748	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4749		val := req.Config.GetAttr("command").AsString()
4750		if val != "destroy a bar" {
4751			t.Fatalf("bad value for foo: %q", val)
4752		}
4753
4754		return
4755	}
4756
4757	state := states.NewState()
4758	root := state.RootModule()
4759	root.SetResourceInstanceCurrent(
4760		mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
4761		&states.ResourceInstanceObjectSrc{
4762			Status:    states.ObjectReady,
4763			AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`),
4764		},
4765		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4766	)
4767
4768	ctx := testContext2(t, &ContextOpts{
4769		Config:   m,
4770		State:    state,
4771		PlanMode: plans.DestroyMode,
4772		Providers: map[addrs.Provider]providers.Factory{
4773			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4774		},
4775		Provisioners: map[string]provisioners.Factory{
4776			"shell": testProvisionerFuncFixed(pr),
4777		},
4778	})
4779
4780	if _, diags := ctx.Plan(); diags.HasErrors() {
4781		t.Fatalf("plan errors: %s", diags.Err())
4782	}
4783
4784	state, diags := ctx.Apply()
4785	if diags.HasErrors() {
4786		t.Fatalf("diags: %s", diags.Err())
4787	}
4788
4789	checkStateString(t, state, `<no state>`)
4790
4791	// Verify apply was invoked
4792	if !pr.ProvisionResourceCalled {
4793		t.Fatalf("provisioner not invoked")
4794	}
4795}
4796
4797// Verify that on destroy provisioner failure, nothing happens to the instance
4798func TestContext2Apply_provisionerDestroyFail(t *testing.T) {
4799	m := testModule(t, "apply-provisioner-destroy")
4800	p := testProvider("aws")
4801	pr := testProvisioner()
4802	p.PlanResourceChangeFn = testDiffFn
4803	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4804		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error"))
4805		return
4806	}
4807
4808	state := states.NewState()
4809	root := state.RootModule()
4810	root.SetResourceInstanceCurrent(
4811		mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
4812		&states.ResourceInstanceObjectSrc{
4813			Status:    states.ObjectReady,
4814			AttrsJSON: []byte(`{"id":"bar","foo":"bar"}`),
4815		},
4816		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4817	)
4818
4819	ctx := testContext2(t, &ContextOpts{
4820		Config:   m,
4821		State:    state,
4822		PlanMode: plans.DestroyMode,
4823		Providers: map[addrs.Provider]providers.Factory{
4824			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4825		},
4826		Provisioners: map[string]provisioners.Factory{
4827			"shell": testProvisionerFuncFixed(pr),
4828		},
4829	})
4830
4831	if _, diags := ctx.Plan(); diags.HasErrors() {
4832		t.Fatalf("plan errors: %s", diags.Err())
4833	}
4834
4835	state, diags := ctx.Apply()
4836	if diags == nil {
4837		t.Fatal("should error")
4838	}
4839
4840	checkStateString(t, state, `
4841aws_instance.foo["a"]:
4842  ID = bar
4843  provider = provider["registry.terraform.io/hashicorp/aws"]
4844  foo = bar
4845	`)
4846
4847	// Verify apply was invoked
4848	if !pr.ProvisionResourceCalled {
4849		t.Fatalf("provisioner not invoked")
4850	}
4851}
4852
4853// Verify that on destroy provisioner failure with "continue" that
4854// we continue to the next provisioner.
4855func TestContext2Apply_provisionerDestroyFailContinue(t *testing.T) {
4856	m := testModule(t, "apply-provisioner-destroy-continue")
4857	p := testProvider("aws")
4858	pr := testProvisioner()
4859	p.PlanResourceChangeFn = testDiffFn
4860
4861	var l sync.Mutex
4862	var calls []string
4863	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4864		val := req.Config.GetAttr("command")
4865		if val.IsNull() {
4866			t.Fatalf("bad value for foo: %#v", val)
4867		}
4868
4869		l.Lock()
4870		defer l.Unlock()
4871		calls = append(calls, val.AsString())
4872		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error"))
4873		return
4874	}
4875
4876	state := states.NewState()
4877	root := state.RootModule()
4878	root.SetResourceInstanceCurrent(
4879		mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
4880		&states.ResourceInstanceObjectSrc{
4881			Status:    states.ObjectReady,
4882			AttrsJSON: []byte(`{"id":"bar"}`),
4883		},
4884		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4885	)
4886
4887	ctx := testContext2(t, &ContextOpts{
4888		Config:   m,
4889		State:    state,
4890		PlanMode: plans.DestroyMode,
4891		Providers: map[addrs.Provider]providers.Factory{
4892			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4893		},
4894		Provisioners: map[string]provisioners.Factory{
4895			"shell": testProvisionerFuncFixed(pr),
4896		},
4897	})
4898
4899	if _, diags := ctx.Plan(); diags.HasErrors() {
4900		t.Fatalf("plan errors: %s", diags.Err())
4901	}
4902
4903	state, diags := ctx.Apply()
4904	if diags.HasErrors() {
4905		t.Fatalf("diags: %s", diags.Err())
4906	}
4907
4908	checkStateString(t, state, `<no state>`)
4909
4910	// Verify apply was invoked
4911	if !pr.ProvisionResourceCalled {
4912		t.Fatalf("provisioner not invoked")
4913	}
4914
4915	expected := []string{"one", "two"}
4916	if !reflect.DeepEqual(calls, expected) {
4917		t.Fatalf("wrong commands\ngot:  %#v\nwant: %#v", calls, expected)
4918	}
4919}
4920
4921// Verify that on destroy provisioner failure with "continue" that
4922// we continue to the next provisioner. But if the next provisioner defines
4923// to fail, then we fail after running it.
4924func TestContext2Apply_provisionerDestroyFailContinueFail(t *testing.T) {
4925	m := testModule(t, "apply-provisioner-destroy-fail")
4926	p := testProvider("aws")
4927	pr := testProvisioner()
4928	p.PlanResourceChangeFn = testDiffFn
4929
4930	var l sync.Mutex
4931	var calls []string
4932	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
4933		val := req.Config.GetAttr("command")
4934		if val.IsNull() {
4935			t.Fatalf("bad value for foo: %#v", val)
4936		}
4937
4938		l.Lock()
4939		defer l.Unlock()
4940		calls = append(calls, val.AsString())
4941		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provisioner error"))
4942		return
4943	}
4944
4945	state := states.NewState()
4946	root := state.EnsureModule(addrs.RootModuleInstance)
4947	root.SetResourceInstanceCurrent(
4948		mustResourceInstanceAddr("aws_instance.foo").Resource,
4949		&states.ResourceInstanceObjectSrc{
4950			Status:    states.ObjectReady,
4951			AttrsJSON: []byte(`{"id":"bar"}`),
4952		},
4953		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
4954	)
4955
4956	ctx := testContext2(t, &ContextOpts{
4957		Config:   m,
4958		State:    state,
4959		PlanMode: plans.DestroyMode,
4960		Providers: map[addrs.Provider]providers.Factory{
4961			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
4962		},
4963		Provisioners: map[string]provisioners.Factory{
4964			"shell": testProvisionerFuncFixed(pr),
4965		},
4966	})
4967
4968	if _, diags := ctx.Plan(); diags.HasErrors() {
4969		t.Fatalf("plan errors: %s", diags.Err())
4970	}
4971
4972	state, diags := ctx.Apply()
4973	if diags == nil {
4974		t.Fatal("apply succeeded; wanted error from second provisioner")
4975	}
4976
4977	checkStateString(t, state, `
4978aws_instance.foo:
4979  ID = bar
4980  provider = provider["registry.terraform.io/hashicorp/aws"]
4981  `)
4982
4983	// Verify apply was invoked
4984	if !pr.ProvisionResourceCalled {
4985		t.Fatalf("provisioner not invoked")
4986	}
4987
4988	expected := []string{"one", "two"}
4989	if !reflect.DeepEqual(calls, expected) {
4990		t.Fatalf("bad: %#v", calls)
4991	}
4992}
4993
4994// Verify destroy provisioners are not run for tainted instances.
4995func TestContext2Apply_provisionerDestroyTainted(t *testing.T) {
4996	m := testModule(t, "apply-provisioner-destroy")
4997	p := testProvider("aws")
4998	pr := testProvisioner()
4999	p.PlanResourceChangeFn = testDiffFn
5000	p.ApplyResourceChangeFn = testApplyFn
5001
5002	destroyCalled := false
5003	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
5004		expected := "create a b"
5005		val := req.Config.GetAttr("command")
5006		if val.AsString() != expected {
5007			t.Fatalf("bad value for command: %#v", val)
5008		}
5009
5010		return
5011	}
5012
5013	state := states.NewState()
5014	root := state.RootModule()
5015	root.SetResourceInstanceCurrent(
5016		mustResourceInstanceAddr(`aws_instance.foo["a"]`).Resource,
5017		&states.ResourceInstanceObjectSrc{
5018			Status:    states.ObjectTainted,
5019			AttrsJSON: []byte(`{"id":"bar"}`),
5020		},
5021		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
5022	)
5023
5024	ctx := testContext2(t, &ContextOpts{
5025		Config: m,
5026		State:  state,
5027		Providers: map[addrs.Provider]providers.Factory{
5028			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5029		},
5030		Provisioners: map[string]provisioners.Factory{
5031			"shell": testProvisionerFuncFixed(pr),
5032		},
5033		Variables: InputValues{
5034			"input": &InputValue{
5035				Value: cty.MapVal(map[string]cty.Value{
5036					"a": cty.StringVal("b"),
5037				}),
5038				SourceType: ValueFromInput,
5039			},
5040		},
5041	})
5042
5043	if _, diags := ctx.Plan(); diags.HasErrors() {
5044		t.Fatalf("plan errors: %s", diags.Err())
5045	}
5046
5047	state, diags := ctx.Apply()
5048	if diags.HasErrors() {
5049		t.Fatalf("diags: %s", diags.Err())
5050	}
5051
5052	checkStateString(t, state, `
5053aws_instance.foo["a"]:
5054  ID = foo
5055  provider = provider["registry.terraform.io/hashicorp/aws"]
5056  foo = bar
5057  type = aws_instance
5058	`)
5059
5060	// Verify apply was invoked
5061	if !pr.ProvisionResourceCalled {
5062		t.Fatalf("provisioner not invoked")
5063	}
5064
5065	if destroyCalled {
5066		t.Fatal("destroy should not be called")
5067	}
5068}
5069
5070func TestContext2Apply_provisionerResourceRef(t *testing.T) {
5071	m := testModule(t, "apply-provisioner-resource-ref")
5072	p := testProvider("aws")
5073	p.PlanResourceChangeFn = testDiffFn
5074	p.ApplyResourceChangeFn = testApplyFn
5075
5076	pr := testProvisioner()
5077	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
5078		val := req.Config.GetAttr("command")
5079		if val.AsString() != "2" {
5080			t.Fatalf("bad value for command: %#v", val)
5081		}
5082
5083		return
5084	}
5085
5086	ctx := testContext2(t, &ContextOpts{
5087		Config: m,
5088		Providers: map[addrs.Provider]providers.Factory{
5089			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5090		},
5091		Provisioners: map[string]provisioners.Factory{
5092			"shell": testProvisionerFuncFixed(pr),
5093		},
5094	})
5095
5096	if _, diags := ctx.Plan(); diags.HasErrors() {
5097		t.Fatalf("plan errors: %s", diags.Err())
5098	}
5099
5100	state, diags := ctx.Apply()
5101	if diags.HasErrors() {
5102		t.Fatalf("diags: %s", diags.Err())
5103	}
5104
5105	actual := strings.TrimSpace(state.String())
5106	expected := strings.TrimSpace(testTerraformApplyProvisionerResourceRefStr)
5107	if actual != expected {
5108		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5109	}
5110
5111	// Verify apply was invoked
5112	if !pr.ProvisionResourceCalled {
5113		t.Fatalf("provisioner not invoked")
5114	}
5115}
5116
5117func TestContext2Apply_provisionerSelfRef(t *testing.T) {
5118	m := testModule(t, "apply-provisioner-self-ref")
5119	p := testProvider("aws")
5120	pr := testProvisioner()
5121	p.PlanResourceChangeFn = testDiffFn
5122	p.ApplyResourceChangeFn = testApplyFn
5123	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
5124		val := req.Config.GetAttr("command")
5125		if val.AsString() != "bar" {
5126			t.Fatalf("bad value for command: %#v", val)
5127		}
5128
5129		return
5130	}
5131
5132	ctx := testContext2(t, &ContextOpts{
5133		Config: m,
5134		Providers: map[addrs.Provider]providers.Factory{
5135			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5136		},
5137		Provisioners: map[string]provisioners.Factory{
5138			"shell": testProvisionerFuncFixed(pr),
5139		},
5140	})
5141
5142	if _, diags := ctx.Plan(); diags.HasErrors() {
5143		t.Fatalf("plan errors: %s", diags.Err())
5144	}
5145
5146	state, diags := ctx.Apply()
5147	if diags.HasErrors() {
5148		t.Fatalf("diags: %s", diags.Err())
5149	}
5150
5151	actual := strings.TrimSpace(state.String())
5152	expected := strings.TrimSpace(testTerraformApplyProvisionerSelfRefStr)
5153	if actual != expected {
5154		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5155	}
5156
5157	// Verify apply was invoked
5158	if !pr.ProvisionResourceCalled {
5159		t.Fatalf("provisioner not invoked")
5160	}
5161}
5162
5163func TestContext2Apply_provisionerMultiSelfRef(t *testing.T) {
5164	var lock sync.Mutex
5165	commands := make([]string, 0, 5)
5166
5167	m := testModule(t, "apply-provisioner-multi-self-ref")
5168	p := testProvider("aws")
5169	pr := testProvisioner()
5170	p.PlanResourceChangeFn = testDiffFn
5171	p.ApplyResourceChangeFn = testApplyFn
5172	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
5173		lock.Lock()
5174		defer lock.Unlock()
5175
5176		val := req.Config.GetAttr("command")
5177		if val.IsNull() {
5178			t.Fatalf("bad value for command: %#v", val)
5179		}
5180
5181		commands = append(commands, val.AsString())
5182		return
5183	}
5184
5185	ctx := testContext2(t, &ContextOpts{
5186		Config: m,
5187		Providers: map[addrs.Provider]providers.Factory{
5188			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5189		},
5190		Provisioners: map[string]provisioners.Factory{
5191			"shell": testProvisionerFuncFixed(pr),
5192		},
5193	})
5194
5195	if _, diags := ctx.Plan(); diags.HasErrors() {
5196		t.Fatalf("plan errors: %s", diags.Err())
5197	}
5198
5199	state, diags := ctx.Apply()
5200	if diags.HasErrors() {
5201		t.Fatalf("diags: %s", diags.Err())
5202	}
5203
5204	actual := strings.TrimSpace(state.String())
5205	expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefStr)
5206	if actual != expected {
5207		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5208	}
5209
5210	// Verify apply was invoked
5211	if !pr.ProvisionResourceCalled {
5212		t.Fatalf("provisioner not invoked")
5213	}
5214
5215	// Verify our result
5216	sort.Strings(commands)
5217	expectedCommands := []string{"number 0", "number 1", "number 2"}
5218	if !reflect.DeepEqual(commands, expectedCommands) {
5219		t.Fatalf("bad: %#v", commands)
5220	}
5221}
5222
5223func TestContext2Apply_provisionerMultiSelfRefSingle(t *testing.T) {
5224	var lock sync.Mutex
5225	order := make([]string, 0, 5)
5226
5227	m := testModule(t, "apply-provisioner-multi-self-ref-single")
5228	p := testProvider("aws")
5229	pr := testProvisioner()
5230	p.PlanResourceChangeFn = testDiffFn
5231	p.ApplyResourceChangeFn = testApplyFn
5232	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
5233		lock.Lock()
5234		defer lock.Unlock()
5235
5236		val := req.Config.GetAttr("order")
5237		if val.IsNull() {
5238			t.Fatalf("no val for order")
5239		}
5240
5241		order = append(order, val.AsString())
5242		return
5243	}
5244
5245	ctx := testContext2(t, &ContextOpts{
5246		Config: m,
5247		Providers: map[addrs.Provider]providers.Factory{
5248			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5249		},
5250		Provisioners: map[string]provisioners.Factory{
5251			"shell": testProvisionerFuncFixed(pr),
5252		},
5253	})
5254
5255	if _, diags := ctx.Plan(); diags.HasErrors() {
5256		t.Fatalf("plan errors: %s", diags.Err())
5257	}
5258
5259	state, diags := ctx.Apply()
5260	if diags.HasErrors() {
5261		t.Fatalf("diags: %s", diags.Err())
5262	}
5263
5264	actual := strings.TrimSpace(state.String())
5265	expected := strings.TrimSpace(testTerraformApplyProvisionerMultiSelfRefSingleStr)
5266	if actual != expected {
5267		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5268	}
5269
5270	// Verify apply was invoked
5271	if !pr.ProvisionResourceCalled {
5272		t.Fatalf("provisioner not invoked")
5273	}
5274
5275	// Verify our result
5276	sort.Strings(order)
5277	expectedOrder := []string{"0", "1", "2"}
5278	if !reflect.DeepEqual(order, expectedOrder) {
5279		t.Fatalf("bad: %#v", order)
5280	}
5281}
5282
5283func TestContext2Apply_provisionerExplicitSelfRef(t *testing.T) {
5284	m := testModule(t, "apply-provisioner-explicit-self-ref")
5285	p := testProvider("aws")
5286	pr := testProvisioner()
5287	p.PlanResourceChangeFn = testDiffFn
5288	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
5289		val := req.Config.GetAttr("command")
5290		if val.IsNull() || val.AsString() != "bar" {
5291			t.Fatalf("bad value for command: %#v", val)
5292		}
5293
5294		return
5295	}
5296
5297	var state *states.State
5298	{
5299		ctx := testContext2(t, &ContextOpts{
5300			Config: m,
5301			Providers: map[addrs.Provider]providers.Factory{
5302				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5303			},
5304			Provisioners: map[string]provisioners.Factory{
5305				"shell": testProvisionerFuncFixed(pr),
5306			},
5307		})
5308
5309		_, diags := ctx.Plan()
5310		if diags.HasErrors() {
5311			t.Fatalf("diags: %s", diags.Err())
5312		}
5313
5314		state, diags = ctx.Apply()
5315		if diags.HasErrors() {
5316			t.Fatalf("diags: %s", diags.Err())
5317		}
5318
5319		// Verify apply was invoked
5320		if !pr.ProvisionResourceCalled {
5321			t.Fatalf("provisioner not invoked")
5322		}
5323	}
5324
5325	{
5326		ctx := testContext2(t, &ContextOpts{
5327			Config:   m,
5328			PlanMode: plans.DestroyMode,
5329			State:    state,
5330			Providers: map[addrs.Provider]providers.Factory{
5331				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5332			},
5333			Provisioners: map[string]provisioners.Factory{
5334				"shell": testProvisionerFuncFixed(pr),
5335			},
5336		})
5337
5338		_, diags := ctx.Plan()
5339		if diags.HasErrors() {
5340			t.Fatalf("diags: %s", diags.Err())
5341		}
5342
5343		state, diags = ctx.Apply()
5344		if diags.HasErrors() {
5345			t.Fatalf("diags: %s", diags.Err())
5346		}
5347
5348		checkStateString(t, state, `<no state>`)
5349	}
5350}
5351
5352func TestContext2Apply_provisionerForEachSelfRef(t *testing.T) {
5353	m := testModule(t, "apply-provisioner-for-each-self")
5354	p := testProvider("aws")
5355	pr := testProvisioner()
5356	p.PlanResourceChangeFn = testDiffFn
5357
5358	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
5359		val := req.Config.GetAttr("command")
5360		if val.IsNull() {
5361			t.Fatalf("bad value for command: %#v", val)
5362		}
5363
5364		return resp
5365	}
5366
5367	ctx := testContext2(t, &ContextOpts{
5368		Config: m,
5369		Providers: map[addrs.Provider]providers.Factory{
5370			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5371		},
5372		Provisioners: map[string]provisioners.Factory{
5373			"shell": testProvisionerFuncFixed(pr),
5374		},
5375	})
5376
5377	if _, diags := ctx.Plan(); diags.HasErrors() {
5378		t.Fatalf("plan errors: %s", diags.Err())
5379	}
5380
5381	_, diags := ctx.Apply()
5382	if diags.HasErrors() {
5383		t.Fatalf("diags: %s", diags.Err())
5384	}
5385}
5386
5387// Provisioner should NOT run on a diff, only create
5388func TestContext2Apply_Provisioner_Diff(t *testing.T) {
5389	m := testModule(t, "apply-provisioner-diff")
5390	p := testProvider("aws")
5391	pr := testProvisioner()
5392	p.PlanResourceChangeFn = testDiffFn
5393	p.ApplyResourceChangeFn = testApplyFn
5394	ctx := testContext2(t, &ContextOpts{
5395		Config: m,
5396		Providers: map[addrs.Provider]providers.Factory{
5397			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5398		},
5399		Provisioners: map[string]provisioners.Factory{
5400			"shell": testProvisionerFuncFixed(pr),
5401		},
5402	})
5403
5404	if _, diags := ctx.Plan(); diags.HasErrors() {
5405		logDiagnostics(t, diags)
5406		t.Fatal("plan failed")
5407	}
5408
5409	state, diags := ctx.Apply()
5410	if diags.HasErrors() {
5411		logDiagnostics(t, diags)
5412		t.Fatal("apply failed")
5413	}
5414
5415	actual := strings.TrimSpace(state.String())
5416	expected := strings.TrimSpace(testTerraformApplyProvisionerDiffStr)
5417	if actual != expected {
5418		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5419	}
5420
5421	// Verify apply was invoked
5422	if !pr.ProvisionResourceCalled {
5423		t.Fatalf("provisioner was not called on first apply")
5424	}
5425	pr.ProvisionResourceCalled = false
5426
5427	// Change the state to force a diff
5428	mod := state.RootModule()
5429	obj := mod.Resources["aws_instance.bar"].Instances[addrs.NoKey].Current
5430	var attrs map[string]interface{}
5431	err := json.Unmarshal(obj.AttrsJSON, &attrs)
5432	if err != nil {
5433		t.Fatal(err)
5434	}
5435	attrs["foo"] = "baz"
5436	obj.AttrsJSON, err = json.Marshal(attrs)
5437	if err != nil {
5438		t.Fatal(err)
5439	}
5440
5441	// Re-create context with state
5442	ctx = testContext2(t, &ContextOpts{
5443		Config: m,
5444		Providers: map[addrs.Provider]providers.Factory{
5445			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5446		},
5447		Provisioners: map[string]provisioners.Factory{
5448			"shell": testProvisionerFuncFixed(pr),
5449		},
5450		State: state,
5451	})
5452
5453	if _, diags := ctx.Plan(); diags.HasErrors() {
5454		logDiagnostics(t, diags)
5455		t.Fatal("plan failed")
5456	}
5457
5458	state2, diags := ctx.Apply()
5459	if diags.HasErrors() {
5460		logDiagnostics(t, diags)
5461		t.Fatal("apply failed")
5462	}
5463
5464	actual = strings.TrimSpace(state2.String())
5465	if actual != expected {
5466		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5467	}
5468
5469	// Verify apply was NOT invoked
5470	if pr.ProvisionResourceCalled {
5471		t.Fatalf("provisioner was called on second apply; should not have been")
5472	}
5473}
5474
5475func TestContext2Apply_outputDiffVars(t *testing.T) {
5476	m := testModule(t, "apply-good")
5477	p := testProvider("aws")
5478
5479	state := states.NewState()
5480	root := state.EnsureModule(addrs.RootModuleInstance)
5481	root.SetResourceInstanceCurrent(
5482		mustResourceInstanceAddr("aws_instance.baz").Resource,
5483		&states.ResourceInstanceObjectSrc{
5484			Status:    states.ObjectReady,
5485			AttrsJSON: []byte(`{"id":"bar"}`),
5486		},
5487		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
5488	)
5489
5490	ctx := testContext2(t, &ContextOpts{
5491		Config: m,
5492		Providers: map[addrs.Provider]providers.Factory{
5493			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5494		},
5495		State: state,
5496	})
5497
5498	p.PlanResourceChangeFn = testDiffFn
5499	//func(info *InstanceInfo, s *InstanceState, rc *ResourceConfig) (*InstanceDiff, error) {
5500	//    d := &InstanceDiff{
5501	//        Attributes: map[string]*ResourceAttrDiff{},
5502	//    }
5503	//    if new, ok := rc.Get("value"); ok {
5504	//        d.Attributes["value"] = &ResourceAttrDiff{
5505	//            New: new.(string),
5506	//        }
5507	//    }
5508	//    if new, ok := rc.Get("foo"); ok {
5509	//        d.Attributes["foo"] = &ResourceAttrDiff{
5510	//            New: new.(string),
5511	//        }
5512	//    } else if rc.IsComputed("foo") {
5513	//        d.Attributes["foo"] = &ResourceAttrDiff{
5514	//            NewComputed: true,
5515	//            Type:        DiffAttrOutput, // This doesn't actually really do anything anymore, but this test originally set it.
5516	//        }
5517	//    }
5518	//    if new, ok := rc.Get("num"); ok {
5519	//        d.Attributes["num"] = &ResourceAttrDiff{
5520	//            New: fmt.Sprintf("%#v", new),
5521	//        }
5522	//    }
5523	//    return d, nil
5524	//}
5525
5526	if _, diags := ctx.Plan(); diags.HasErrors() {
5527		logDiagnostics(t, diags)
5528		t.Fatal("plan failed")
5529	}
5530	if _, diags := ctx.Apply(); diags.HasErrors() {
5531		logDiagnostics(t, diags)
5532		t.Fatal("apply failed")
5533	}
5534}
5535
5536func TestContext2Apply_destroyX(t *testing.T) {
5537	m := testModule(t, "apply-destroy")
5538	h := new(HookRecordApplyOrder)
5539	p := testProvider("aws")
5540	p.PlanResourceChangeFn = testDiffFn
5541	ctx := testContext2(t, &ContextOpts{
5542		Config: m,
5543		Hooks:  []Hook{h},
5544		Providers: map[addrs.Provider]providers.Factory{
5545			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5546		},
5547	})
5548
5549	// First plan and apply a create operation
5550	if _, diags := ctx.Plan(); diags.HasErrors() {
5551		t.Fatalf("plan errors: %s", diags.Err())
5552	}
5553
5554	state, diags := ctx.Apply()
5555	if diags.HasErrors() {
5556		t.Fatalf("diags: %s", diags.Err())
5557	}
5558
5559	// Next, plan and apply a destroy operation
5560	h.Active = true
5561	ctx = testContext2(t, &ContextOpts{
5562		PlanMode: plans.DestroyMode,
5563		State:    state,
5564		Config:   m,
5565		Hooks:    []Hook{h},
5566		Providers: map[addrs.Provider]providers.Factory{
5567			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5568		},
5569	})
5570
5571	if _, diags := ctx.Plan(); diags.HasErrors() {
5572		t.Fatalf("plan errors: %s", diags.Err())
5573	}
5574
5575	state, diags = ctx.Apply()
5576	if diags.HasErrors() {
5577		t.Fatalf("diags: %s", diags.Err())
5578	}
5579
5580	// Test that things were destroyed
5581	actual := strings.TrimSpace(state.String())
5582	expected := strings.TrimSpace(testTerraformApplyDestroyStr)
5583	if actual != expected {
5584		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5585	}
5586
5587	// Test that things were destroyed _in the right order_
5588	expected2 := []string{"aws_instance.bar", "aws_instance.foo"}
5589	actual2 := h.IDs
5590	if !reflect.DeepEqual(actual2, expected2) {
5591		t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2)
5592	}
5593}
5594
5595func TestContext2Apply_destroyOrder(t *testing.T) {
5596	m := testModule(t, "apply-destroy")
5597	h := new(HookRecordApplyOrder)
5598	p := testProvider("aws")
5599	p.PlanResourceChangeFn = testDiffFn
5600	ctx := testContext2(t, &ContextOpts{
5601		Config: m,
5602		Hooks:  []Hook{h},
5603		Providers: map[addrs.Provider]providers.Factory{
5604			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5605		},
5606	})
5607
5608	// First plan and apply a create operation
5609	if _, diags := ctx.Plan(); diags.HasErrors() {
5610		t.Fatalf("plan errors: %s", diags.Err())
5611	}
5612
5613	state, diags := ctx.Apply()
5614	if diags.HasErrors() {
5615		t.Fatalf("diags: %s", diags.Err())
5616	}
5617
5618	t.Logf("State 1: %s", state)
5619
5620	// Next, plan and apply a destroy
5621	h.Active = true
5622	ctx = testContext2(t, &ContextOpts{
5623		PlanMode: plans.DestroyMode,
5624		State:    state,
5625		Config:   m,
5626		Hooks:    []Hook{h},
5627		Providers: map[addrs.Provider]providers.Factory{
5628			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5629		},
5630	})
5631
5632	if _, diags := ctx.Plan(); diags.HasErrors() {
5633		t.Fatalf("plan errors: %s", diags.Err())
5634	}
5635
5636	state, diags = ctx.Apply()
5637	if diags.HasErrors() {
5638		t.Fatalf("diags: %s", diags.Err())
5639	}
5640
5641	// Test that things were destroyed
5642	actual := strings.TrimSpace(state.String())
5643	expected := strings.TrimSpace(testTerraformApplyDestroyStr)
5644	if actual != expected {
5645		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
5646	}
5647
5648	// Test that things were destroyed _in the right order_
5649	expected2 := []string{"aws_instance.bar", "aws_instance.foo"}
5650	actual2 := h.IDs
5651	if !reflect.DeepEqual(actual2, expected2) {
5652		t.Fatalf("expected: %#v\n\ngot:%#v", expected2, actual2)
5653	}
5654}
5655
5656// https://github.com/hashicorp/terraform/issues/2767
5657func TestContext2Apply_destroyModulePrefix(t *testing.T) {
5658	m := testModule(t, "apply-destroy-module-resource-prefix")
5659	h := new(MockHook)
5660	p := testProvider("aws")
5661	p.PlanResourceChangeFn = testDiffFn
5662	ctx := testContext2(t, &ContextOpts{
5663		Config: m,
5664		Hooks:  []Hook{h},
5665		Providers: map[addrs.Provider]providers.Factory{
5666			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5667		},
5668	})
5669
5670	// First plan and apply a create operation
5671	if _, diags := ctx.Plan(); diags.HasErrors() {
5672		t.Fatalf("plan errors: %s", diags.Err())
5673	}
5674
5675	state, diags := ctx.Apply()
5676	if diags.HasErrors() {
5677		t.Fatalf("diags: %s", diags.Err())
5678	}
5679
5680	// Verify that we got the apply info correct
5681	if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" {
5682		t.Fatalf("bad: %s", v)
5683	}
5684
5685	// Next, plan and apply a destroy operation and reset the hook
5686	h = new(MockHook)
5687	ctx = testContext2(t, &ContextOpts{
5688		PlanMode: plans.DestroyMode,
5689		State:    state,
5690		Config:   m,
5691		Hooks:    []Hook{h},
5692		Providers: map[addrs.Provider]providers.Factory{
5693			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5694		},
5695	})
5696
5697	if _, diags := ctx.Plan(); diags.HasErrors() {
5698		t.Fatalf("plan errors: %s", diags.Err())
5699	}
5700
5701	_, diags = ctx.Apply()
5702	if diags.HasErrors() {
5703		t.Fatalf("diags: %s", diags.Err())
5704	}
5705
5706	// Test that things were destroyed
5707	if v := h.PreApplyAddr.String(); v != "module.child.aws_instance.foo" {
5708		t.Fatalf("bad: %s", v)
5709	}
5710}
5711
5712func TestContext2Apply_destroyNestedModule(t *testing.T) {
5713	m := testModule(t, "apply-destroy-nested-module")
5714	p := testProvider("aws")
5715	p.PlanResourceChangeFn = testDiffFn
5716
5717	state := states.NewState()
5718	root := state.EnsureModule(addrs.RootModuleInstance)
5719	root.SetResourceInstanceCurrent(
5720		mustResourceInstanceAddr("aws_instance.bar").Resource,
5721		&states.ResourceInstanceObjectSrc{
5722			Status:    states.ObjectReady,
5723			AttrsJSON: []byte(`{"id":"bar"}`),
5724		},
5725		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
5726	)
5727
5728	ctx := testContext2(t, &ContextOpts{
5729		Config: m,
5730		Providers: map[addrs.Provider]providers.Factory{
5731			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5732		},
5733		State: state,
5734	})
5735
5736	// First plan and apply a create operation
5737	if _, diags := ctx.Plan(); diags.HasErrors() {
5738		t.Fatalf("plan errors: %s", diags.Err())
5739	}
5740
5741	s, diags := ctx.Apply()
5742	if diags.HasErrors() {
5743		t.Fatalf("diags: %s", diags.Err())
5744	}
5745
5746	// Test that things were destroyed
5747	actual := strings.TrimSpace(s.String())
5748	if actual != "<no state>" {
5749		t.Fatalf("expected no state, got: %s", actual)
5750	}
5751}
5752
5753func TestContext2Apply_destroyDeeplyNestedModule(t *testing.T) {
5754	m := testModule(t, "apply-destroy-deeply-nested-module")
5755	p := testProvider("aws")
5756	p.PlanResourceChangeFn = testDiffFn
5757
5758	state := states.NewState()
5759	root := state.EnsureModule(addrs.RootModuleInstance)
5760	root.SetResourceInstanceCurrent(
5761		mustResourceInstanceAddr("aws_instance.bar").Resource,
5762		&states.ResourceInstanceObjectSrc{
5763			Status:    states.ObjectReady,
5764			AttrsJSON: []byte(`{"id":"bar"}`),
5765		},
5766		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
5767	)
5768
5769	ctx := testContext2(t, &ContextOpts{
5770		Config: m,
5771		Providers: map[addrs.Provider]providers.Factory{
5772			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5773		},
5774		State: state,
5775	})
5776
5777	// First plan and apply a create operation
5778	if _, diags := ctx.Plan(); diags.HasErrors() {
5779		t.Fatalf("plan errors: %s", diags.Err())
5780	}
5781
5782	s, diags := ctx.Apply()
5783	if diags.HasErrors() {
5784		t.Fatalf("diags: %s", diags.Err())
5785	}
5786
5787	// Test that things were destroyed
5788	if !s.Empty() {
5789		t.Fatalf("wrong final state %s\nwant empty state", spew.Sdump(s))
5790	}
5791}
5792
5793// https://github.com/hashicorp/terraform/issues/5440
5794func TestContext2Apply_destroyModuleWithAttrsReferencingResource(t *testing.T) {
5795	m, snap := testModuleWithSnapshot(t, "apply-destroy-module-with-attrs")
5796	p := testProvider("aws")
5797	p.PlanResourceChangeFn = testDiffFn
5798
5799	var state *states.State
5800	{
5801		ctx := testContext2(t, &ContextOpts{
5802			Config: m,
5803			Providers: map[addrs.Provider]providers.Factory{
5804				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5805			},
5806		})
5807
5808		// First plan and apply a create operation
5809		if p, diags := ctx.Plan(); diags.HasErrors() {
5810			t.Fatalf("plan diags: %s", diags.Err())
5811		} else {
5812			t.Logf("Step 1 plan: %s", legacyDiffComparisonString(p.Changes))
5813		}
5814
5815		var diags tfdiags.Diagnostics
5816		state, diags = ctx.Apply()
5817		if diags.HasErrors() {
5818			t.Fatalf("apply errs: %s", diags.Err())
5819		}
5820
5821		t.Logf("Step 1 state: %s", state)
5822	}
5823
5824	h := new(HookRecordApplyOrder)
5825	h.Active = true
5826
5827	{
5828		ctx := testContext2(t, &ContextOpts{
5829			PlanMode: plans.DestroyMode,
5830			Config:   m,
5831			State:    state,
5832			Hooks:    []Hook{h},
5833			Providers: map[addrs.Provider]providers.Factory{
5834				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5835			},
5836		})
5837
5838		// First plan and apply a create operation
5839		plan, diags := ctx.Plan()
5840		if diags.HasErrors() {
5841			t.Fatalf("destroy plan err: %s", diags.Err())
5842		}
5843
5844		t.Logf("Step 2 plan: %s", legacyDiffComparisonString(plan.Changes))
5845
5846		ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
5847		if err != nil {
5848			t.Fatalf("failed to round-trip through planfile: %s", err)
5849		}
5850
5851		ctxOpts.Providers = map[addrs.Provider]providers.Factory{
5852			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5853		}
5854
5855		ctx, diags = NewContext(ctxOpts)
5856		if diags.HasErrors() {
5857			t.Fatalf("err: %s", diags.Err())
5858		}
5859
5860		state, diags = ctx.Apply()
5861		if diags.HasErrors() {
5862			t.Fatalf("destroy apply err: %s", diags.Err())
5863		}
5864
5865		t.Logf("Step 2 state: %s", state)
5866	}
5867
5868	//Test that things were destroyed
5869	if state.HasResources() {
5870		t.Fatal("expected empty state, got:", state)
5871	}
5872}
5873
5874func TestContext2Apply_destroyWithModuleVariableAndCount(t *testing.T) {
5875	m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count")
5876	p := testProvider("aws")
5877	p.PlanResourceChangeFn = testDiffFn
5878
5879	var state *states.State
5880	var diags tfdiags.Diagnostics
5881	{
5882		ctx := testContext2(t, &ContextOpts{
5883			Config: m,
5884			Providers: map[addrs.Provider]providers.Factory{
5885				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5886			},
5887		})
5888
5889		// First plan and apply a create operation
5890		if _, diags := ctx.Plan(); diags.HasErrors() {
5891			t.Fatalf("plan err: %s", diags.Err())
5892		}
5893
5894		state, diags = ctx.Apply()
5895		if diags.HasErrors() {
5896			t.Fatalf("apply err: %s", diags.Err())
5897		}
5898	}
5899
5900	h := new(HookRecordApplyOrder)
5901	h.Active = true
5902
5903	{
5904		ctx := testContext2(t, &ContextOpts{
5905			PlanMode: plans.DestroyMode,
5906			Config:   m,
5907			State:    state,
5908			Hooks:    []Hook{h},
5909			Providers: map[addrs.Provider]providers.Factory{
5910				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5911			},
5912		})
5913
5914		// First plan and apply a create operation
5915		plan, diags := ctx.Plan()
5916		if diags.HasErrors() {
5917			t.Fatalf("destroy plan err: %s", diags.Err())
5918		}
5919
5920		ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
5921		if err != nil {
5922			t.Fatalf("failed to round-trip through planfile: %s", err)
5923		}
5924
5925		ctxOpts.Providers =
5926			map[addrs.Provider]providers.Factory{
5927				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5928			}
5929
5930		ctx, diags = NewContext(ctxOpts)
5931		if diags.HasErrors() {
5932			t.Fatalf("err: %s", diags.Err())
5933		}
5934
5935		state, diags = ctx.Apply()
5936		if diags.HasErrors() {
5937			t.Fatalf("destroy apply err: %s", diags.Err())
5938		}
5939	}
5940
5941	//Test that things were destroyed
5942	actual := strings.TrimSpace(state.String())
5943	expected := strings.TrimSpace(`
5944<no state>`)
5945	if actual != expected {
5946		t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
5947	}
5948}
5949
5950func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) {
5951	m := testModule(t, "apply-destroy-mod-var-and-count")
5952	p := testProvider("aws")
5953	p.PlanResourceChangeFn = testDiffFn
5954
5955	var state *states.State
5956	var diags tfdiags.Diagnostics
5957	{
5958		ctx := testContext2(t, &ContextOpts{
5959			Config: m,
5960			Providers: map[addrs.Provider]providers.Factory{
5961				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5962			},
5963		})
5964
5965		// First plan and apply a create operation
5966		if _, diags := ctx.Plan(); diags.HasErrors() {
5967			t.Fatalf("plan err: %s", diags.Err())
5968		}
5969
5970		state, diags = ctx.Apply()
5971		if diags.HasErrors() {
5972			t.Fatalf("apply err: %s", diags.Err())
5973		}
5974	}
5975
5976	{
5977		ctx := testContext2(t, &ContextOpts{
5978			PlanMode: plans.DestroyMode,
5979			Config:   m,
5980			State:    state,
5981			Providers: map[addrs.Provider]providers.Factory{
5982				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
5983			},
5984			Targets: []addrs.Targetable{
5985				addrs.RootModuleInstance.Child("child", addrs.NoKey),
5986			},
5987		})
5988
5989		_, diags := ctx.Plan()
5990		if diags.HasErrors() {
5991			t.Fatalf("plan err: %s", diags)
5992		}
5993		if len(diags) != 1 {
5994			// Should have one warning that -target is in effect.
5995			t.Fatalf("got %d diagnostics in plan; want 1", len(diags))
5996		}
5997		if got, want := diags[0].Severity(), tfdiags.Warning; got != want {
5998			t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
5999		}
6000		if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want {
6001			t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
6002		}
6003
6004		// Destroy, targeting the module explicitly
6005		state, diags = ctx.Apply()
6006		if diags.HasErrors() {
6007			t.Fatalf("destroy apply err: %s", diags)
6008		}
6009		if len(diags) != 1 {
6010			t.Fatalf("got %d diagnostics; want 1", len(diags))
6011		}
6012		if got, want := diags[0].Severity(), tfdiags.Warning; got != want {
6013			t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
6014		}
6015		if got, want := diags[0].Description().Summary, "Applied changes may be incomplete"; got != want {
6016			t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
6017		}
6018	}
6019
6020	//Test that things were destroyed
6021	actual := strings.TrimSpace(state.String())
6022	expected := strings.TrimSpace(`<no state>`)
6023	if actual != expected {
6024		t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
6025	}
6026}
6027
6028func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) {
6029	m, snap := testModuleWithSnapshot(t, "apply-destroy-mod-var-and-count-nested")
6030	p := testProvider("aws")
6031	p.PlanResourceChangeFn = testDiffFn
6032
6033	var state *states.State
6034	var diags tfdiags.Diagnostics
6035	{
6036		ctx := testContext2(t, &ContextOpts{
6037			Config: m,
6038			Providers: map[addrs.Provider]providers.Factory{
6039				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6040			},
6041		})
6042
6043		// First plan and apply a create operation
6044		if _, diags := ctx.Plan(); diags.HasErrors() {
6045			t.Fatalf("plan err: %s", diags.Err())
6046		}
6047
6048		state, diags = ctx.Apply()
6049		if diags.HasErrors() {
6050			t.Fatalf("apply err: %s", diags.Err())
6051		}
6052	}
6053
6054	h := new(HookRecordApplyOrder)
6055	h.Active = true
6056
6057	{
6058		ctx := testContext2(t, &ContextOpts{
6059			PlanMode: plans.DestroyMode,
6060			Config:   m,
6061			State:    state,
6062			Hooks:    []Hook{h},
6063			Providers: map[addrs.Provider]providers.Factory{
6064				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6065			},
6066		})
6067
6068		// First plan and apply a create operation
6069		plan, diags := ctx.Plan()
6070		if diags.HasErrors() {
6071			t.Fatalf("destroy plan err: %s", diags.Err())
6072		}
6073
6074		ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
6075		if err != nil {
6076			t.Fatalf("failed to round-trip through planfile: %s", err)
6077		}
6078
6079		ctxOpts.Providers =
6080			map[addrs.Provider]providers.Factory{
6081				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6082			}
6083
6084		ctx, diags = NewContext(ctxOpts)
6085		if diags.HasErrors() {
6086			t.Fatalf("err: %s", diags.Err())
6087		}
6088
6089		state, diags = ctx.Apply()
6090		if diags.HasErrors() {
6091			t.Fatalf("destroy apply err: %s", diags.Err())
6092		}
6093	}
6094
6095	//Test that things were destroyed
6096	actual := strings.TrimSpace(state.String())
6097	expected := strings.TrimSpace(`
6098<no state>`)
6099	if actual != expected {
6100		t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
6101	}
6102}
6103
6104func TestContext2Apply_destroyOutputs(t *testing.T) {
6105	m := testModule(t, "apply-destroy-outputs")
6106	p := testProvider("test")
6107	p.PlanResourceChangeFn = testDiffFn
6108
6109	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
6110		// add the required id
6111		m := req.Config.AsValueMap()
6112		m["id"] = cty.StringVal("foo")
6113
6114		return providers.ReadDataSourceResponse{
6115			State: cty.ObjectVal(m),
6116		}
6117	}
6118
6119	ctx := testContext2(t, &ContextOpts{
6120		Config: m,
6121		Providers: map[addrs.Provider]providers.Factory{
6122			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
6123		},
6124	})
6125
6126	// First plan and apply a create operation
6127	if _, diags := ctx.Plan(); diags.HasErrors() {
6128		t.Fatalf("plan errors: %s", diags.Err())
6129	}
6130
6131	state, diags := ctx.Apply()
6132
6133	if diags.HasErrors() {
6134		t.Fatalf("diags: %s", diags.Err())
6135	}
6136
6137	// Next, plan and apply a destroy operation
6138	ctx = testContext2(t, &ContextOpts{
6139		PlanMode: plans.DestroyMode,
6140		State:    state,
6141		Config:   m,
6142		Providers: map[addrs.Provider]providers.Factory{
6143			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
6144		},
6145	})
6146
6147	if _, diags := ctx.Plan(); diags.HasErrors() {
6148		t.Fatalf("plan errors: %s", diags.Err())
6149	}
6150
6151	state, diags = ctx.Apply()
6152	if diags.HasErrors() {
6153		t.Fatalf("diags: %s", diags.Err())
6154	}
6155
6156	mod := state.RootModule()
6157	if len(mod.Resources) > 0 {
6158		t.Fatalf("expected no resources, got: %#v", mod)
6159	}
6160
6161	// destroying again should produce no errors
6162	ctx = testContext2(t, &ContextOpts{
6163		PlanMode: plans.DestroyMode,
6164		State:    state,
6165		Config:   m,
6166		Providers: map[addrs.Provider]providers.Factory{
6167			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
6168		},
6169	})
6170	if _, diags := ctx.Plan(); diags.HasErrors() {
6171		t.Fatal(diags.Err())
6172	}
6173
6174	if _, diags := ctx.Apply(); diags.HasErrors() {
6175		t.Fatal(diags.Err())
6176	}
6177}
6178
6179func TestContext2Apply_destroyOrphan(t *testing.T) {
6180	m := testModule(t, "apply-error")
6181	p := testProvider("aws")
6182	state := states.NewState()
6183	root := state.EnsureModule(addrs.RootModuleInstance)
6184	root.SetResourceInstanceCurrent(
6185		mustResourceInstanceAddr("aws_instance.baz").Resource,
6186		&states.ResourceInstanceObjectSrc{
6187			Status:    states.ObjectReady,
6188			AttrsJSON: []byte(`{"id":"bar"}`),
6189		},
6190		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6191	)
6192	ctx := testContext2(t, &ContextOpts{
6193		Config: m,
6194		Providers: map[addrs.Provider]providers.Factory{
6195			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6196		},
6197		State: state,
6198	})
6199
6200	p.PlanResourceChangeFn = testDiffFn
6201
6202	if _, diags := ctx.Plan(); diags.HasErrors() {
6203		t.Fatalf("plan errors: %s", diags.Err())
6204	}
6205
6206	s, diags := ctx.Apply()
6207	if diags.HasErrors() {
6208		t.Fatalf("diags: %s", diags.Err())
6209	}
6210
6211	mod := s.RootModule()
6212	if _, ok := mod.Resources["aws_instance.baz"]; ok {
6213		t.Fatalf("bad: %#v", mod.Resources)
6214	}
6215}
6216
6217func TestContext2Apply_destroyTaintedProvisioner(t *testing.T) {
6218	m := testModule(t, "apply-destroy-provisioner")
6219	p := testProvider("aws")
6220	pr := testProvisioner()
6221	p.PlanResourceChangeFn = testDiffFn
6222
6223	state := states.NewState()
6224	root := state.EnsureModule(addrs.RootModuleInstance)
6225	root.SetResourceInstanceCurrent(
6226		mustResourceInstanceAddr("aws_instance.foo").Resource,
6227		&states.ResourceInstanceObjectSrc{
6228			Status:    states.ObjectReady,
6229			AttrsJSON: []byte(`{"id":"bar"}`),
6230		},
6231		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6232	)
6233
6234	ctx := testContext2(t, &ContextOpts{
6235		Config: m,
6236		Providers: map[addrs.Provider]providers.Factory{
6237			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6238		},
6239		Provisioners: map[string]provisioners.Factory{
6240			"shell": testProvisionerFuncFixed(pr),
6241		},
6242		State:    state,
6243		PlanMode: plans.DestroyMode,
6244	})
6245
6246	if _, diags := ctx.Plan(); diags.HasErrors() {
6247		t.Fatalf("plan errors: %s", diags.Err())
6248	}
6249
6250	s, diags := ctx.Apply()
6251	if diags.HasErrors() {
6252		t.Fatalf("diags: %s", diags.Err())
6253	}
6254
6255	if pr.ProvisionResourceCalled {
6256		t.Fatal("provisioner should not be called")
6257	}
6258
6259	actual := strings.TrimSpace(s.String())
6260	expected := strings.TrimSpace("<no state>")
6261	if actual != expected {
6262		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
6263	}
6264}
6265
6266func TestContext2Apply_error(t *testing.T) {
6267	errored := false
6268
6269	m := testModule(t, "apply-error")
6270	p := testProvider("aws")
6271	ctx := testContext2(t, &ContextOpts{
6272		Config: m,
6273		Providers: map[addrs.Provider]providers.Factory{
6274			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6275		},
6276	})
6277
6278	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
6279		if errored {
6280			resp.NewState = req.PlannedState
6281			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error"))
6282			return
6283		}
6284		errored = true
6285
6286		return testApplyFn(req)
6287	}
6288	p.PlanResourceChangeFn = testDiffFn
6289
6290	if _, diags := ctx.Plan(); diags.HasErrors() {
6291		t.Fatalf("plan errors: %s", diags.Err())
6292	}
6293
6294	state, diags := ctx.Apply()
6295	if diags == nil {
6296		t.Fatal("should have error")
6297	}
6298
6299	actual := strings.TrimSpace(state.String())
6300	expected := strings.TrimSpace(testTerraformApplyErrorStr)
6301	if actual != expected {
6302		t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
6303	}
6304}
6305
6306func TestContext2Apply_errorDestroy(t *testing.T) {
6307	m := testModule(t, "empty")
6308	p := testProvider("test")
6309
6310	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
6311		ResourceTypes: map[string]*configschema.Block{
6312			"test_thing": {
6313				Attributes: map[string]*configschema.Attribute{
6314					"id": {Type: cty.String, Optional: true},
6315				},
6316			},
6317		},
6318	})
6319	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
6320		// Should actually be called for this test, because Terraform Core
6321		// constructs the plan for a destroy operation itself.
6322		return providers.PlanResourceChangeResponse{
6323			PlannedState: req.ProposedNewState,
6324		}
6325	}
6326	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
6327		// The apply (in this case, a destroy) always fails, so we can verify
6328		// that the object stays in the state after a destroy fails even though
6329		// we aren't returning a new state object here.
6330		return providers.ApplyResourceChangeResponse{
6331			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("failed")),
6332		}
6333	}
6334
6335	ctx := testContext2(t, &ContextOpts{
6336		Config: m,
6337		State: states.BuildState(func(ss *states.SyncState) {
6338			ss.SetResourceInstanceCurrent(
6339				addrs.Resource{
6340					Mode: addrs.ManagedResourceMode,
6341					Type: "test_thing",
6342					Name: "foo",
6343				}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
6344				&states.ResourceInstanceObjectSrc{
6345					Status:    states.ObjectReady,
6346					AttrsJSON: []byte(`{"id":"baz"}`),
6347				},
6348				addrs.AbsProviderConfig{
6349					Provider: addrs.NewDefaultProvider("test"),
6350					Module:   addrs.RootModule,
6351				},
6352			)
6353		}),
6354		Providers: map[addrs.Provider]providers.Factory{
6355			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
6356		},
6357	})
6358
6359	if _, diags := ctx.Plan(); diags.HasErrors() {
6360		t.Fatalf("plan errors: %s", diags.Err())
6361	}
6362
6363	state, diags := ctx.Apply()
6364	if diags == nil {
6365		t.Fatal("should have error")
6366	}
6367
6368	actual := strings.TrimSpace(state.String())
6369	expected := strings.TrimSpace(`
6370test_thing.foo:
6371  ID = baz
6372  provider = provider["registry.terraform.io/hashicorp/test"]
6373`) // test_thing.foo is still here, even though provider returned no new state along with its error
6374	if actual != expected {
6375		t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
6376	}
6377}
6378
6379func TestContext2Apply_errorCreateInvalidNew(t *testing.T) {
6380	m := testModule(t, "apply-error")
6381
6382	p := testProvider("aws")
6383	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
6384		ResourceTypes: map[string]*configschema.Block{
6385			"aws_instance": {
6386				Attributes: map[string]*configschema.Attribute{
6387					"value": {Type: cty.String, Optional: true},
6388					"foo":   {Type: cty.String, Optional: true},
6389				},
6390			},
6391		},
6392	})
6393	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
6394		return providers.PlanResourceChangeResponse{
6395			PlannedState: req.ProposedNewState,
6396		}
6397	}
6398	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
6399		// We're intentionally returning an inconsistent new state here
6400		// because we want to test that Terraform ignores the inconsistency
6401		// when accompanied by another error.
6402		return providers.ApplyResourceChangeResponse{
6403			NewState: cty.ObjectVal(map[string]cty.Value{
6404				"value": cty.StringVal("wrong wrong wrong wrong"),
6405				"foo":   cty.StringVal("absolutely brimming over with wrongability"),
6406			}),
6407			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")),
6408		}
6409	}
6410
6411	ctx := testContext2(t, &ContextOpts{
6412		Config: m,
6413		Providers: map[addrs.Provider]providers.Factory{
6414			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6415		},
6416	})
6417
6418	if _, diags := ctx.Plan(); diags.HasErrors() {
6419		t.Fatalf("plan errors: %s", diags.Err())
6420	}
6421
6422	state, diags := ctx.Apply()
6423	if diags == nil {
6424		t.Fatal("should have error")
6425	}
6426	if got, want := len(diags), 1; got != want {
6427		// There should be no additional diagnostics generated by Terraform's own eval logic,
6428		// because the provider's own error supersedes them.
6429		t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err())
6430	}
6431	if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) {
6432		t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err())
6433	}
6434	if got, want := len(state.RootModule().Resources), 2; got != want {
6435		t.Errorf("%d resources in state before prune; should have %d\n%s", got, want, spew.Sdump(state))
6436	}
6437	state.PruneResourceHusks() // aws_instance.bar with no instances gets left behind when we bail out, but that's okay
6438	if got, want := len(state.RootModule().Resources), 1; got != want {
6439		t.Errorf("%d resources in state after prune; should have only one (aws_instance.foo, tainted)\n%s", got, spew.Sdump(state))
6440	}
6441}
6442
6443func TestContext2Apply_errorUpdateNullNew(t *testing.T) {
6444	m := testModule(t, "apply-error")
6445
6446	p := testProvider("aws")
6447	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
6448		ResourceTypes: map[string]*configschema.Block{
6449			"aws_instance": {
6450				Attributes: map[string]*configschema.Attribute{
6451					"value": {Type: cty.String, Optional: true},
6452					"foo":   {Type: cty.String, Optional: true},
6453				},
6454			},
6455		},
6456	})
6457	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
6458		return providers.PlanResourceChangeResponse{
6459			PlannedState: req.ProposedNewState,
6460		}
6461	}
6462	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
6463		// We're intentionally returning no NewState here because we want to
6464		// test that Terraform retains the prior state, rather than treating
6465		// the returned null as "no state" (object deleted).
6466		return providers.ApplyResourceChangeResponse{
6467			Diagnostics: tfdiags.Diagnostics(nil).Append(fmt.Errorf("forced error")),
6468		}
6469	}
6470
6471	ctx := testContext2(t, &ContextOpts{
6472		Config: m,
6473		State: states.BuildState(func(ss *states.SyncState) {
6474			ss.SetResourceInstanceCurrent(
6475				addrs.Resource{
6476					Mode: addrs.ManagedResourceMode,
6477					Type: "aws_instance",
6478					Name: "foo",
6479				}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
6480				&states.ResourceInstanceObjectSrc{
6481					Status:    states.ObjectReady,
6482					AttrsJSON: []byte(`{"value":"old"}`),
6483				},
6484				addrs.AbsProviderConfig{
6485					Provider: addrs.NewDefaultProvider("aws"),
6486					Module:   addrs.RootModule,
6487				},
6488			)
6489		}),
6490		Providers: map[addrs.Provider]providers.Factory{
6491			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6492		},
6493	})
6494
6495	if _, diags := ctx.Plan(); diags.HasErrors() {
6496		t.Fatalf("plan errors: %s", diags.Err())
6497	}
6498
6499	state, diags := ctx.Apply()
6500	if diags == nil {
6501		t.Fatal("should have error")
6502	}
6503	if got, want := len(diags), 1; got != want {
6504		// There should be no additional diagnostics generated by Terraform's own eval logic,
6505		// because the provider's own error supersedes them.
6506		t.Errorf("wrong number of diagnostics %d; want %d\n%s", got, want, diags.Err())
6507	}
6508	if got, want := diags.Err().Error(), "forced error"; !strings.Contains(got, want) {
6509		t.Errorf("returned error does not contain %q, but it should\n%s", want, diags.Err())
6510	}
6511	state.PruneResourceHusks()
6512	if got, want := len(state.RootModule().Resources), 1; got != want {
6513		t.Fatalf("%d resources in state; should have only one (aws_instance.foo, unmodified)\n%s", got, spew.Sdump(state))
6514	}
6515
6516	is := state.ResourceInstance(addrs.Resource{
6517		Mode: addrs.ManagedResourceMode,
6518		Type: "aws_instance",
6519		Name: "foo",
6520	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance))
6521	if is == nil {
6522		t.Fatalf("aws_instance.foo is not in the state after apply")
6523	}
6524	if got, want := is.Current.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) {
6525		t.Fatalf("incorrect attributes for aws_instance.foo\ngot: %s\nwant: JSON containing %s\n\n%s", got, want, spew.Sdump(is))
6526	}
6527}
6528
6529func TestContext2Apply_errorPartial(t *testing.T) {
6530	errored := false
6531
6532	m := testModule(t, "apply-error")
6533	p := testProvider("aws")
6534
6535	state := states.NewState()
6536	root := state.EnsureModule(addrs.RootModuleInstance)
6537	root.SetResourceInstanceCurrent(
6538		mustResourceInstanceAddr("aws_instance.bar").Resource,
6539		&states.ResourceInstanceObjectSrc{
6540			Status:    states.ObjectReady,
6541			AttrsJSON: []byte(`{"id":"bar","type":"aws_instance"}`),
6542		},
6543		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6544	)
6545
6546	ctx := testContext2(t, &ContextOpts{
6547		Config: m,
6548		Providers: map[addrs.Provider]providers.Factory{
6549			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6550		},
6551		State: state,
6552	})
6553
6554	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
6555		if errored {
6556			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("error"))
6557			return
6558		}
6559		errored = true
6560
6561		return testApplyFn(req)
6562	}
6563	p.PlanResourceChangeFn = testDiffFn
6564
6565	if _, diags := ctx.Plan(); diags.HasErrors() {
6566		t.Fatalf("plan errors: %s", diags.Err())
6567	}
6568
6569	s, diags := ctx.Apply()
6570	if diags == nil {
6571		t.Fatal("should have error")
6572	}
6573
6574	mod := s.RootModule()
6575	if len(mod.Resources) != 2 {
6576		t.Fatalf("bad: %#v", mod.Resources)
6577	}
6578
6579	actual := strings.TrimSpace(s.String())
6580	expected := strings.TrimSpace(testTerraformApplyErrorPartialStr)
6581	if actual != expected {
6582		t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
6583	}
6584}
6585
6586func TestContext2Apply_hook(t *testing.T) {
6587	m := testModule(t, "apply-good")
6588	h := new(MockHook)
6589	p := testProvider("aws")
6590	p.PlanResourceChangeFn = testDiffFn
6591	ctx := testContext2(t, &ContextOpts{
6592		Config: m,
6593		Hooks:  []Hook{h},
6594		Providers: map[addrs.Provider]providers.Factory{
6595			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6596		},
6597	})
6598
6599	if _, diags := ctx.Plan(); diags.HasErrors() {
6600		t.Fatalf("plan errors: %s", diags.Err())
6601	}
6602
6603	if _, diags := ctx.Apply(); diags.HasErrors() {
6604		t.Fatalf("apply errors: %s", diags.Err())
6605	}
6606
6607	if !h.PreApplyCalled {
6608		t.Fatal("should be called")
6609	}
6610	if !h.PostApplyCalled {
6611		t.Fatal("should be called")
6612	}
6613	if !h.PostStateUpdateCalled {
6614		t.Fatalf("should call post state update")
6615	}
6616}
6617
6618func TestContext2Apply_hookOrphan(t *testing.T) {
6619	m := testModule(t, "apply-blank")
6620	h := new(MockHook)
6621	p := testProvider("aws")
6622	p.PlanResourceChangeFn = testDiffFn
6623
6624	state := states.NewState()
6625	root := state.EnsureModule(addrs.RootModuleInstance)
6626	root.SetResourceInstanceCurrent(
6627		mustResourceInstanceAddr("aws_instance.bar").Resource,
6628		&states.ResourceInstanceObjectSrc{
6629			Status:    states.ObjectReady,
6630			AttrsJSON: []byte(`{"id":"bar"}`),
6631		},
6632		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6633	)
6634
6635	ctx := testContext2(t, &ContextOpts{
6636		Config: m,
6637		State:  state,
6638		Hooks:  []Hook{h},
6639		Providers: map[addrs.Provider]providers.Factory{
6640			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6641		},
6642	})
6643
6644	if _, diags := ctx.Plan(); diags.HasErrors() {
6645		t.Fatalf("plan errors: %s", diags.Err())
6646	}
6647
6648	if _, diags := ctx.Apply(); diags.HasErrors() {
6649		t.Fatalf("apply errors: %s", diags.Err())
6650	}
6651
6652	if !h.PreApplyCalled {
6653		t.Fatal("should be called")
6654	}
6655	if !h.PostApplyCalled {
6656		t.Fatal("should be called")
6657	}
6658	if !h.PostStateUpdateCalled {
6659		t.Fatalf("should call post state update")
6660	}
6661}
6662
6663func TestContext2Apply_idAttr(t *testing.T) {
6664	m := testModule(t, "apply-idattr")
6665	p := testProvider("aws")
6666	ctx := testContext2(t, &ContextOpts{
6667		Config: m,
6668		Providers: map[addrs.Provider]providers.Factory{
6669			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6670		},
6671	})
6672
6673	p.PlanResourceChangeFn = testDiffFn
6674	p.ApplyResourceChangeFn = testApplyFn
6675
6676	if _, diags := ctx.Plan(); diags.HasErrors() {
6677		t.Fatalf("plan errors: %s", diags.Err())
6678	}
6679
6680	state, diags := ctx.Apply()
6681	if diags.HasErrors() {
6682		t.Fatalf("apply errors: %s", diags.Err())
6683	}
6684
6685	mod := state.RootModule()
6686	rs, ok := mod.Resources["aws_instance.foo"]
6687	if !ok {
6688		t.Fatal("not in state")
6689	}
6690	var attrs map[string]interface{}
6691	err := json.Unmarshal(rs.Instances[addrs.NoKey].Current.AttrsJSON, &attrs)
6692	if err != nil {
6693		t.Fatal(err)
6694	}
6695	if got, want := attrs["id"], "foo"; got != want {
6696		t.Fatalf("wrong id\ngot:  %#v\nwant: %#v", got, want)
6697	}
6698}
6699
6700func TestContext2Apply_outputBasic(t *testing.T) {
6701	m := testModule(t, "apply-output")
6702	p := testProvider("aws")
6703	p.PlanResourceChangeFn = testDiffFn
6704	p.ApplyResourceChangeFn = testApplyFn
6705	ctx := testContext2(t, &ContextOpts{
6706		Config: m,
6707		Providers: map[addrs.Provider]providers.Factory{
6708			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6709		},
6710	})
6711
6712	if _, diags := ctx.Plan(); diags.HasErrors() {
6713		t.Fatalf("plan errors: %s", diags.Err())
6714	}
6715
6716	state, diags := ctx.Apply()
6717	if diags.HasErrors() {
6718		t.Fatalf("diags: %s", diags.Err())
6719	}
6720
6721	actual := strings.TrimSpace(state.String())
6722	expected := strings.TrimSpace(testTerraformApplyOutputStr)
6723	if actual != expected {
6724		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
6725	}
6726}
6727
6728func TestContext2Apply_outputAdd(t *testing.T) {
6729	m1 := testModule(t, "apply-output-add-before")
6730	p1 := testProvider("aws")
6731	p1.ApplyResourceChangeFn = testApplyFn
6732	p1.PlanResourceChangeFn = testDiffFn
6733	ctx1 := testContext2(t, &ContextOpts{
6734		Config: m1,
6735		Providers: map[addrs.Provider]providers.Factory{
6736			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p1),
6737		},
6738	})
6739
6740	if _, diags := ctx1.Plan(); diags.HasErrors() {
6741		t.Fatalf("diags: %s", diags.Err())
6742	}
6743
6744	state1, diags := ctx1.Apply()
6745	if diags.HasErrors() {
6746		t.Fatalf("diags: %s", diags.Err())
6747	}
6748
6749	m2 := testModule(t, "apply-output-add-after")
6750	p2 := testProvider("aws")
6751	p2.ApplyResourceChangeFn = testApplyFn
6752	p2.PlanResourceChangeFn = testDiffFn
6753	ctx2 := testContext2(t, &ContextOpts{
6754		Config: m2,
6755		Providers: map[addrs.Provider]providers.Factory{
6756			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p2),
6757		},
6758
6759		State: state1,
6760	})
6761
6762	if _, diags := ctx2.Plan(); diags.HasErrors() {
6763		t.Fatalf("diags: %s", diags.Err())
6764	}
6765
6766	state2, diags := ctx2.Apply()
6767	if diags.HasErrors() {
6768		t.Fatalf("diags: %s", diags.Err())
6769	}
6770
6771	actual := strings.TrimSpace(state2.String())
6772	expected := strings.TrimSpace(testTerraformApplyOutputAddStr)
6773	if actual != expected {
6774		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
6775	}
6776}
6777
6778func TestContext2Apply_outputList(t *testing.T) {
6779	m := testModule(t, "apply-output-list")
6780	p := testProvider("aws")
6781	p.PlanResourceChangeFn = testDiffFn
6782	p.ApplyResourceChangeFn = testApplyFn
6783	ctx := testContext2(t, &ContextOpts{
6784		Config: m,
6785		Providers: map[addrs.Provider]providers.Factory{
6786			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6787		},
6788	})
6789
6790	if _, diags := ctx.Plan(); diags.HasErrors() {
6791		t.Fatalf("plan errors: %s", diags.Err())
6792	}
6793
6794	state, diags := ctx.Apply()
6795	if diags.HasErrors() {
6796		t.Fatalf("diags: %s", diags.Err())
6797	}
6798
6799	actual := strings.TrimSpace(state.String())
6800	expected := strings.TrimSpace(testTerraformApplyOutputListStr)
6801	if actual != expected {
6802		t.Fatalf("expected: \n%s\n\nbad: \n%s", expected, actual)
6803	}
6804}
6805
6806func TestContext2Apply_outputMulti(t *testing.T) {
6807	m := testModule(t, "apply-output-multi")
6808	p := testProvider("aws")
6809	p.PlanResourceChangeFn = testDiffFn
6810	p.ApplyResourceChangeFn = testApplyFn
6811	ctx := testContext2(t, &ContextOpts{
6812		Config: m,
6813		Providers: map[addrs.Provider]providers.Factory{
6814			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6815		},
6816	})
6817
6818	if _, diags := ctx.Plan(); diags.HasErrors() {
6819		t.Fatalf("plan errors: %s", diags.Err())
6820	}
6821
6822	state, diags := ctx.Apply()
6823	if diags.HasErrors() {
6824		t.Fatalf("diags: %s", diags.Err())
6825	}
6826
6827	actual := strings.TrimSpace(state.String())
6828	expected := strings.TrimSpace(testTerraformApplyOutputMultiStr)
6829	if actual != expected {
6830		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
6831	}
6832}
6833
6834func TestContext2Apply_outputMultiIndex(t *testing.T) {
6835	m := testModule(t, "apply-output-multi-index")
6836	p := testProvider("aws")
6837	p.PlanResourceChangeFn = testDiffFn
6838	p.ApplyResourceChangeFn = testApplyFn
6839	ctx := testContext2(t, &ContextOpts{
6840		Config: m,
6841		Providers: map[addrs.Provider]providers.Factory{
6842			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6843		},
6844	})
6845
6846	if _, diags := ctx.Plan(); diags.HasErrors() {
6847		t.Fatalf("plan errors: %s", diags.Err())
6848	}
6849
6850	state, diags := ctx.Apply()
6851	if diags.HasErrors() {
6852		t.Fatalf("diags: %s", diags.Err())
6853	}
6854
6855	actual := strings.TrimSpace(state.String())
6856	expected := strings.TrimSpace(testTerraformApplyOutputMultiIndexStr)
6857	if actual != expected {
6858		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
6859	}
6860}
6861
6862func TestContext2Apply_taintX(t *testing.T) {
6863	m := testModule(t, "apply-taint")
6864	p := testProvider("aws")
6865	// destroyCount tests against regression of
6866	// https://github.com/hashicorp/terraform/issues/1056
6867	var destroyCount = int32(0)
6868	var once sync.Once
6869	simulateProviderDelay := func() {
6870		time.Sleep(10 * time.Millisecond)
6871	}
6872
6873	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
6874		once.Do(simulateProviderDelay)
6875		if req.PlannedState.IsNull() {
6876			atomic.AddInt32(&destroyCount, 1)
6877		}
6878		return testApplyFn(req)
6879	}
6880	p.PlanResourceChangeFn = testDiffFn
6881
6882	state := states.NewState()
6883	root := state.EnsureModule(addrs.RootModuleInstance)
6884	root.SetResourceInstanceCurrent(
6885		mustResourceInstanceAddr("aws_instance.bar").Resource,
6886		&states.ResourceInstanceObjectSrc{
6887			Status:    states.ObjectTainted,
6888			AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`),
6889		},
6890		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6891	)
6892
6893	ctx := testContext2(t, &ContextOpts{
6894		Config: m,
6895		Providers: map[addrs.Provider]providers.Factory{
6896			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6897		},
6898		State: state,
6899	})
6900
6901	if p, diags := ctx.Plan(); diags.HasErrors() {
6902		t.Fatalf("diags: %s", diags.Err())
6903	} else {
6904		t.Logf("plan: %s", legacyDiffComparisonString(p.Changes))
6905	}
6906
6907	s, diags := ctx.Apply()
6908	if diags.HasErrors() {
6909		t.Fatalf("diags: %s", diags.Err())
6910	}
6911
6912	actual := strings.TrimSpace(s.String())
6913	expected := strings.TrimSpace(testTerraformApplyTaintStr)
6914	if actual != expected {
6915		t.Fatalf("bad:\n%s", actual)
6916	}
6917
6918	if destroyCount != 1 {
6919		t.Fatalf("Expected 1 destroy, got %d", destroyCount)
6920	}
6921}
6922
6923func TestContext2Apply_taintDep(t *testing.T) {
6924	m := testModule(t, "apply-taint-dep")
6925	p := testProvider("aws")
6926	p.PlanResourceChangeFn = testDiffFn
6927	p.ApplyResourceChangeFn = testApplyFn
6928
6929	state := states.NewState()
6930	root := state.EnsureModule(addrs.RootModuleInstance)
6931	root.SetResourceInstanceCurrent(
6932		mustResourceInstanceAddr("aws_instance.foo").Resource,
6933		&states.ResourceInstanceObjectSrc{
6934			Status:    states.ObjectTainted,
6935			AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`),
6936		},
6937		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6938	)
6939	root.SetResourceInstanceCurrent(
6940		mustResourceInstanceAddr("aws_instance.bar").Resource,
6941		&states.ResourceInstanceObjectSrc{
6942			Status:       states.ObjectReady,
6943			AttrsJSON:    []byte(`{"id":"bar","num": "2", "type": "aws_instance", "foo": "baz"}`),
6944			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")},
6945		},
6946		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6947	)
6948
6949	ctx := testContext2(t, &ContextOpts{
6950		Config: m,
6951		Providers: map[addrs.Provider]providers.Factory{
6952			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
6953		},
6954		State: state,
6955	})
6956
6957	if p, diags := ctx.Plan(); diags.HasErrors() {
6958		t.Fatalf("diags: %s", diags.Err())
6959	} else {
6960		t.Logf("plan: %s", legacyDiffComparisonString(p.Changes))
6961	}
6962
6963	s, diags := ctx.Apply()
6964	if diags.HasErrors() {
6965		t.Fatalf("diags: %s", diags.Err())
6966	}
6967
6968	actual := strings.TrimSpace(s.String())
6969	expected := strings.TrimSpace(testTerraformApplyTaintDepStr)
6970	if actual != expected {
6971		t.Fatalf("bad:\n%s", actual)
6972	}
6973}
6974
6975func TestContext2Apply_taintDepRequiresNew(t *testing.T) {
6976	m := testModule(t, "apply-taint-dep-requires-new")
6977	p := testProvider("aws")
6978	p.PlanResourceChangeFn = testDiffFn
6979	p.ApplyResourceChangeFn = testApplyFn
6980
6981	state := states.NewState()
6982	root := state.EnsureModule(addrs.RootModuleInstance)
6983	root.SetResourceInstanceCurrent(
6984		mustResourceInstanceAddr("aws_instance.foo").Resource,
6985		&states.ResourceInstanceObjectSrc{
6986			Status:    states.ObjectTainted,
6987			AttrsJSON: []byte(`{"id":"baz","num": "2", "type": "aws_instance"}`),
6988		},
6989		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6990	)
6991	root.SetResourceInstanceCurrent(
6992		mustResourceInstanceAddr("aws_instance.bar").Resource,
6993		&states.ResourceInstanceObjectSrc{
6994			Status:       states.ObjectReady,
6995			AttrsJSON:    []byte(`{"id":"bar","num": "2", "type": "aws_instance", "foo": "baz"}`),
6996			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")},
6997		},
6998		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
6999	)
7000
7001	ctx := testContext2(t, &ContextOpts{
7002		Config: m,
7003		Providers: map[addrs.Provider]providers.Factory{
7004			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7005		},
7006		State: state,
7007	})
7008
7009	if p, diags := ctx.Plan(); diags.HasErrors() {
7010		t.Fatalf("diags: %s", diags.Err())
7011	} else {
7012		t.Logf("plan: %s", legacyDiffComparisonString(p.Changes))
7013	}
7014
7015	s, diags := ctx.Apply()
7016	if diags.HasErrors() {
7017		t.Fatalf("diags: %s", diags.Err())
7018	}
7019
7020	actual := strings.TrimSpace(s.String())
7021	expected := strings.TrimSpace(testTerraformApplyTaintDepRequireNewStr)
7022	if actual != expected {
7023		t.Fatalf("bad:\n%s", actual)
7024	}
7025}
7026
7027func TestContext2Apply_targeted(t *testing.T) {
7028	m := testModule(t, "apply-targeted")
7029	p := testProvider("aws")
7030	p.PlanResourceChangeFn = testDiffFn
7031	p.ApplyResourceChangeFn = testApplyFn
7032	ctx := testContext2(t, &ContextOpts{
7033		Config: m,
7034		Providers: map[addrs.Provider]providers.Factory{
7035			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7036		},
7037		Targets: []addrs.Targetable{
7038			addrs.RootModuleInstance.Resource(
7039				addrs.ManagedResourceMode, "aws_instance", "foo",
7040			),
7041		},
7042	})
7043
7044	if _, diags := ctx.Plan(); diags.HasErrors() {
7045		t.Fatalf("plan errors: %s", diags.Err())
7046	}
7047
7048	state, diags := ctx.Apply()
7049	if diags.HasErrors() {
7050		t.Fatalf("diags: %s", diags.Err())
7051	}
7052
7053	mod := state.RootModule()
7054	if len(mod.Resources) != 1 {
7055		t.Fatalf("expected 1 resource, got: %#v", mod.Resources)
7056	}
7057
7058	checkStateString(t, state, `
7059aws_instance.foo:
7060  ID = foo
7061  provider = provider["registry.terraform.io/hashicorp/aws"]
7062  num = 2
7063  type = aws_instance
7064	`)
7065}
7066
7067func TestContext2Apply_targetedCount(t *testing.T) {
7068	m := testModule(t, "apply-targeted-count")
7069	p := testProvider("aws")
7070	p.PlanResourceChangeFn = testDiffFn
7071	p.ApplyResourceChangeFn = testApplyFn
7072	ctx := testContext2(t, &ContextOpts{
7073		Config: m,
7074		Providers: map[addrs.Provider]providers.Factory{
7075			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7076		},
7077		Targets: []addrs.Targetable{
7078			addrs.RootModuleInstance.Resource(
7079				addrs.ManagedResourceMode, "aws_instance", "foo",
7080			),
7081		},
7082	})
7083
7084	if _, diags := ctx.Plan(); diags.HasErrors() {
7085		t.Fatalf("plan errors: %s", diags.Err())
7086	}
7087
7088	state, diags := ctx.Apply()
7089	if diags.HasErrors() {
7090		t.Fatalf("diags: %s", diags.Err())
7091	}
7092
7093	checkStateString(t, state, `
7094aws_instance.foo.0:
7095  ID = foo
7096  provider = provider["registry.terraform.io/hashicorp/aws"]
7097  type = aws_instance
7098aws_instance.foo.1:
7099  ID = foo
7100  provider = provider["registry.terraform.io/hashicorp/aws"]
7101  type = aws_instance
7102aws_instance.foo.2:
7103  ID = foo
7104  provider = provider["registry.terraform.io/hashicorp/aws"]
7105  type = aws_instance
7106	`)
7107}
7108
7109func TestContext2Apply_targetedCountIndex(t *testing.T) {
7110	m := testModule(t, "apply-targeted-count")
7111	p := testProvider("aws")
7112	p.PlanResourceChangeFn = testDiffFn
7113	p.ApplyResourceChangeFn = testApplyFn
7114	ctx := testContext2(t, &ContextOpts{
7115		Config: m,
7116		Providers: map[addrs.Provider]providers.Factory{
7117			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7118		},
7119		Targets: []addrs.Targetable{
7120			addrs.RootModuleInstance.ResourceInstance(
7121				addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(1),
7122			),
7123		},
7124	})
7125
7126	if _, diags := ctx.Plan(); diags.HasErrors() {
7127		t.Fatalf("plan errors: %s", diags.Err())
7128	}
7129
7130	state, diags := ctx.Apply()
7131	if diags.HasErrors() {
7132		t.Fatalf("diags: %s", diags.Err())
7133	}
7134
7135	checkStateString(t, state, `
7136aws_instance.foo.1:
7137  ID = foo
7138  provider = provider["registry.terraform.io/hashicorp/aws"]
7139  type = aws_instance
7140	`)
7141}
7142
7143func TestContext2Apply_targetedDestroy(t *testing.T) {
7144	m := testModule(t, "destroy-targeted")
7145	p := testProvider("aws")
7146	p.PlanResourceChangeFn = testDiffFn
7147
7148	state := states.NewState()
7149	root := state.EnsureModule(addrs.RootModuleInstance)
7150	root.SetResourceInstanceCurrent(
7151		mustResourceInstanceAddr("aws_instance.a").Resource,
7152		&states.ResourceInstanceObjectSrc{
7153			Status:    states.ObjectReady,
7154			AttrsJSON: []byte(`{"id":"bar"}`),
7155		},
7156		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7157	)
7158	root.SetOutputValue("out", cty.StringVal("bar"), false)
7159
7160	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
7161	child.SetResourceInstanceCurrent(
7162		mustResourceInstanceAddr("aws_instance.b").Resource,
7163		&states.ResourceInstanceObjectSrc{
7164			Status:    states.ObjectReady,
7165			AttrsJSON: []byte(`{"id":"i-bcd345"}`),
7166		},
7167		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7168	)
7169
7170	ctx := testContext2(t, &ContextOpts{
7171		Config: m,
7172		Providers: map[addrs.Provider]providers.Factory{
7173			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7174		},
7175		State: state,
7176		Targets: []addrs.Targetable{
7177			addrs.RootModuleInstance.Resource(
7178				addrs.ManagedResourceMode, "aws_instance", "a",
7179			),
7180		},
7181		PlanMode: plans.DestroyMode,
7182	})
7183
7184	if diags := ctx.Validate(); diags.HasErrors() {
7185		t.Fatalf("validate errors: %s", diags.Err())
7186	}
7187
7188	if _, diags := ctx.Plan(); diags.HasErrors() {
7189		t.Fatalf("plan errors: %s", diags.Err())
7190	}
7191
7192	state, diags := ctx.Apply()
7193	if diags.HasErrors() {
7194		t.Fatalf("diags: %s", diags.Err())
7195	}
7196
7197	mod := state.RootModule()
7198	if len(mod.Resources) != 0 {
7199		t.Fatalf("expected 0 resources, got: %#v", mod.Resources)
7200	}
7201
7202	// the root output should not get removed; only the targeted resource.
7203	//
7204	// Note: earlier versions of this test expected 0 outputs, but it turns out
7205	// that was because Validate - not apply or destroy - removed the output
7206	// (which depends on the targeted resource) from state. That version of this
7207	// test did not match actual terraform behavior: the output remains in
7208	// state.
7209	//
7210	// TODO: Future refactoring may enable us to remove the output from state in
7211	// this case, and that would be Just Fine - this test can be modified to
7212	// expect 0 outputs.
7213	if len(mod.OutputValues) != 1 {
7214		t.Fatalf("expected 1 outputs, got: %#v", mod.OutputValues)
7215	}
7216
7217	// the module instance should remain
7218	mod = state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey))
7219	if len(mod.Resources) != 1 {
7220		t.Fatalf("expected 1 resources, got: %#v", mod.Resources)
7221	}
7222}
7223
7224func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) {
7225	m := testModule(t, "apply-destroy-targeted-count")
7226	p := testProvider("aws")
7227	p.PlanResourceChangeFn = testDiffFn
7228
7229	state := states.NewState()
7230	root := state.EnsureModule(addrs.RootModuleInstance)
7231	root.SetResourceInstanceCurrent(
7232		mustResourceInstanceAddr("aws_instance.foo").Resource,
7233		&states.ResourceInstanceObjectSrc{
7234			Status:    states.ObjectReady,
7235			AttrsJSON: []byte(`{"id":"i-bcd345"}`),
7236		},
7237		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7238	)
7239	root.SetResourceInstanceCurrent(
7240		mustResourceInstanceAddr("aws_instance.bar").Resource,
7241		&states.ResourceInstanceObjectSrc{
7242			Status:       states.ObjectReady,
7243			AttrsJSON:    []byte(`{"id":"i-abc123"}`),
7244			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("aws_instance.foo")},
7245		},
7246		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7247	)
7248
7249	ctx := testContext2(t, &ContextOpts{
7250		Config: m,
7251		Providers: map[addrs.Provider]providers.Factory{
7252			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7253		},
7254		State: state,
7255		Targets: []addrs.Targetable{
7256			addrs.RootModuleInstance.Resource(
7257				addrs.ManagedResourceMode, "aws_instance", "foo",
7258			),
7259		},
7260		PlanMode: plans.DestroyMode,
7261	})
7262
7263	if _, diags := ctx.Plan(); diags.HasErrors() {
7264		t.Fatalf("plan errors: %s", diags.Err())
7265	}
7266
7267	state, diags := ctx.Apply()
7268	if diags.HasErrors() {
7269		t.Fatalf("diags: %s", diags.Err())
7270	}
7271
7272	checkStateString(t, state, `<no state>`)
7273}
7274
7275// https://github.com/hashicorp/terraform/issues/4462
7276func TestContext2Apply_targetedDestroyModule(t *testing.T) {
7277	m := testModule(t, "apply-targeted-module")
7278	p := testProvider("aws")
7279	p.PlanResourceChangeFn = testDiffFn
7280
7281	state := states.NewState()
7282	root := state.EnsureModule(addrs.RootModuleInstance)
7283	root.SetResourceInstanceCurrent(
7284		mustResourceInstanceAddr("aws_instance.foo").Resource,
7285		&states.ResourceInstanceObjectSrc{
7286			Status:    states.ObjectReady,
7287			AttrsJSON: []byte(`{"id":"i-bcd345"}`),
7288		},
7289		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7290	)
7291	root.SetResourceInstanceCurrent(
7292		mustResourceInstanceAddr("aws_instance.bar").Resource,
7293		&states.ResourceInstanceObjectSrc{
7294			Status:    states.ObjectReady,
7295			AttrsJSON: []byte(`{"id":"i-abc123"}`),
7296		},
7297		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7298	)
7299	child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
7300	child.SetResourceInstanceCurrent(
7301		mustResourceInstanceAddr("aws_instance.foo").Resource,
7302		&states.ResourceInstanceObjectSrc{
7303			Status:    states.ObjectReady,
7304			AttrsJSON: []byte(`{"id":"i-bcd345"}`),
7305		},
7306		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7307	)
7308	child.SetResourceInstanceCurrent(
7309		mustResourceInstanceAddr("aws_instance.bar").Resource,
7310		&states.ResourceInstanceObjectSrc{
7311			Status:    states.ObjectReady,
7312			AttrsJSON: []byte(`{"id":"i-abc123"}`),
7313		},
7314		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7315	)
7316
7317	ctx := testContext2(t, &ContextOpts{
7318		Config: m,
7319		Providers: map[addrs.Provider]providers.Factory{
7320			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7321		},
7322		State: state,
7323		Targets: []addrs.Targetable{
7324			addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource(
7325				addrs.ManagedResourceMode, "aws_instance", "foo",
7326			),
7327		},
7328		PlanMode: plans.DestroyMode,
7329	})
7330
7331	if _, diags := ctx.Plan(); diags.HasErrors() {
7332		t.Fatalf("plan errors: %s", diags.Err())
7333	}
7334
7335	state, diags := ctx.Apply()
7336	if diags.HasErrors() {
7337		t.Fatalf("diags: %s", diags.Err())
7338	}
7339
7340	checkStateString(t, state, `
7341aws_instance.bar:
7342  ID = i-abc123
7343  provider = provider["registry.terraform.io/hashicorp/aws"]
7344aws_instance.foo:
7345  ID = i-bcd345
7346  provider = provider["registry.terraform.io/hashicorp/aws"]
7347
7348module.child:
7349  aws_instance.bar:
7350    ID = i-abc123
7351    provider = provider["registry.terraform.io/hashicorp/aws"]
7352	`)
7353}
7354
7355func TestContext2Apply_targetedDestroyCountIndex(t *testing.T) {
7356	m := testModule(t, "apply-targeted-count")
7357	p := testProvider("aws")
7358	p.PlanResourceChangeFn = testDiffFn
7359
7360	foo := &states.ResourceInstanceObjectSrc{
7361		Status:    states.ObjectReady,
7362		AttrsJSON: []byte(`{"id":"i-bcd345"}`),
7363	}
7364	bar := &states.ResourceInstanceObjectSrc{
7365		Status:    states.ObjectReady,
7366		AttrsJSON: []byte(`{"id":"i-abc123"}`),
7367	}
7368
7369	state := states.NewState()
7370	root := state.EnsureModule(addrs.RootModuleInstance)
7371	root.SetResourceInstanceCurrent(
7372		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
7373		foo,
7374		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7375	)
7376	root.SetResourceInstanceCurrent(
7377		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
7378		foo,
7379		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7380	)
7381	root.SetResourceInstanceCurrent(
7382		mustResourceInstanceAddr("aws_instance.foo[2]").Resource,
7383		foo,
7384		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7385	)
7386	root.SetResourceInstanceCurrent(
7387		mustResourceInstanceAddr("aws_instance.bar[0]").Resource,
7388		bar,
7389		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7390	)
7391	root.SetResourceInstanceCurrent(
7392		mustResourceInstanceAddr("aws_instance.bar[1]").Resource,
7393		bar,
7394		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7395	)
7396	root.SetResourceInstanceCurrent(
7397		mustResourceInstanceAddr("aws_instance.bar[2]").Resource,
7398		bar,
7399		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7400	)
7401
7402	ctx := testContext2(t, &ContextOpts{
7403		Config: m,
7404		Providers: map[addrs.Provider]providers.Factory{
7405			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7406		},
7407		State: state,
7408		Targets: []addrs.Targetable{
7409			addrs.RootModuleInstance.ResourceInstance(
7410				addrs.ManagedResourceMode, "aws_instance", "foo", addrs.IntKey(2),
7411			),
7412			addrs.RootModuleInstance.ResourceInstance(
7413				addrs.ManagedResourceMode, "aws_instance", "bar", addrs.IntKey(1),
7414			),
7415		},
7416		PlanMode: plans.DestroyMode,
7417	})
7418
7419	if _, diags := ctx.Plan(); diags.HasErrors() {
7420		t.Fatalf("plan errors: %s", diags.Err())
7421	}
7422
7423	state, diags := ctx.Apply()
7424	if diags.HasErrors() {
7425		t.Fatalf("diags: %s", diags.Err())
7426	}
7427
7428	checkStateString(t, state, `
7429aws_instance.bar.0:
7430  ID = i-abc123
7431  provider = provider["registry.terraform.io/hashicorp/aws"]
7432aws_instance.bar.2:
7433  ID = i-abc123
7434  provider = provider["registry.terraform.io/hashicorp/aws"]
7435aws_instance.foo.0:
7436  ID = i-bcd345
7437  provider = provider["registry.terraform.io/hashicorp/aws"]
7438aws_instance.foo.1:
7439  ID = i-bcd345
7440  provider = provider["registry.terraform.io/hashicorp/aws"]
7441	`)
7442}
7443
7444func TestContext2Apply_targetedModule(t *testing.T) {
7445	m := testModule(t, "apply-targeted-module")
7446	p := testProvider("aws")
7447	p.PlanResourceChangeFn = testDiffFn
7448	p.ApplyResourceChangeFn = testApplyFn
7449	ctx := testContext2(t, &ContextOpts{
7450		Config: m,
7451		Providers: map[addrs.Provider]providers.Factory{
7452			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7453		},
7454		Targets: []addrs.Targetable{
7455			addrs.RootModuleInstance.Child("child", addrs.NoKey),
7456		},
7457	})
7458
7459	if _, diags := ctx.Plan(); diags.HasErrors() {
7460		t.Fatalf("plan errors: %s", diags.Err())
7461	}
7462
7463	state, diags := ctx.Apply()
7464	if diags.HasErrors() {
7465		t.Fatalf("diags: %s", diags.Err())
7466	}
7467
7468	mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey))
7469	if mod == nil {
7470		t.Fatalf("no child module found in the state!\n\n%#v", state)
7471	}
7472	if len(mod.Resources) != 2 {
7473		t.Fatalf("expected 2 resources, got: %#v", mod.Resources)
7474	}
7475
7476	checkStateString(t, state, `
7477<no state>
7478module.child:
7479  aws_instance.bar:
7480    ID = foo
7481    provider = provider["registry.terraform.io/hashicorp/aws"]
7482    num = 2
7483    type = aws_instance
7484  aws_instance.foo:
7485    ID = foo
7486    provider = provider["registry.terraform.io/hashicorp/aws"]
7487    num = 2
7488    type = aws_instance
7489	`)
7490}
7491
7492// GH-1858
7493func TestContext2Apply_targetedModuleDep(t *testing.T) {
7494	m := testModule(t, "apply-targeted-module-dep")
7495	p := testProvider("aws")
7496	p.PlanResourceChangeFn = testDiffFn
7497	p.ApplyResourceChangeFn = testApplyFn
7498	ctx := testContext2(t, &ContextOpts{
7499		Config: m,
7500		Providers: map[addrs.Provider]providers.Factory{
7501			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7502		},
7503		Targets: []addrs.Targetable{
7504			addrs.RootModuleInstance.Resource(
7505				addrs.ManagedResourceMode, "aws_instance", "foo",
7506			),
7507		},
7508	})
7509
7510	if p, diags := ctx.Plan(); diags.HasErrors() {
7511		t.Fatalf("diags: %s", diags.Err())
7512	} else {
7513		t.Logf("Diff: %s", legacyDiffComparisonString(p.Changes))
7514	}
7515
7516	state, diags := ctx.Apply()
7517	if diags.HasErrors() {
7518		t.Fatalf("diags: %s", diags.Err())
7519	}
7520
7521	checkStateString(t, state, `
7522aws_instance.foo:
7523  ID = foo
7524  provider = provider["registry.terraform.io/hashicorp/aws"]
7525  foo = foo
7526  type = aws_instance
7527
7528  Dependencies:
7529    module.child.aws_instance.mod
7530
7531module.child:
7532  aws_instance.mod:
7533    ID = foo
7534    provider = provider["registry.terraform.io/hashicorp/aws"]
7535    type = aws_instance
7536
7537  Outputs:
7538
7539  output = foo
7540	`)
7541}
7542
7543// GH-10911 untargeted outputs should not be in the graph, and therefore
7544// not execute.
7545func TestContext2Apply_targetedModuleUnrelatedOutputs(t *testing.T) {
7546	m := testModule(t, "apply-targeted-module-unrelated-outputs")
7547	p := testProvider("aws")
7548	p.PlanResourceChangeFn = testDiffFn
7549	p.ApplyResourceChangeFn = testApplyFn
7550
7551	state := states.NewState()
7552	_ = state.EnsureModule(addrs.RootModuleInstance.Child("child2", addrs.NoKey))
7553
7554	ctx := testContext2(t, &ContextOpts{
7555		Config: m,
7556		Providers: map[addrs.Provider]providers.Factory{
7557			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7558		},
7559		Targets: []addrs.Targetable{
7560			addrs.RootModuleInstance.Child("child2", addrs.NoKey),
7561		},
7562		State: state,
7563	})
7564
7565	if _, diags := ctx.Plan(); diags.HasErrors() {
7566		t.Fatalf("plan errors: %s", diags.Err())
7567	}
7568
7569	s, diags := ctx.Apply()
7570	if diags.HasErrors() {
7571		t.Fatalf("diags: %s", diags.Err())
7572	}
7573
7574	// - module.child1's instance_id output is dropped because we don't preserve
7575	//   non-root module outputs between runs (they can be recalculated from config)
7576	// - module.child2's instance_id is updated because its dependency is updated
7577	// - child2_id is updated because if its transitive dependency via module.child2
7578	checkStateString(t, s, `
7579<no state>
7580Outputs:
7581
7582child2_id = foo
7583
7584module.child2:
7585  aws_instance.foo:
7586    ID = foo
7587    provider = provider["registry.terraform.io/hashicorp/aws"]
7588    type = aws_instance
7589
7590  Outputs:
7591
7592  instance_id = foo
7593`)
7594}
7595
7596func TestContext2Apply_targetedModuleResource(t *testing.T) {
7597	m := testModule(t, "apply-targeted-module-resource")
7598	p := testProvider("aws")
7599	p.PlanResourceChangeFn = testDiffFn
7600	p.ApplyResourceChangeFn = testApplyFn
7601	ctx := testContext2(t, &ContextOpts{
7602		Config: m,
7603		Providers: map[addrs.Provider]providers.Factory{
7604			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7605		},
7606		Targets: []addrs.Targetable{
7607			addrs.RootModuleInstance.Child("child", addrs.NoKey).Resource(
7608				addrs.ManagedResourceMode, "aws_instance", "foo",
7609			),
7610		},
7611	})
7612
7613	if _, diags := ctx.Plan(); diags.HasErrors() {
7614		t.Fatalf("plan errors: %s", diags.Err())
7615	}
7616
7617	state, diags := ctx.Apply()
7618	if diags.HasErrors() {
7619		t.Fatalf("diags: %s", diags.Err())
7620	}
7621
7622	mod := state.Module(addrs.RootModuleInstance.Child("child", addrs.NoKey))
7623	if mod == nil || len(mod.Resources) != 1 {
7624		t.Fatalf("expected 1 resource, got: %#v", mod)
7625	}
7626
7627	checkStateString(t, state, `
7628<no state>
7629module.child:
7630  aws_instance.foo:
7631    ID = foo
7632    provider = provider["registry.terraform.io/hashicorp/aws"]
7633    num = 2
7634    type = aws_instance
7635	`)
7636}
7637
7638func TestContext2Apply_targetedResourceOrphanModule(t *testing.T) {
7639	m := testModule(t, "apply-targeted-resource-orphan-module")
7640	p := testProvider("aws")
7641	p.PlanResourceChangeFn = testDiffFn
7642
7643	state := states.NewState()
7644	child := state.EnsureModule(addrs.RootModuleInstance.Child("parent", addrs.NoKey))
7645	child.SetResourceInstanceCurrent(
7646		mustResourceInstanceAddr("aws_instance.bar").Resource,
7647		&states.ResourceInstanceObjectSrc{
7648			Status:    states.ObjectReady,
7649			AttrsJSON: []byte(`{"type":"aws_instance"}`),
7650		},
7651		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
7652	)
7653
7654	ctx := testContext2(t, &ContextOpts{
7655		Config: m,
7656		Providers: map[addrs.Provider]providers.Factory{
7657			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7658		},
7659		State: state,
7660		Targets: []addrs.Targetable{
7661			addrs.RootModuleInstance.Resource(
7662				addrs.ManagedResourceMode, "aws_instance", "foo",
7663			),
7664		},
7665	})
7666
7667	if _, diags := ctx.Plan(); diags.HasErrors() {
7668		t.Fatalf("plan errors: %s", diags.Err())
7669	}
7670
7671	if _, diags := ctx.Apply(); diags.HasErrors() {
7672		t.Fatalf("apply errors: %s", diags.Err())
7673	}
7674}
7675
7676func TestContext2Apply_unknownAttribute(t *testing.T) {
7677	m := testModule(t, "apply-unknown")
7678	p := testProvider("aws")
7679	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
7680		resp = testDiffFn(req)
7681		planned := resp.PlannedState.AsValueMap()
7682		planned["unknown"] = cty.UnknownVal(cty.String)
7683		resp.PlannedState = cty.ObjectVal(planned)
7684		return resp
7685	}
7686	p.ApplyResourceChangeFn = testApplyFn
7687
7688	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
7689		ResourceTypes: map[string]*configschema.Block{
7690			"aws_instance": {
7691				Attributes: map[string]*configschema.Attribute{
7692					"id":      {Type: cty.String, Computed: true},
7693					"num":     {Type: cty.Number, Optional: true},
7694					"unknown": {Type: cty.String, Computed: true},
7695					"type":    {Type: cty.String, Computed: true},
7696				},
7697			},
7698		},
7699	})
7700
7701	ctx := testContext2(t, &ContextOpts{
7702		Config: m,
7703		Providers: map[addrs.Provider]providers.Factory{
7704			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7705		},
7706	})
7707
7708	if _, diags := ctx.Plan(); diags.HasErrors() {
7709		t.Fatalf("plan errors: %s", diags.Err())
7710	}
7711
7712	state, diags := ctx.Apply()
7713	if !diags.HasErrors() {
7714		t.Error("should error, because attribute 'unknown' is still unknown after apply")
7715	}
7716
7717	actual := strings.TrimSpace(state.String())
7718	expected := strings.TrimSpace(testTerraformApplyUnknownAttrStr)
7719	if actual != expected {
7720		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
7721	}
7722}
7723
7724func TestContext2Apply_unknownAttributeInterpolate(t *testing.T) {
7725	m := testModule(t, "apply-unknown-interpolate")
7726	p := testProvider("aws")
7727	p.PlanResourceChangeFn = testDiffFn
7728	ctx := testContext2(t, &ContextOpts{
7729		Config: m,
7730		Providers: map[addrs.Provider]providers.Factory{
7731			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7732		},
7733	})
7734
7735	if _, diags := ctx.Plan(); diags == nil {
7736		t.Fatal("should error")
7737	}
7738}
7739
7740func TestContext2Apply_vars(t *testing.T) {
7741	fixture := contextFixtureApplyVars(t)
7742	opts := fixture.ContextOpts()
7743	opts.Variables = InputValues{
7744		"foo": &InputValue{
7745			Value:      cty.StringVal("us-east-1"),
7746			SourceType: ValueFromCaller,
7747		},
7748		"test_list": &InputValue{
7749			Value: cty.ListVal([]cty.Value{
7750				cty.StringVal("Hello"),
7751				cty.StringVal("World"),
7752			}),
7753			SourceType: ValueFromCaller,
7754		},
7755		"test_map": &InputValue{
7756			Value: cty.MapVal(map[string]cty.Value{
7757				"Hello": cty.StringVal("World"),
7758				"Foo":   cty.StringVal("Bar"),
7759				"Baz":   cty.StringVal("Foo"),
7760			}),
7761			SourceType: ValueFromCaller,
7762		},
7763		"amis": &InputValue{
7764			Value: cty.MapVal(map[string]cty.Value{
7765				"us-east-1": cty.StringVal("override"),
7766			}),
7767			SourceType: ValueFromCaller,
7768		},
7769	}
7770	ctx := testContext2(t, opts)
7771
7772	diags := ctx.Validate()
7773	if len(diags) != 0 {
7774		t.Fatalf("bad: %s", diags.ErrWithWarnings())
7775	}
7776
7777	if _, diags := ctx.Plan(); diags.HasErrors() {
7778		t.Fatalf("err: %s", diags.Err())
7779	}
7780
7781	state, diags := ctx.Apply()
7782	if diags.HasErrors() {
7783		t.Fatalf("err: %s", diags.Err())
7784	}
7785
7786	got := strings.TrimSpace(state.String())
7787	want := strings.TrimSpace(testTerraformApplyVarsStr)
7788	if got != want {
7789		t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", got, want)
7790	}
7791}
7792
7793func TestContext2Apply_varsEnv(t *testing.T) {
7794	fixture := contextFixtureApplyVarsEnv(t)
7795	opts := fixture.ContextOpts()
7796	opts.Variables = InputValues{
7797		"string": &InputValue{
7798			Value:      cty.StringVal("baz"),
7799			SourceType: ValueFromEnvVar,
7800		},
7801		"list": &InputValue{
7802			Value: cty.ListVal([]cty.Value{
7803				cty.StringVal("Hello"),
7804				cty.StringVal("World"),
7805			}),
7806			SourceType: ValueFromEnvVar,
7807		},
7808		"map": &InputValue{
7809			Value: cty.MapVal(map[string]cty.Value{
7810				"Hello": cty.StringVal("World"),
7811				"Foo":   cty.StringVal("Bar"),
7812				"Baz":   cty.StringVal("Foo"),
7813			}),
7814			SourceType: ValueFromEnvVar,
7815		},
7816	}
7817	ctx := testContext2(t, opts)
7818
7819	diags := ctx.Validate()
7820	if len(diags) != 0 {
7821		t.Fatalf("bad: %s", diags.ErrWithWarnings())
7822	}
7823
7824	if _, diags := ctx.Plan(); diags.HasErrors() {
7825		t.Fatalf("err: %s", diags.Err())
7826	}
7827
7828	state, diags := ctx.Apply()
7829	if diags.HasErrors() {
7830		t.Fatalf("err: %s", diags.Err())
7831	}
7832
7833	actual := strings.TrimSpace(state.String())
7834	expected := strings.TrimSpace(testTerraformApplyVarsEnvStr)
7835	if actual != expected {
7836		t.Errorf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
7837	}
7838}
7839
7840func TestContext2Apply_createBefore_depends(t *testing.T) {
7841	m := testModule(t, "apply-depends-create-before")
7842	h := new(HookRecordApplyOrder)
7843	p := testProvider("aws")
7844	p.PlanResourceChangeFn = testDiffFn
7845	p.ApplyResourceChangeFn = testApplyFn
7846	state := states.NewState()
7847	root := state.EnsureModule(addrs.RootModuleInstance)
7848	root.SetResourceInstanceCurrent(
7849		addrs.Resource{
7850			Mode: addrs.ManagedResourceMode,
7851			Type: "aws_instance",
7852			Name: "web",
7853		}.Instance(addrs.NoKey),
7854		&states.ResourceInstanceObjectSrc{
7855			Status:    states.ObjectReady,
7856			AttrsJSON: []byte(`{"id":"bar","require_new":"ami-old"}`),
7857		},
7858		addrs.AbsProviderConfig{
7859			Provider: addrs.NewDefaultProvider("aws"),
7860			Module:   addrs.RootModule,
7861		},
7862	)
7863
7864	root.SetResourceInstanceCurrent(
7865		addrs.Resource{
7866			Mode: addrs.ManagedResourceMode,
7867			Type: "aws_instance",
7868			Name: "lb",
7869		}.Instance(addrs.NoKey),
7870		&states.ResourceInstanceObjectSrc{
7871			Status:    states.ObjectReady,
7872			AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`),
7873			Dependencies: []addrs.ConfigResource{
7874				addrs.ConfigResource{
7875					Resource: addrs.Resource{
7876						Mode: addrs.ManagedResourceMode,
7877						Type: "aws_instance",
7878						Name: "web",
7879					},
7880					Module: addrs.RootModule,
7881				},
7882			},
7883		},
7884		addrs.AbsProviderConfig{
7885			Provider: addrs.NewDefaultProvider("aws"),
7886			Module:   addrs.RootModule,
7887		},
7888	)
7889
7890	ctx := testContext2(t, &ContextOpts{
7891		Config: m,
7892		Hooks:  []Hook{h},
7893		Providers: map[addrs.Provider]providers.Factory{
7894			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
7895		},
7896		State: state,
7897	})
7898
7899	if p, diags := ctx.Plan(); diags.HasErrors() {
7900		logDiagnostics(t, diags)
7901		t.Fatal("plan failed")
7902	} else {
7903		t.Logf("plan:\n%s", legacyDiffComparisonString(p.Changes))
7904	}
7905
7906	h.Active = true
7907	state, diags := ctx.Apply()
7908	if diags.HasErrors() {
7909		logDiagnostics(t, diags)
7910		t.Fatal("apply failed")
7911	}
7912
7913	mod := state.RootModule()
7914	if len(mod.Resources) < 2 {
7915		t.Logf("state after apply:\n%s", state.String())
7916		t.Fatalf("only %d resources in root module; want at least 2", len(mod.Resources))
7917	}
7918
7919	got := strings.TrimSpace(state.String())
7920	want := strings.TrimSpace(testTerraformApplyDependsCreateBeforeStr)
7921	if got != want {
7922		t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", got, want)
7923	}
7924
7925	// Test that things were managed _in the right order_
7926	order := h.States
7927
7928	diffs := h.Diffs
7929	if !order[0].IsNull() || diffs[0].Action == plans.Delete {
7930		t.Fatalf("should create new instance first: %#v", order)
7931	}
7932
7933	if order[1].GetAttr("id").AsString() != "baz" {
7934		t.Fatalf("update must happen after create: %#v", order[1])
7935	}
7936
7937	if order[2].GetAttr("id").AsString() != "bar" || diffs[2].Action != plans.Delete {
7938		t.Fatalf("destroy must happen after update: %#v", order[2])
7939	}
7940}
7941
7942func TestContext2Apply_singleDestroy(t *testing.T) {
7943	m := testModule(t, "apply-depends-create-before")
7944	h := new(HookRecordApplyOrder)
7945	p := testProvider("aws")
7946	invokeCount := 0
7947	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
7948		invokeCount++
7949		switch invokeCount {
7950		case 1:
7951			if req.PlannedState.IsNull() {
7952				t.Fatalf("should not destroy")
7953			}
7954			if id := req.PlannedState.GetAttr("id"); id.IsKnown() {
7955				t.Fatalf("should not have ID")
7956			}
7957		case 2:
7958			if req.PlannedState.IsNull() {
7959				t.Fatalf("should not destroy")
7960			}
7961			if id := req.PlannedState.GetAttr("id"); id.AsString() != "baz" {
7962				t.Fatalf("should have id")
7963			}
7964		case 3:
7965			if !req.PlannedState.IsNull() {
7966				t.Fatalf("should destroy")
7967			}
7968		default:
7969			t.Fatalf("bad invoke count %d", invokeCount)
7970		}
7971		return testApplyFn(req)
7972	}
7973
7974	p.PlanResourceChangeFn = testDiffFn
7975	state := states.NewState()
7976	root := state.EnsureModule(addrs.RootModuleInstance)
7977	root.SetResourceInstanceCurrent(
7978		addrs.Resource{
7979			Mode: addrs.ManagedResourceMode,
7980			Type: "aws_instance",
7981			Name: "web",
7982		}.Instance(addrs.NoKey),
7983		&states.ResourceInstanceObjectSrc{
7984			Status:    states.ObjectReady,
7985			AttrsJSON: []byte(`{"id":"bar","require_new":"ami-old"}`),
7986		},
7987		addrs.AbsProviderConfig{
7988			Provider: addrs.NewDefaultProvider("aws"),
7989			Module:   addrs.RootModule,
7990		},
7991	)
7992
7993	root.SetResourceInstanceCurrent(
7994		addrs.Resource{
7995			Mode: addrs.ManagedResourceMode,
7996			Type: "aws_instance",
7997			Name: "lb",
7998		}.Instance(addrs.NoKey),
7999		&states.ResourceInstanceObjectSrc{
8000			Status:    states.ObjectReady,
8001			AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`),
8002			Dependencies: []addrs.ConfigResource{
8003				addrs.ConfigResource{
8004					Resource: addrs.Resource{
8005						Mode: addrs.ManagedResourceMode,
8006						Type: "aws_instance",
8007						Name: "web",
8008					},
8009					Module: addrs.RootModule,
8010				},
8011			},
8012		},
8013		addrs.AbsProviderConfig{
8014			Provider: addrs.NewDefaultProvider("aws"),
8015			Module:   addrs.RootModule,
8016		},
8017	)
8018
8019	ctx := testContext2(t, &ContextOpts{
8020		Config: m,
8021		Hooks:  []Hook{h},
8022		Providers: map[addrs.Provider]providers.Factory{
8023			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8024		},
8025		State: state,
8026	})
8027
8028	if _, diags := ctx.Plan(); diags.HasErrors() {
8029		t.Fatalf("plan errors: %s", diags.Err())
8030	}
8031
8032	h.Active = true
8033	_, diags := ctx.Apply()
8034	if diags.HasErrors() {
8035		t.Fatalf("diags: %s", diags.Err())
8036	}
8037
8038	if invokeCount != 3 {
8039		t.Fatalf("bad: %d", invokeCount)
8040	}
8041}
8042
8043// GH-7824
8044func TestContext2Apply_issue7824(t *testing.T) {
8045	p := testProvider("template")
8046	p.PlanResourceChangeFn = testDiffFn
8047	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
8048		ResourceTypes: map[string]*configschema.Block{
8049			"template_file": {
8050				Attributes: map[string]*configschema.Attribute{
8051					"template":                {Type: cty.String, Optional: true},
8052					"__template_requires_new": {Type: cty.Bool, Optional: true},
8053				},
8054			},
8055		},
8056	})
8057
8058	m, snap := testModuleWithSnapshot(t, "issue-7824")
8059
8060	// Apply cleanly step 0
8061	ctx := testContext2(t, &ContextOpts{
8062		Config: m,
8063		Providers: map[addrs.Provider]providers.Factory{
8064			addrs.NewDefaultProvider("template"): testProviderFuncFixed(p),
8065		},
8066	})
8067
8068	plan, diags := ctx.Plan()
8069	if diags.HasErrors() {
8070		t.Fatalf("err: %s", diags.Err())
8071	}
8072
8073	// Write / Read plan to simulate running it through a Plan file
8074	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
8075	if err != nil {
8076		t.Fatalf("failed to round-trip through planfile: %s", err)
8077	}
8078
8079	ctxOpts.Providers =
8080		map[addrs.Provider]providers.Factory{
8081			addrs.NewDefaultProvider("template"): testProviderFuncFixed(p),
8082		}
8083
8084	ctx, diags = NewContext(ctxOpts)
8085	if diags.HasErrors() {
8086		t.Fatalf("err: %s", diags.Err())
8087	}
8088
8089	_, diags = ctx.Apply()
8090	if diags.HasErrors() {
8091		t.Fatalf("err: %s", diags.Err())
8092	}
8093}
8094
8095// This deals with the situation where a splat expression is used referring
8096// to another resource whose count is non-constant.
8097func TestContext2Apply_issue5254(t *testing.T) {
8098	// Create a provider. We use "template" here just to match the repro
8099	// we got from the issue itself.
8100	p := testProvider("template")
8101	p.PlanResourceChangeFn = testDiffFn
8102	p.ApplyResourceChangeFn = testApplyFn
8103	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
8104		ResourceTypes: map[string]*configschema.Block{
8105			"template_file": {
8106				Attributes: map[string]*configschema.Attribute{
8107					"template":                {Type: cty.String, Optional: true},
8108					"__template_requires_new": {Type: cty.Bool, Optional: true},
8109					"id":                      {Type: cty.String, Computed: true},
8110					"type":                    {Type: cty.String, Computed: true},
8111				},
8112			},
8113		},
8114	})
8115
8116	// Apply cleanly step 0
8117	ctx := testContext2(t, &ContextOpts{
8118		Config: testModule(t, "issue-5254/step-0"),
8119		Providers: map[addrs.Provider]providers.Factory{
8120			addrs.NewDefaultProvider("template"): testProviderFuncFixed(p),
8121		},
8122	})
8123
8124	_, diags := ctx.Plan()
8125	if diags.HasErrors() {
8126		t.Fatalf("err: %s", diags.Err())
8127	}
8128
8129	state, diags := ctx.Apply()
8130	if diags.HasErrors() {
8131		t.Fatalf("err: %s", diags.Err())
8132	}
8133
8134	m, snap := testModuleWithSnapshot(t, "issue-5254/step-1")
8135
8136	// Application success. Now make the modification and store a plan
8137	ctx = testContext2(t, &ContextOpts{
8138		Config: m,
8139		State:  state,
8140		Providers: map[addrs.Provider]providers.Factory{
8141			addrs.NewDefaultProvider("template"): testProviderFuncFixed(p),
8142		},
8143	})
8144
8145	plan, diags := ctx.Plan()
8146	if diags.HasErrors() {
8147		t.Fatalf("err: %s", diags.Err())
8148	}
8149
8150	// Write / Read plan to simulate running it through a Plan file
8151	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
8152	if err != nil {
8153		t.Fatalf("failed to round-trip through planfile: %s", err)
8154	}
8155
8156	ctxOpts.Providers = map[addrs.Provider]providers.Factory{
8157		addrs.NewDefaultProvider("template"): testProviderFuncFixed(p),
8158	}
8159
8160	ctx, diags = NewContext(ctxOpts)
8161	if diags.HasErrors() {
8162		t.Fatalf("err: %s", diags.Err())
8163	}
8164
8165	state, diags = ctx.Apply()
8166	if diags.HasErrors() {
8167		t.Fatalf("err: %s", diags.Err())
8168	}
8169
8170	actual := strings.TrimSpace(state.String())
8171	expected := strings.TrimSpace(`
8172template_file.child:
8173  ID = foo
8174  provider = provider["registry.terraform.io/hashicorp/template"]
8175  __template_requires_new = true
8176  template = Hi
8177  type = template_file
8178
8179  Dependencies:
8180    template_file.parent
8181template_file.parent.0:
8182  ID = foo
8183  provider = provider["registry.terraform.io/hashicorp/template"]
8184  template = Hi
8185  type = template_file
8186`)
8187	if actual != expected {
8188		t.Fatalf("wrong final state\ngot:\n%s\n\nwant:\n%s", actual, expected)
8189	}
8190}
8191
8192func TestContext2Apply_targetedWithTaintedInState(t *testing.T) {
8193	p := testProvider("aws")
8194	p.PlanResourceChangeFn = testDiffFn
8195	p.ApplyResourceChangeFn = testApplyFn
8196	m, snap := testModuleWithSnapshot(t, "apply-tainted-targets")
8197
8198	state := states.NewState()
8199	root := state.EnsureModule(addrs.RootModuleInstance)
8200	root.SetResourceInstanceCurrent(
8201		mustResourceInstanceAddr("aws_instance.ifailedprovisioners").Resource,
8202		&states.ResourceInstanceObjectSrc{
8203			Status:    states.ObjectTainted,
8204			AttrsJSON: []byte(`{"id":"ifailedprovisioners"}`),
8205		},
8206		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
8207	)
8208
8209	ctx := testContext2(t, &ContextOpts{
8210		Config: m,
8211		Providers: map[addrs.Provider]providers.Factory{
8212			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8213		},
8214		Targets: []addrs.Targetable{
8215			addrs.RootModuleInstance.Resource(
8216				addrs.ManagedResourceMode, "aws_instance", "iambeingadded",
8217			),
8218		},
8219		State: state,
8220	})
8221
8222	plan, diags := ctx.Plan()
8223	if diags.HasErrors() {
8224		t.Fatalf("err: %s", diags.Err())
8225	}
8226
8227	// Write / Read plan to simulate running it through a Plan file
8228	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
8229	if err != nil {
8230		t.Fatalf("failed to round-trip through planfile: %s", err)
8231	}
8232
8233	ctxOpts.Providers = map[addrs.Provider]providers.Factory{
8234		addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8235	}
8236
8237	ctx, diags = NewContext(ctxOpts)
8238	if diags.HasErrors() {
8239		t.Fatalf("err: %s", diags.Err())
8240	}
8241
8242	s, diags := ctx.Apply()
8243	if diags.HasErrors() {
8244		t.Fatalf("err: %s", diags.Err())
8245	}
8246
8247	actual := strings.TrimSpace(s.String())
8248	expected := strings.TrimSpace(`
8249aws_instance.iambeingadded:
8250  ID = foo
8251  provider = provider["registry.terraform.io/hashicorp/aws"]
8252  type = aws_instance
8253aws_instance.ifailedprovisioners: (tainted)
8254  ID = ifailedprovisioners
8255  provider = provider["registry.terraform.io/hashicorp/aws"]
8256		`)
8257	if actual != expected {
8258		t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual)
8259	}
8260}
8261
8262// Higher level test exposing the bug this covers in
8263// TestResource_ignoreChangesRequired
8264func TestContext2Apply_ignoreChangesCreate(t *testing.T) {
8265	m := testModule(t, "apply-ignore-changes-create")
8266	p := testProvider("aws")
8267	p.PlanResourceChangeFn = testDiffFn
8268	p.ApplyResourceChangeFn = testApplyFn
8269
8270	instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
8271	instanceSchema.Attributes["required_field"] = &configschema.Attribute{
8272		Type:     cty.String,
8273		Required: true,
8274	}
8275
8276	ctx := testContext2(t, &ContextOpts{
8277		Config: m,
8278		Providers: map[addrs.Provider]providers.Factory{
8279			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8280		},
8281	})
8282
8283	if p, diags := ctx.Plan(); diags.HasErrors() {
8284		t.Fatalf("diags: %s", diags.Err())
8285	} else {
8286		t.Logf(legacyDiffComparisonString(p.Changes))
8287	}
8288
8289	state, diags := ctx.Apply()
8290	if diags.HasErrors() {
8291		t.Fatalf("diags: %s", diags.Err())
8292	}
8293
8294	mod := state.RootModule()
8295	if len(mod.Resources) != 1 {
8296		t.Fatalf("bad: %s", state)
8297	}
8298
8299	actual := strings.TrimSpace(state.String())
8300	// Expect no changes from original state
8301	expected := strings.TrimSpace(`
8302aws_instance.foo:
8303  ID = foo
8304  provider = provider["registry.terraform.io/hashicorp/aws"]
8305  required_field = set
8306  type = aws_instance
8307`)
8308	if actual != expected {
8309		t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual)
8310	}
8311}
8312
8313func TestContext2Apply_ignoreChangesWithDep(t *testing.T) {
8314	m := testModule(t, "apply-ignore-changes-dep")
8315	p := testProvider("aws")
8316
8317	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
8318		resp.PlannedState = req.ProposedNewState
8319
8320		switch req.TypeName {
8321		case "aws_instance":
8322			resp.RequiresReplace = append(resp.RequiresReplace, cty.Path{cty.GetAttrStep{Name: "ami"}})
8323		case "aws_eip":
8324			return testDiffFn(req)
8325		default:
8326			t.Fatalf("Unexpected type: %s", req.TypeName)
8327		}
8328		return
8329	}
8330
8331	state := states.NewState()
8332	root := state.EnsureModule(addrs.RootModuleInstance)
8333	root.SetResourceInstanceCurrent(
8334		mustResourceInstanceAddr("aws_instance.foo[0]").Resource,
8335		&states.ResourceInstanceObjectSrc{
8336			Status:    states.ObjectReady,
8337			AttrsJSON: []byte(`{"id":"i-abc123","ami":"ami-abcd1234"}`),
8338		},
8339		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
8340	)
8341	root.SetResourceInstanceCurrent(
8342		mustResourceInstanceAddr("aws_instance.foo[1]").Resource,
8343		&states.ResourceInstanceObjectSrc{
8344			Status:    states.ObjectReady,
8345			AttrsJSON: []byte(`{"id":"i-bcd234","ami":"i-bcd234"}`),
8346		},
8347		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
8348	)
8349	root.SetResourceInstanceCurrent(
8350		mustResourceInstanceAddr("aws_eip.foo[0]").Resource,
8351		&states.ResourceInstanceObjectSrc{
8352			Status:    states.ObjectReady,
8353			AttrsJSON: []byte(`{"id":"eip-abc123","instance":"i-abc123"}`),
8354			Dependencies: []addrs.ConfigResource{
8355				addrs.ConfigResource{
8356					Resource: addrs.Resource{
8357						Mode: addrs.ManagedResourceMode,
8358						Type: "aws_instance",
8359						Name: "foo",
8360					},
8361					Module: addrs.RootModule,
8362				},
8363			},
8364		},
8365		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
8366	)
8367	root.SetResourceInstanceCurrent(
8368		mustResourceInstanceAddr("aws_eip.foo[1]").Resource,
8369		&states.ResourceInstanceObjectSrc{
8370			Status:    states.ObjectReady,
8371			AttrsJSON: []byte(`{"id":"eip-bcd234","instance":"i-bcd234"}`),
8372			Dependencies: []addrs.ConfigResource{
8373				addrs.ConfigResource{
8374					Resource: addrs.Resource{
8375						Mode: addrs.ManagedResourceMode,
8376						Type: "aws_instance",
8377						Name: "foo",
8378					},
8379					Module: addrs.RootModule,
8380				},
8381			},
8382		},
8383		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
8384	)
8385
8386	ctx := testContext2(t, &ContextOpts{
8387		Config: m,
8388		Providers: map[addrs.Provider]providers.Factory{
8389			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8390		},
8391		State: state.DeepCopy(),
8392	})
8393
8394	_, diags := ctx.Plan()
8395	assertNoErrors(t, diags)
8396
8397	s, diags := ctx.Apply()
8398	assertNoErrors(t, diags)
8399
8400	actual := strings.TrimSpace(s.String())
8401	expected := strings.TrimSpace(state.String())
8402	if actual != expected {
8403		t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
8404	}
8405}
8406
8407func TestContext2Apply_ignoreChangesAll(t *testing.T) {
8408	m := testModule(t, "apply-ignore-changes-all")
8409	p := testProvider("aws")
8410	p.PlanResourceChangeFn = testDiffFn
8411	p.ApplyResourceChangeFn = testApplyFn
8412
8413	instanceSchema := p.GetProviderSchemaResponse.ResourceTypes["aws_instance"].Block
8414	instanceSchema.Attributes["required_field"] = &configschema.Attribute{
8415		Type:     cty.String,
8416		Required: true,
8417	}
8418
8419	ctx := testContext2(t, &ContextOpts{
8420		Config: m,
8421		Providers: map[addrs.Provider]providers.Factory{
8422			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8423		},
8424	})
8425
8426	if p, diags := ctx.Plan(); diags.HasErrors() {
8427		logDiagnostics(t, diags)
8428		t.Fatal("plan failed")
8429	} else {
8430		t.Logf(legacyDiffComparisonString(p.Changes))
8431	}
8432
8433	state, diags := ctx.Apply()
8434	assertNoErrors(t, diags)
8435
8436	mod := state.RootModule()
8437	if len(mod.Resources) != 1 {
8438		t.Fatalf("bad: %s", state)
8439	}
8440
8441	actual := strings.TrimSpace(state.String())
8442	// Expect no changes from original state
8443	expected := strings.TrimSpace(`
8444aws_instance.foo:
8445  ID = foo
8446  provider = provider["registry.terraform.io/hashicorp/aws"]
8447  required_field = set
8448  type = aws_instance
8449`)
8450	if actual != expected {
8451		t.Fatalf("expected:\n%s\ngot:\n%s", expected, actual)
8452	}
8453}
8454
8455// https://github.com/hashicorp/terraform/issues/7378
8456func TestContext2Apply_destroyNestedModuleWithAttrsReferencingResource(t *testing.T) {
8457	m, snap := testModuleWithSnapshot(t, "apply-destroy-nested-module-with-attrs")
8458	p := testProvider("null")
8459	p.PlanResourceChangeFn = testDiffFn
8460
8461	var state *states.State
8462	var diags tfdiags.Diagnostics
8463	{
8464		ctx := testContext2(t, &ContextOpts{
8465			Config: m,
8466			Providers: map[addrs.Provider]providers.Factory{
8467				addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
8468			},
8469		})
8470
8471		// First plan and apply a create operation
8472		if _, diags := ctx.Plan(); diags.HasErrors() {
8473			t.Fatalf("plan err: %s", diags.Err())
8474		}
8475
8476		state, diags = ctx.Apply()
8477		if diags.HasErrors() {
8478			t.Fatalf("apply err: %s", diags.Err())
8479		}
8480	}
8481
8482	{
8483		ctx := testContext2(t, &ContextOpts{
8484			PlanMode: plans.DestroyMode,
8485			Config:   m,
8486			State:    state,
8487			Providers: map[addrs.Provider]providers.Factory{
8488				addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
8489			},
8490		})
8491
8492		plan, diags := ctx.Plan()
8493		if diags.HasErrors() {
8494			t.Fatalf("destroy plan err: %s", diags.Err())
8495		}
8496
8497		ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
8498		if err != nil {
8499			t.Fatalf("failed to round-trip through planfile: %s", err)
8500		}
8501
8502		ctxOpts.Providers = map[addrs.Provider]providers.Factory{
8503			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
8504		}
8505
8506		ctx, diags = NewContext(ctxOpts)
8507		if diags.HasErrors() {
8508			t.Fatalf("err: %s", diags.Err())
8509		}
8510
8511		state, diags = ctx.Apply()
8512		if diags.HasErrors() {
8513			t.Fatalf("destroy apply err: %s", diags.Err())
8514		}
8515	}
8516
8517	if !state.Empty() {
8518		t.Fatalf("state after apply: %s\nwant empty state", spew.Sdump(state))
8519	}
8520}
8521
8522// If a data source explicitly depends on another resource, it's because we need
8523// that resource to be applied first.
8524func TestContext2Apply_dataDependsOn(t *testing.T) {
8525	p := testProvider("null")
8526	m := testModuleInline(t, map[string]string{
8527		"main.tf": `
8528resource "null_instance" "write" {
8529  foo = "attribute"
8530}
8531
8532data "null_data_source" "read" {
8533  count = 1
8534  depends_on = ["null_instance.write"]
8535}
8536
8537resource "null_instance" "depends" {
8538  foo = data.null_data_source.read[0].foo
8539}
8540`})
8541
8542	ctx := testContext2(t, &ContextOpts{
8543		Config: m,
8544		Providers: map[addrs.Provider]providers.Factory{
8545			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
8546		},
8547	})
8548
8549	// the "provisioner" here writes to this variable, because the intent is to
8550	// create a dependency which can't be viewed through the graph, and depends
8551	// solely on the configuration providing "depends_on"
8552	provisionerOutput := ""
8553
8554	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
8555		// the side effect of the resource being applied
8556		provisionerOutput = "APPLIED"
8557		return testApplyFn(req)
8558	}
8559
8560	p.PlanResourceChangeFn = testDiffFn
8561	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
8562		return providers.ReadDataSourceResponse{
8563			State: cty.ObjectVal(map[string]cty.Value{
8564				"id":  cty.StringVal("boop"),
8565				"foo": cty.StringVal(provisionerOutput),
8566			}),
8567		}
8568	}
8569
8570	_, diags := ctx.Plan()
8571	assertNoErrors(t, diags)
8572
8573	state, diags := ctx.Apply()
8574	assertNoErrors(t, diags)
8575
8576	root := state.Module(addrs.RootModuleInstance)
8577	is := root.ResourceInstance(addrs.Resource{
8578		Mode: addrs.DataResourceMode,
8579		Type: "null_data_source",
8580		Name: "read",
8581	}.Instance(addrs.IntKey(0)))
8582	if is == nil {
8583		t.Fatal("data resource instance is not present in state; should be")
8584	}
8585	var attrs map[string]interface{}
8586	err := json.Unmarshal(is.Current.AttrsJSON, &attrs)
8587	if err != nil {
8588		t.Fatal(err)
8589	}
8590	actual := attrs["foo"]
8591	expected := "APPLIED"
8592	if actual != expected {
8593		t.Fatalf("bad:\n%s", strings.TrimSpace(state.String()))
8594	}
8595
8596	// run another plan to make sure the data source doesn't show as a change
8597	plan, diags := ctx.Plan()
8598	assertNoErrors(t, diags)
8599
8600	for _, c := range plan.Changes.Resources {
8601		if c.Action != plans.NoOp {
8602			t.Fatalf("unexpected change for %s", c.Addr)
8603		}
8604	}
8605
8606	// now we cause a change in the first resource, which should trigger a plan
8607	// in the data source, and the resource that depends on the data source
8608	// must plan a change as well.
8609	m = testModuleInline(t, map[string]string{
8610		"main.tf": `
8611resource "null_instance" "write" {
8612  foo = "new"
8613}
8614
8615data "null_data_source" "read" {
8616  depends_on = ["null_instance.write"]
8617}
8618
8619resource "null_instance" "depends" {
8620  foo = data.null_data_source.read.foo
8621}
8622`})
8623
8624	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
8625		// the side effect of the resource being applied
8626		provisionerOutput = "APPLIED_AGAIN"
8627		return testApplyFn(req)
8628	}
8629
8630	ctx = testContext2(t, &ContextOpts{
8631		Config: m,
8632		State:  state,
8633		Providers: map[addrs.Provider]providers.Factory{
8634			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
8635		},
8636	})
8637
8638	plan, diags = ctx.Plan()
8639	assertNoErrors(t, diags)
8640
8641	expectedChanges := map[string]plans.Action{
8642		"null_instance.write":        plans.Update,
8643		"data.null_data_source.read": plans.Read,
8644		"null_instance.depends":      plans.Update,
8645	}
8646
8647	for _, c := range plan.Changes.Resources {
8648		if c.Action != expectedChanges[c.Addr.String()] {
8649			t.Errorf("unexpected %s for %s", c.Action, c.Addr)
8650		}
8651	}
8652}
8653
8654func TestContext2Apply_terraformWorkspace(t *testing.T) {
8655	m := testModule(t, "apply-terraform-workspace")
8656	p := testProvider("aws")
8657	p.PlanResourceChangeFn = testDiffFn
8658
8659	ctx := testContext2(t, &ContextOpts{
8660		Meta:   &ContextMeta{Env: "foo"},
8661		Config: m,
8662		Providers: map[addrs.Provider]providers.Factory{
8663			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8664		},
8665	})
8666
8667	if _, diags := ctx.Plan(); diags.HasErrors() {
8668		t.Fatalf("plan errors: %s", diags.Err())
8669	}
8670
8671	state, diags := ctx.Apply()
8672	if diags.HasErrors() {
8673		t.Fatalf("diags: %s", diags.Err())
8674	}
8675
8676	actual := state.RootModule().OutputValues["output"]
8677	expected := cty.StringVal("foo")
8678	if actual == nil || actual.Value != expected {
8679		t.Fatalf("wrong value\ngot:  %#v\nwant: %#v", actual.Value, expected)
8680	}
8681}
8682
8683// verify that multiple config references only create a single depends_on entry
8684func TestContext2Apply_multiRef(t *testing.T) {
8685	m := testModule(t, "apply-multi-ref")
8686	p := testProvider("aws")
8687	p.PlanResourceChangeFn = testDiffFn
8688	ctx := testContext2(t, &ContextOpts{
8689		Config: m,
8690		Providers: map[addrs.Provider]providers.Factory{
8691			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8692		},
8693	})
8694
8695	if _, diags := ctx.Plan(); diags.HasErrors() {
8696		t.Fatalf("err: %s", diags.Err())
8697	}
8698
8699	state, diags := ctx.Apply()
8700	if diags.HasErrors() {
8701		t.Fatalf("err: %s", diags.Err())
8702	}
8703
8704	deps := state.Modules[""].Resources["aws_instance.other"].Instances[addrs.NoKey].Current.Dependencies
8705	if len(deps) != 1 || deps[0].String() != "aws_instance.create" {
8706		t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps)
8707	}
8708}
8709
8710func TestContext2Apply_targetedModuleRecursive(t *testing.T) {
8711	m := testModule(t, "apply-targeted-module-recursive")
8712	p := testProvider("aws")
8713	p.PlanResourceChangeFn = testDiffFn
8714	p.ApplyResourceChangeFn = testApplyFn
8715	ctx := testContext2(t, &ContextOpts{
8716		Config: m,
8717		Providers: map[addrs.Provider]providers.Factory{
8718			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8719		},
8720		Targets: []addrs.Targetable{
8721			addrs.RootModuleInstance.Child("child", addrs.NoKey),
8722		},
8723	})
8724
8725	if _, diags := ctx.Plan(); diags.HasErrors() {
8726		t.Fatalf("err: %s", diags.Err())
8727	}
8728
8729	state, diags := ctx.Apply()
8730	if diags.HasErrors() {
8731		t.Fatalf("err: %s", diags.Err())
8732	}
8733
8734	mod := state.Module(
8735		addrs.RootModuleInstance.Child("child", addrs.NoKey).Child("subchild", addrs.NoKey),
8736	)
8737	if mod == nil {
8738		t.Fatalf("no subchild module found in the state!\n\n%#v", state)
8739	}
8740	if len(mod.Resources) != 1 {
8741		t.Fatalf("expected 1 resources, got: %#v", mod.Resources)
8742	}
8743
8744	checkStateString(t, state, `
8745<no state>
8746module.child.subchild:
8747  aws_instance.foo:
8748    ID = foo
8749    provider = provider["registry.terraform.io/hashicorp/aws"]
8750    num = 2
8751    type = aws_instance
8752	`)
8753}
8754
8755func TestContext2Apply_localVal(t *testing.T) {
8756	m := testModule(t, "apply-local-val")
8757	ctx := testContext2(t, &ContextOpts{
8758		Config:    m,
8759		Providers: map[addrs.Provider]providers.Factory{},
8760	})
8761
8762	if _, diags := ctx.Plan(); diags.HasErrors() {
8763		t.Fatalf("error during plan: %s", diags.Err())
8764	}
8765
8766	state, diags := ctx.Apply()
8767	if diags.HasErrors() {
8768		t.Fatalf("error during apply: %s", diags.Err())
8769	}
8770
8771	got := strings.TrimSpace(state.String())
8772	want := strings.TrimSpace(`
8773<no state>
8774Outputs:
8775
8776result_1 = hello
8777result_3 = hello world
8778`)
8779	if got != want {
8780		t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
8781	}
8782}
8783
8784func TestContext2Apply_destroyWithLocals(t *testing.T) {
8785	m := testModule(t, "apply-destroy-with-locals")
8786	p := testProvider("aws")
8787	p.PlanResourceChangeFn = testDiffFn
8788
8789	state := states.NewState()
8790	root := state.EnsureModule(addrs.RootModuleInstance)
8791	root.SetResourceInstanceCurrent(
8792		mustResourceInstanceAddr("aws_instance.foo").Resource,
8793		&states.ResourceInstanceObjectSrc{
8794			Status:    states.ObjectReady,
8795			AttrsJSON: []byte(`{"id":"foo"}`),
8796		},
8797		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
8798	)
8799	root.SetOutputValue("name", cty.StringVal("test-bar"), false)
8800
8801	ctx := testContext2(t, &ContextOpts{
8802		Config: m,
8803		Providers: map[addrs.Provider]providers.Factory{
8804			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8805		},
8806		State:    state,
8807		PlanMode: plans.DestroyMode,
8808	})
8809
8810	if _, diags := ctx.Plan(); diags.HasErrors() {
8811		t.Fatalf("err: %s", diags.Err())
8812	}
8813
8814	s, diags := ctx.Apply()
8815	if diags.HasErrors() {
8816		t.Fatalf("error during apply: %s", diags.Err())
8817	}
8818
8819	got := strings.TrimSpace(s.String())
8820	want := strings.TrimSpace(`<no state>`)
8821	if got != want {
8822		t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
8823	}
8824}
8825
8826func TestContext2Apply_providerWithLocals(t *testing.T) {
8827	m := testModule(t, "provider-with-locals")
8828	p := testProvider("aws")
8829
8830	providerRegion := ""
8831	// this should not be overridden during destroy
8832	p.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
8833		val := req.Config.GetAttr("region")
8834		if !val.IsNull() {
8835			providerRegion = val.AsString()
8836		}
8837
8838		return
8839	}
8840
8841	p.PlanResourceChangeFn = testDiffFn
8842	ctx := testContext2(t, &ContextOpts{
8843		Config: m,
8844		Providers: map[addrs.Provider]providers.Factory{
8845			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8846		},
8847	})
8848
8849	if _, diags := ctx.Plan(); diags.HasErrors() {
8850		t.Fatalf("err: %s", diags.Err())
8851	}
8852
8853	state, diags := ctx.Apply()
8854	if diags.HasErrors() {
8855		t.Fatalf("err: %s", diags.Err())
8856	}
8857
8858	ctx = testContext2(t, &ContextOpts{
8859		Config: m,
8860		Providers: map[addrs.Provider]providers.Factory{
8861			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8862		},
8863		State:    state,
8864		PlanMode: plans.DestroyMode,
8865	})
8866
8867	if _, diags := ctx.Plan(); diags.HasErrors() {
8868		t.Fatalf("err: %s", diags.Err())
8869	}
8870
8871	state, diags = ctx.Apply()
8872	if diags.HasErrors() {
8873		t.Fatalf("err: %s", diags.Err())
8874	}
8875
8876	if state.HasResources() {
8877		t.Fatal("expected no state, got:", state)
8878	}
8879
8880	if providerRegion != "bar" {
8881		t.Fatalf("expected region %q, got: %q", "bar", providerRegion)
8882	}
8883}
8884
8885func TestContext2Apply_destroyWithProviders(t *testing.T) {
8886	m := testModule(t, "destroy-module-with-provider")
8887	p := testProvider("aws")
8888	p.PlanResourceChangeFn = testDiffFn
8889
8890	state := states.NewState()
8891	removed := state.EnsureModule(addrs.RootModuleInstance.Child("mod", addrs.NoKey).Child("removed", addrs.NoKey))
8892	removed.SetResourceInstanceCurrent(
8893		mustResourceInstanceAddr("aws_instance.child").Resource,
8894		&states.ResourceInstanceObjectSrc{
8895			Status:    states.ObjectReady,
8896			AttrsJSON: []byte(`{"id":"bar"}`),
8897		},
8898		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].baz`),
8899	)
8900
8901	ctx := testContext2(t, &ContextOpts{
8902		Config: m,
8903		Providers: map[addrs.Provider]providers.Factory{
8904			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8905		},
8906		State:    state,
8907		PlanMode: plans.DestroyMode,
8908	})
8909
8910	// test that we can't destroy if the provider is missing
8911	if _, diags := ctx.Plan(); diags == nil {
8912		t.Fatal("expected plan error, provider.aws.baz doesn't exist")
8913	}
8914
8915	// correct the state
8916	state.Modules["module.mod.module.removed"].Resources["aws_instance.child"].ProviderConfig = mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].bar`)
8917
8918	ctx = testContext2(t, &ContextOpts{
8919		Config: m,
8920		Providers: map[addrs.Provider]providers.Factory{
8921			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
8922		},
8923		State:    state,
8924		PlanMode: plans.DestroyMode,
8925	})
8926
8927	if _, diags := ctx.Plan(); diags.HasErrors() {
8928		t.Fatal(diags.Err())
8929	}
8930	state, diags := ctx.Apply()
8931	if diags.HasErrors() {
8932		t.Fatalf("error during apply: %s", diags.Err())
8933	}
8934
8935	got := strings.TrimSpace(state.String())
8936
8937	want := strings.TrimSpace("<no state>")
8938	if got != want {
8939		t.Fatalf("wrong final state\ngot:\n%s\nwant:\n%s", got, want)
8940	}
8941}
8942
8943func TestContext2Apply_providersFromState(t *testing.T) {
8944	m := configs.NewEmptyConfig()
8945	p := testProvider("aws")
8946	p.PlanResourceChangeFn = testDiffFn
8947
8948	implicitProviderState := states.NewState()
8949	impRoot := implicitProviderState.EnsureModule(addrs.RootModuleInstance)
8950	impRoot.SetResourceInstanceCurrent(
8951		mustResourceInstanceAddr("aws_instance.a").Resource,
8952		&states.ResourceInstanceObjectSrc{
8953			Status:    states.ObjectReady,
8954			AttrsJSON: []byte(`{"id":"bar"}`),
8955		},
8956		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
8957	)
8958
8959	aliasedProviderState := states.NewState()
8960	aliasRoot := aliasedProviderState.EnsureModule(addrs.RootModuleInstance)
8961	aliasRoot.SetResourceInstanceCurrent(
8962		mustResourceInstanceAddr("aws_instance.a").Resource,
8963		&states.ResourceInstanceObjectSrc{
8964			Status:    states.ObjectReady,
8965			AttrsJSON: []byte(`{"id":"bar"}`),
8966		},
8967		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"].bar`),
8968	)
8969
8970	moduleProviderState := states.NewState()
8971	moduleProviderRoot := moduleProviderState.EnsureModule(addrs.RootModuleInstance)
8972	moduleProviderRoot.SetResourceInstanceCurrent(
8973		mustResourceInstanceAddr("aws_instance.a").Resource,
8974		&states.ResourceInstanceObjectSrc{
8975			Status:    states.ObjectReady,
8976			AttrsJSON: []byte(`{"id":"bar"}`),
8977		},
8978		mustProviderConfig(`module.child.provider["registry.terraform.io/hashicorp/aws"]`),
8979	)
8980
8981	for _, tc := range []struct {
8982		name   string
8983		state  *states.State
8984		output string
8985		err    bool
8986	}{
8987		{
8988			name:   "add implicit provider",
8989			state:  implicitProviderState,
8990			err:    false,
8991			output: "<no state>",
8992		},
8993
8994		// an aliased provider must be in the config to remove a resource
8995		{
8996			name:  "add aliased provider",
8997			state: aliasedProviderState,
8998			err:   true,
8999		},
9000
9001		// a provider in a module implies some sort of config, so this isn't
9002		// allowed even without an alias
9003		{
9004			name:  "add unaliased module provider",
9005			state: moduleProviderState,
9006			err:   true,
9007		},
9008	} {
9009		t.Run(tc.name, func(t *testing.T) {
9010			ctx := testContext2(t, &ContextOpts{
9011				Config: m,
9012				Providers: map[addrs.Provider]providers.Factory{
9013					addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
9014				},
9015				State: tc.state,
9016			})
9017
9018			_, diags := ctx.Plan()
9019			if tc.err {
9020				if diags == nil {
9021					t.Fatal("expected error")
9022				} else {
9023					return
9024				}
9025			}
9026			if !tc.err && diags.HasErrors() {
9027				t.Fatal(diags.Err())
9028			}
9029
9030			state, diags := ctx.Apply()
9031			if diags.HasErrors() {
9032				t.Fatalf("diags: %s", diags.Err())
9033			}
9034
9035			checkStateString(t, state, "<no state>")
9036
9037		})
9038	}
9039}
9040
9041func TestContext2Apply_plannedInterpolatedCount(t *testing.T) {
9042	m, snap := testModuleWithSnapshot(t, "apply-interpolated-count")
9043
9044	p := testProvider("aws")
9045	p.PlanResourceChangeFn = testDiffFn
9046
9047	Providers := map[addrs.Provider]providers.Factory{
9048		addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
9049	}
9050
9051	state := states.NewState()
9052	root := state.EnsureModule(addrs.RootModuleInstance)
9053	root.SetResourceInstanceCurrent(
9054		mustResourceInstanceAddr("aws_instance.test").Resource,
9055		&states.ResourceInstanceObjectSrc{
9056			Status:    states.ObjectReady,
9057			AttrsJSON: []byte(`{"id":"foo"}`),
9058		},
9059		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
9060	)
9061
9062	ctx := testContext2(t, &ContextOpts{
9063		Config:    m,
9064		Providers: Providers,
9065		State:     state,
9066	})
9067
9068	plan, diags := ctx.Plan()
9069	if diags.HasErrors() {
9070		t.Fatalf("plan failed: %s", diags.Err())
9071	}
9072
9073	// We'll marshal and unmarshal the plan here, to ensure that we have
9074	// a clean new context as would be created if we separately ran
9075	// terraform plan -out=tfplan && terraform apply tfplan
9076	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
9077	if err != nil {
9078		t.Fatalf("failed to round-trip through planfile: %s", err)
9079	}
9080
9081	ctxOpts.Providers = Providers
9082	ctx, diags = NewContext(ctxOpts)
9083	if diags.HasErrors() {
9084		t.Fatalf("err: %s", diags.Err())
9085	}
9086
9087	// Applying the plan should now succeed
9088	_, diags = ctx.Apply()
9089	if diags.HasErrors() {
9090		t.Fatalf("apply failed: %s", diags.Err())
9091	}
9092}
9093
9094func TestContext2Apply_plannedDestroyInterpolatedCount(t *testing.T) {
9095	m, snap := testModuleWithSnapshot(t, "plan-destroy-interpolated-count")
9096
9097	p := testProvider("aws")
9098	p.PlanResourceChangeFn = testDiffFn
9099	providers := map[addrs.Provider]providers.Factory{
9100		addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
9101	}
9102
9103	state := states.NewState()
9104	root := state.EnsureModule(addrs.RootModuleInstance)
9105	root.SetResourceInstanceCurrent(
9106		mustResourceInstanceAddr("aws_instance.a[0]").Resource,
9107		&states.ResourceInstanceObjectSrc{
9108			Status:    states.ObjectReady,
9109			AttrsJSON: []byte(`{"id":"foo"}`),
9110		},
9111		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
9112	)
9113	root.SetResourceInstanceCurrent(
9114		mustResourceInstanceAddr("aws_instance.a[1]").Resource,
9115		&states.ResourceInstanceObjectSrc{
9116			Status:    states.ObjectReady,
9117			AttrsJSON: []byte(`{"id":"foo"}`),
9118		},
9119		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
9120	)
9121	root.SetOutputValue("out", cty.ListVal([]cty.Value{cty.StringVal("foo"), cty.StringVal("foo")}), false)
9122
9123	ctx := testContext2(t, &ContextOpts{
9124		Config:    m,
9125		Providers: providers,
9126		State:     state,
9127		PlanMode:  plans.DestroyMode,
9128	})
9129
9130	plan, diags := ctx.Plan()
9131	if diags.HasErrors() {
9132		t.Fatalf("plan failed: %s", diags.Err())
9133	}
9134
9135	// We'll marshal and unmarshal the plan here, to ensure that we have
9136	// a clean new context as would be created if we separately ran
9137	// terraform plan -out=tfplan && terraform apply tfplan
9138	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
9139	if err != nil {
9140		t.Fatalf("failed to round-trip through planfile: %s", err)
9141	}
9142
9143	ctxOpts.Providers = providers
9144	ctx, diags = NewContext(ctxOpts)
9145	if diags.HasErrors() {
9146		t.Fatalf("err: %s", diags.Err())
9147	}
9148
9149	// Applying the plan should now succeed
9150	state, diags = ctx.Apply()
9151	if diags.HasErrors() {
9152		t.Fatalf("apply failed: %s", diags.Err())
9153	}
9154	if !state.Empty() {
9155		t.Fatalf("state not empty: %s\n", state)
9156	}
9157}
9158
9159func TestContext2Apply_scaleInMultivarRef(t *testing.T) {
9160	m := testModule(t, "apply-resource-scale-in")
9161
9162	p := testProvider("aws")
9163	p.PlanResourceChangeFn = testDiffFn
9164
9165	Providers := map[addrs.Provider]providers.Factory{
9166		addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
9167	}
9168
9169	state := states.NewState()
9170	root := state.EnsureModule(addrs.RootModuleInstance)
9171	root.SetResourceInstanceCurrent(
9172		mustResourceInstanceAddr("aws_instance.one").Resource,
9173		&states.ResourceInstanceObjectSrc{
9174			Status:    states.ObjectReady,
9175			AttrsJSON: []byte(`{"id":"foo"}`),
9176		},
9177		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
9178	)
9179	root.SetResourceInstanceCurrent(
9180		mustResourceInstanceAddr("aws_instance.two").Resource,
9181		&states.ResourceInstanceObjectSrc{
9182			Status:    states.ObjectReady,
9183			AttrsJSON: []byte(`{"id":"foo"}`),
9184		},
9185		mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
9186	)
9187
9188	ctx := testContext2(t, &ContextOpts{
9189		Config:    m,
9190		Providers: Providers,
9191		State:     state,
9192		Variables: InputValues{
9193			"instance_count": {
9194				Value:      cty.NumberIntVal(0),
9195				SourceType: ValueFromCaller,
9196			},
9197		},
9198	})
9199
9200	_, diags := ctx.Plan()
9201	assertNoErrors(t, diags)
9202
9203	// Applying the plan should now succeed
9204	_, diags = ctx.Apply()
9205	assertNoErrors(t, diags)
9206}
9207
9208func TestContext2Apply_inconsistentWithPlan(t *testing.T) {
9209	m := testModule(t, "apply-inconsistent-with-plan")
9210	p := testProvider("test")
9211	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
9212		ResourceTypes: map[string]*configschema.Block{
9213			"test": {
9214				Attributes: map[string]*configschema.Attribute{
9215					"id": {Type: cty.String, Computed: true},
9216				},
9217			},
9218		},
9219	})
9220	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
9221		return providers.PlanResourceChangeResponse{
9222			PlannedState: cty.ObjectVal(map[string]cty.Value{
9223				"id": cty.StringVal("before"),
9224			}),
9225		}
9226	}
9227	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
9228		return providers.ApplyResourceChangeResponse{
9229			NewState: cty.ObjectVal(map[string]cty.Value{
9230				// This is intentionally incorrect: because id was fixed at "before"
9231				// during plan, it must not change during apply.
9232				"id": cty.StringVal("after"),
9233			}),
9234		}
9235	}
9236	ctx := testContext2(t, &ContextOpts{
9237		Config: m,
9238		Providers: map[addrs.Provider]providers.Factory{
9239			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
9240		},
9241	})
9242
9243	if _, diags := ctx.Plan(); diags.HasErrors() {
9244		t.Fatalf("plan errors: %s", diags.Err())
9245	}
9246
9247	_, diags := ctx.Apply()
9248	if !diags.HasErrors() {
9249		t.Fatalf("apply succeeded; want error")
9250	}
9251	if got, want := diags.Err().Error(), "Provider produced inconsistent result after apply"; !strings.Contains(got, want) {
9252		t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want)
9253	}
9254}
9255
9256// Issue 19908 was about retaining an existing object in the state when an
9257// update to it fails and the provider does not return a partially-updated
9258// value for it. Previously we were incorrectly removing it from the state
9259// in that case, but instead it should be retained so the update can be
9260// retried.
9261func TestContext2Apply_issue19908(t *testing.T) {
9262	m := testModule(t, "apply-issue19908")
9263	p := testProvider("test")
9264	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
9265		ResourceTypes: map[string]*configschema.Block{
9266			"test": {
9267				Attributes: map[string]*configschema.Attribute{
9268					"baz": {Type: cty.String, Required: true},
9269				},
9270			},
9271		},
9272	})
9273	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
9274		return providers.PlanResourceChangeResponse{
9275			PlannedState: req.ProposedNewState,
9276		}
9277	}
9278	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
9279		var diags tfdiags.Diagnostics
9280		diags = diags.Append(fmt.Errorf("update failed"))
9281		return providers.ApplyResourceChangeResponse{
9282			Diagnostics: diags,
9283		}
9284	}
9285	ctx := testContext2(t, &ContextOpts{
9286		Config: m,
9287		State: states.BuildState(func(s *states.SyncState) {
9288			s.SetResourceInstanceCurrent(
9289				addrs.Resource{
9290					Mode: addrs.ManagedResourceMode,
9291					Type: "test",
9292					Name: "foo",
9293				}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
9294				&states.ResourceInstanceObjectSrc{
9295					AttrsJSON: []byte(`{"baz":"old"}`),
9296					Status:    states.ObjectReady,
9297				},
9298				addrs.AbsProviderConfig{
9299					Provider: addrs.NewDefaultProvider("test"),
9300					Module:   addrs.RootModule,
9301				},
9302			)
9303		}),
9304		Providers: map[addrs.Provider]providers.Factory{
9305			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
9306		},
9307	})
9308
9309	if _, diags := ctx.Plan(); diags.HasErrors() {
9310		t.Fatalf("plan errors: %s", diags.Err())
9311	}
9312
9313	state, diags := ctx.Apply()
9314	if !diags.HasErrors() {
9315		t.Fatalf("apply succeeded; want error")
9316	}
9317	if got, want := diags.Err().Error(), "update failed"; !strings.Contains(got, want) {
9318		t.Fatalf("wrong error\ngot: %s\nshould contain: %s", got, want)
9319	}
9320
9321	mod := state.RootModule()
9322	rs := mod.Resources["test.foo"]
9323	if rs == nil {
9324		t.Fatalf("test.foo not in state after apply, but should be")
9325	}
9326	is := rs.Instances[addrs.NoKey]
9327	if is == nil {
9328		t.Fatalf("test.foo not in state after apply, but should be")
9329	}
9330	obj := is.Current
9331	if obj == nil {
9332		t.Fatalf("test.foo has no current object in state after apply, but should do")
9333	}
9334
9335	if got, want := obj.Status, states.ObjectReady; got != want {
9336		t.Errorf("test.foo has wrong status %s after apply; want %s", got, want)
9337	}
9338	if got, want := obj.AttrsJSON, []byte(`"old"`); !bytes.Contains(got, want) {
9339		t.Errorf("test.foo attributes JSON doesn't contain %s after apply\ngot: %s", want, got)
9340	}
9341}
9342
9343func TestContext2Apply_invalidIndexRef(t *testing.T) {
9344	p := testProvider("test")
9345	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
9346		ResourceTypes: map[string]*configschema.Block{
9347			"test_instance": {
9348				Attributes: map[string]*configschema.Attribute{
9349					"value": {Type: cty.String, Optional: true, Computed: true},
9350				},
9351			},
9352		},
9353	})
9354	p.PlanResourceChangeFn = testDiffFn
9355
9356	m := testModule(t, "apply-invalid-index")
9357	c := testContext2(t, &ContextOpts{
9358		Config: m,
9359		Providers: map[addrs.Provider]providers.Factory{
9360			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
9361		},
9362	})
9363	diags := c.Validate()
9364	if diags.HasErrors() {
9365		t.Fatalf("unexpected validation failure: %s", diags.Err())
9366	}
9367
9368	wantErr := `The given key does not identify an element in this collection value`
9369	_, diags = c.Plan()
9370
9371	if !diags.HasErrors() {
9372		t.Fatalf("plan succeeded; want error")
9373	}
9374	gotErr := diags.Err().Error()
9375
9376	if !strings.Contains(gotErr, wantErr) {
9377		t.Fatalf("missing expected error\ngot: %s\n\nwant: error containing %q", gotErr, wantErr)
9378	}
9379}
9380
9381func TestContext2Apply_moduleReplaceCycle(t *testing.T) {
9382	for _, mode := range []string{"normal", "cbd"} {
9383		var m *configs.Config
9384
9385		switch mode {
9386		case "normal":
9387			m = testModule(t, "apply-module-replace-cycle")
9388		case "cbd":
9389			m = testModule(t, "apply-module-replace-cycle-cbd")
9390		}
9391
9392		p := testProvider("aws")
9393		p.PlanResourceChangeFn = testDiffFn
9394
9395		instanceSchema := &configschema.Block{
9396			Attributes: map[string]*configschema.Attribute{
9397				"id":          {Type: cty.String, Computed: true},
9398				"require_new": {Type: cty.String, Optional: true},
9399			},
9400		}
9401
9402		p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
9403			ResourceTypes: map[string]*configschema.Block{
9404				"aws_instance": instanceSchema,
9405			},
9406		})
9407
9408		state := states.NewState()
9409		modA := state.EnsureModule(addrs.RootModuleInstance.Child("a", addrs.NoKey))
9410		modA.SetResourceInstanceCurrent(
9411			addrs.Resource{
9412				Mode: addrs.ManagedResourceMode,
9413				Type: "aws_instance",
9414				Name: "a",
9415			}.Instance(addrs.NoKey),
9416			&states.ResourceInstanceObjectSrc{
9417				Status:              states.ObjectReady,
9418				AttrsJSON:           []byte(`{"id":"a","require_new":"old"}`),
9419				CreateBeforeDestroy: mode == "cbd",
9420			},
9421			addrs.AbsProviderConfig{
9422				Provider: addrs.NewDefaultProvider("aws"),
9423				Module:   addrs.RootModule,
9424			},
9425		)
9426
9427		modB := state.EnsureModule(addrs.RootModuleInstance.Child("b", addrs.NoKey))
9428		modB.SetResourceInstanceCurrent(
9429			addrs.Resource{
9430				Mode: addrs.ManagedResourceMode,
9431				Type: "aws_instance",
9432				Name: "b",
9433			}.Instance(addrs.IntKey(0)),
9434			&states.ResourceInstanceObjectSrc{
9435				Status:    states.ObjectReady,
9436				AttrsJSON: []byte(`{"id":"b","require_new":"old"}`),
9437			},
9438			addrs.AbsProviderConfig{
9439				Provider: addrs.NewDefaultProvider("aws"),
9440				Module:   addrs.RootModule,
9441			},
9442		)
9443
9444		aBefore, _ := plans.NewDynamicValue(
9445			cty.ObjectVal(map[string]cty.Value{
9446				"id":          cty.StringVal("a"),
9447				"require_new": cty.StringVal("old"),
9448			}), instanceSchema.ImpliedType())
9449		aAfter, _ := plans.NewDynamicValue(
9450			cty.ObjectVal(map[string]cty.Value{
9451				"id":          cty.UnknownVal(cty.String),
9452				"require_new": cty.StringVal("new"),
9453			}), instanceSchema.ImpliedType())
9454		bBefore, _ := plans.NewDynamicValue(
9455			cty.ObjectVal(map[string]cty.Value{
9456				"id":          cty.StringVal("b"),
9457				"require_new": cty.StringVal("old"),
9458			}), instanceSchema.ImpliedType())
9459		bAfter, _ := plans.NewDynamicValue(
9460			cty.ObjectVal(map[string]cty.Value{
9461				"id":          cty.UnknownVal(cty.String),
9462				"require_new": cty.UnknownVal(cty.String),
9463			}), instanceSchema.ImpliedType())
9464
9465		var aAction plans.Action
9466		switch mode {
9467		case "normal":
9468			aAction = plans.DeleteThenCreate
9469		case "cbd":
9470			aAction = plans.CreateThenDelete
9471		}
9472
9473		changes := &plans.Changes{
9474			Resources: []*plans.ResourceInstanceChangeSrc{
9475				{
9476					Addr: addrs.Resource{
9477						Mode: addrs.ManagedResourceMode,
9478						Type: "aws_instance",
9479						Name: "a",
9480					}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("a", addrs.NoKey)),
9481					ProviderAddr: addrs.AbsProviderConfig{
9482						Provider: addrs.NewDefaultProvider("aws"),
9483						Module:   addrs.RootModule,
9484					},
9485					ChangeSrc: plans.ChangeSrc{
9486						Action: aAction,
9487						Before: aBefore,
9488						After:  aAfter,
9489					},
9490				},
9491				{
9492					Addr: addrs.Resource{
9493						Mode: addrs.ManagedResourceMode,
9494						Type: "aws_instance",
9495						Name: "b",
9496					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance.Child("b", addrs.NoKey)),
9497					ProviderAddr: addrs.AbsProviderConfig{
9498						Provider: addrs.NewDefaultProvider("aws"),
9499						Module:   addrs.RootModule,
9500					},
9501					ChangeSrc: plans.ChangeSrc{
9502						Action: plans.DeleteThenCreate,
9503						Before: bBefore,
9504						After:  bAfter,
9505					},
9506				},
9507			},
9508		}
9509
9510		ctx := testContext2(t, &ContextOpts{
9511			Config: m,
9512			Providers: map[addrs.Provider]providers.Factory{
9513				addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
9514			},
9515			State:   state,
9516			Changes: changes,
9517		})
9518
9519		t.Run(mode, func(t *testing.T) {
9520			_, diags := ctx.Apply()
9521			if diags.HasErrors() {
9522				t.Fatal(diags.Err())
9523			}
9524		})
9525	}
9526}
9527
9528func TestContext2Apply_destroyDataCycle(t *testing.T) {
9529	m, snap := testModuleWithSnapshot(t, "apply-destroy-data-cycle")
9530	p := testProvider("null")
9531	p.PlanResourceChangeFn = testDiffFn
9532	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
9533		return providers.ReadDataSourceResponse{
9534			State: cty.ObjectVal(map[string]cty.Value{
9535				"id":  cty.StringVal("new"),
9536				"foo": cty.NullVal(cty.String),
9537			}),
9538		}
9539	}
9540
9541	tp := testProvider("test")
9542	tp.PlanResourceChangeFn = testDiffFn
9543
9544	state := states.NewState()
9545	root := state.EnsureModule(addrs.RootModuleInstance)
9546	root.SetResourceInstanceCurrent(
9547		addrs.Resource{
9548			Mode: addrs.ManagedResourceMode,
9549			Type: "null_resource",
9550			Name: "a",
9551		}.Instance(addrs.IntKey(0)),
9552		&states.ResourceInstanceObjectSrc{
9553			Status:    states.ObjectReady,
9554			AttrsJSON: []byte(`{"id":"a"}`),
9555		},
9556		addrs.AbsProviderConfig{
9557			Provider: addrs.NewDefaultProvider("null"),
9558			Module:   addrs.RootModule,
9559		},
9560	)
9561	root.SetResourceInstanceCurrent(
9562		addrs.Resource{
9563			Mode: addrs.ManagedResourceMode,
9564			Type: "test_resource",
9565			Name: "a",
9566		}.Instance(addrs.IntKey(0)),
9567		&states.ResourceInstanceObjectSrc{
9568			Status:    states.ObjectReady,
9569			AttrsJSON: []byte(`{"id":"a"}`),
9570			Dependencies: []addrs.ConfigResource{
9571				addrs.ConfigResource{
9572					Resource: addrs.Resource{
9573						Mode: addrs.DataResourceMode,
9574						Type: "null_data_source",
9575						Name: "d",
9576					},
9577					Module: addrs.RootModule,
9578				},
9579			},
9580		},
9581		addrs.AbsProviderConfig{
9582			Provider: addrs.NewDefaultProvider("test"),
9583			Module:   addrs.RootModule,
9584		},
9585	)
9586	root.SetResourceInstanceCurrent(
9587		addrs.Resource{
9588			Mode: addrs.DataResourceMode,
9589			Type: "null_data_source",
9590			Name: "d",
9591		}.Instance(addrs.NoKey),
9592		&states.ResourceInstanceObjectSrc{
9593			Status:    states.ObjectReady,
9594			AttrsJSON: []byte(`{"id":"old"}`),
9595		},
9596		addrs.AbsProviderConfig{
9597			Provider: addrs.NewDefaultProvider("null"),
9598			Module:   addrs.RootModule,
9599		},
9600	)
9601
9602	Providers := map[addrs.Provider]providers.Factory{
9603		addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
9604		addrs.NewDefaultProvider("test"): testProviderFuncFixed(tp),
9605	}
9606
9607	ctx := testContext2(t, &ContextOpts{
9608		Config:    m,
9609		Providers: Providers,
9610		State:     state,
9611		PlanMode:  plans.DestroyMode,
9612	})
9613
9614	plan, diags := ctx.Plan()
9615	diags.HasErrors()
9616	if diags.HasErrors() {
9617		t.Fatalf("diags: %s", diags.Err())
9618	}
9619
9620	// We'll marshal and unmarshal the plan here, to ensure that we have
9621	// a clean new context as would be created if we separately ran
9622	// terraform plan -out=tfplan && terraform apply tfplan
9623	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
9624	if err != nil {
9625		t.Fatal(err)
9626	}
9627	ctxOpts.Providers = Providers
9628	ctx, diags = NewContext(ctxOpts)
9629	if diags.HasErrors() {
9630		t.Fatalf("failed to create context for plan: %s", diags.Err())
9631	}
9632
9633	tp.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
9634		foo := req.Config.GetAttr("foo")
9635		if !foo.IsKnown() {
9636			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("unknown config value foo"))
9637			return resp
9638		}
9639
9640		if foo.AsString() != "new" {
9641			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("wrong config value: %q", foo.AsString()))
9642		}
9643		return resp
9644	}
9645
9646	_, diags = ctx.Apply()
9647	if diags.HasErrors() {
9648		t.Fatalf("diags: %s", diags.Err())
9649	}
9650}
9651
9652func TestContext2Apply_taintedDestroyFailure(t *testing.T) {
9653	m := testModule(t, "apply-destroy-tainted")
9654	p := testProvider("test")
9655	p.PlanResourceChangeFn = testDiffFn
9656	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
9657		// All destroys fail.
9658		if req.PlannedState.IsNull() {
9659			resp.Diagnostics = resp.Diagnostics.Append(errors.New("failure"))
9660			return
9661		}
9662
9663		// c will also fail to create, meaning the existing tainted instance
9664		// becomes deposed, ans is then promoted back to current.
9665		// only C has a foo attribute
9666		planned := req.PlannedState.AsValueMap()
9667		foo, ok := planned["foo"]
9668		if ok && !foo.IsNull() && foo.AsString() == "c" {
9669			resp.Diagnostics = resp.Diagnostics.Append(errors.New("failure"))
9670			return
9671		}
9672
9673		return testApplyFn(req)
9674	}
9675	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
9676		ResourceTypes: map[string]*configschema.Block{
9677			"test_instance": {
9678				Attributes: map[string]*configschema.Attribute{
9679					"id": {
9680						Type:     cty.String,
9681						Computed: true,
9682					},
9683					"foo": {
9684						Type:     cty.String,
9685						Optional: true,
9686					},
9687				},
9688			},
9689		},
9690	})
9691
9692	state := states.NewState()
9693	root := state.EnsureModule(addrs.RootModuleInstance)
9694	root.SetResourceInstanceCurrent(
9695		addrs.Resource{
9696			Mode: addrs.ManagedResourceMode,
9697			Type: "test_instance",
9698			Name: "a",
9699		}.Instance(addrs.NoKey),
9700		&states.ResourceInstanceObjectSrc{
9701			Status:    states.ObjectTainted,
9702			AttrsJSON: []byte(`{"id":"a","foo":"a"}`),
9703		},
9704		addrs.AbsProviderConfig{
9705			Provider: addrs.NewDefaultProvider("test"),
9706			Module:   addrs.RootModule,
9707		},
9708	)
9709	root.SetResourceInstanceCurrent(
9710		addrs.Resource{
9711			Mode: addrs.ManagedResourceMode,
9712			Type: "test_instance",
9713			Name: "b",
9714		}.Instance(addrs.NoKey),
9715		&states.ResourceInstanceObjectSrc{
9716			Status:    states.ObjectTainted,
9717			AttrsJSON: []byte(`{"id":"b","foo":"b"}`),
9718		},
9719		addrs.AbsProviderConfig{
9720			Provider: addrs.NewDefaultProvider("test"),
9721			Module:   addrs.RootModule,
9722		},
9723	)
9724	root.SetResourceInstanceCurrent(
9725		addrs.Resource{
9726			Mode: addrs.ManagedResourceMode,
9727			Type: "test_instance",
9728			Name: "c",
9729		}.Instance(addrs.NoKey),
9730		&states.ResourceInstanceObjectSrc{
9731			Status:    states.ObjectTainted,
9732			AttrsJSON: []byte(`{"id":"c","foo":"old"}`),
9733		},
9734		addrs.AbsProviderConfig{
9735			Provider: addrs.NewDefaultProvider("test"),
9736			Module:   addrs.RootModule,
9737		},
9738	)
9739
9740	Providers := map[addrs.Provider]providers.Factory{
9741		addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
9742	}
9743
9744	ctx := testContext2(t, &ContextOpts{
9745		Config:    m,
9746		Providers: Providers,
9747		State:     state,
9748		Hooks:     []Hook{&testHook{}},
9749	})
9750
9751	_, diags := ctx.Plan()
9752	diags.HasErrors()
9753	if diags.HasErrors() {
9754		t.Fatalf("diags: %s", diags.Err())
9755	}
9756
9757	state, diags = ctx.Apply()
9758	if !diags.HasErrors() {
9759		t.Fatal("expected error")
9760	}
9761
9762	root = state.Module(addrs.RootModuleInstance)
9763
9764	// the instance that failed to destroy should remain tainted
9765	a := root.ResourceInstance(addrs.Resource{
9766		Mode: addrs.ManagedResourceMode,
9767		Type: "test_instance",
9768		Name: "a",
9769	}.Instance(addrs.NoKey))
9770
9771	if a.Current.Status != states.ObjectTainted {
9772		t.Fatal("test_instance.a should be tainted")
9773	}
9774
9775	// b is create_before_destroy, and the destroy failed, so there should be 1
9776	// deposed instance.
9777	b := root.ResourceInstance(addrs.Resource{
9778		Mode: addrs.ManagedResourceMode,
9779		Type: "test_instance",
9780		Name: "b",
9781	}.Instance(addrs.NoKey))
9782
9783	if b.Current.Status != states.ObjectReady {
9784		t.Fatal("test_instance.b should be Ready")
9785	}
9786
9787	if len(b.Deposed) != 1 {
9788		t.Fatal("test_instance.b failed to keep deposed instance")
9789	}
9790
9791	// the desposed c instance should be promoted back to Current, and remain
9792	// tainted
9793	c := root.ResourceInstance(addrs.Resource{
9794		Mode: addrs.ManagedResourceMode,
9795		Type: "test_instance",
9796		Name: "c",
9797	}.Instance(addrs.NoKey))
9798
9799	if c.Current == nil {
9800		t.Fatal("test_instance.c has no current instance, but it should")
9801	}
9802
9803	if c.Current.Status != states.ObjectTainted {
9804		t.Fatal("test_instance.c should be tainted")
9805	}
9806
9807	if len(c.Deposed) != 0 {
9808		t.Fatal("test_instance.c should have no deposed instances")
9809	}
9810
9811	if string(c.Current.AttrsJSON) != `{"foo":"old","id":"c"}` {
9812		t.Fatalf("unexpected attrs for c: %q\n", c.Current.AttrsJSON)
9813	}
9814}
9815
9816func TestContext2Apply_plannedConnectionRefs(t *testing.T) {
9817	m := testModule(t, "apply-plan-connection-refs")
9818	p := testProvider("test")
9819	p.PlanResourceChangeFn = testDiffFn
9820	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
9821		s := req.PlannedState.AsValueMap()
9822		// delay "a" slightly, so if the reference edge is missing the "b"
9823		// provisioner will see an unknown value.
9824		if s["foo"].AsString() == "a" {
9825			time.Sleep(500 * time.Millisecond)
9826		}
9827
9828		s["id"] = cty.StringVal("ID")
9829		if ty, ok := s["type"]; ok && !ty.IsKnown() {
9830			s["type"] = cty.StringVal(req.TypeName)
9831		}
9832		resp.NewState = cty.ObjectVal(s)
9833		return resp
9834	}
9835
9836	provisionerFactory := func() (provisioners.Interface, error) {
9837		pr := testProvisioner()
9838		pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
9839			host := req.Connection.GetAttr("host")
9840			if host.IsNull() || !host.IsKnown() {
9841				resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("invalid host value: %#v", host))
9842			}
9843
9844			return resp
9845		}
9846		return pr, nil
9847	}
9848
9849	Providers := map[addrs.Provider]providers.Factory{
9850		addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
9851	}
9852
9853	provisioners := map[string]provisioners.Factory{
9854		"shell": provisionerFactory,
9855	}
9856
9857	hook := &testHook{}
9858	ctx := testContext2(t, &ContextOpts{
9859		Config:       m,
9860		Providers:    Providers,
9861		Provisioners: provisioners,
9862		Hooks:        []Hook{hook},
9863	})
9864
9865	_, diags := ctx.Plan()
9866	diags.HasErrors()
9867	if diags.HasErrors() {
9868		t.Fatalf("diags: %s", diags.Err())
9869	}
9870
9871	_, diags = ctx.Apply()
9872	if diags.HasErrors() {
9873		t.Fatalf("diags: %s", diags.Err())
9874	}
9875}
9876
9877func TestContext2Apply_cbdCycle(t *testing.T) {
9878	m, snap := testModuleWithSnapshot(t, "apply-cbd-cycle")
9879	p := testProvider("test")
9880	p.PlanResourceChangeFn = testDiffFn
9881
9882	state := states.NewState()
9883	root := state.EnsureModule(addrs.RootModuleInstance)
9884	root.SetResourceInstanceCurrent(
9885		addrs.Resource{
9886			Mode: addrs.ManagedResourceMode,
9887			Type: "test_instance",
9888			Name: "a",
9889		}.Instance(addrs.NoKey),
9890		&states.ResourceInstanceObjectSrc{
9891			Status:    states.ObjectReady,
9892			AttrsJSON: []byte(`{"id":"a","require_new":"old","foo":"b"}`),
9893			Dependencies: []addrs.ConfigResource{
9894				addrs.ConfigResource{
9895					Resource: addrs.Resource{
9896						Mode: addrs.ManagedResourceMode,
9897						Type: "test_instance",
9898						Name: "b",
9899					},
9900					Module: addrs.RootModule,
9901				},
9902				addrs.ConfigResource{
9903					Resource: addrs.Resource{
9904						Mode: addrs.ManagedResourceMode,
9905						Type: "test_instance",
9906						Name: "c",
9907					},
9908					Module: addrs.RootModule,
9909				},
9910			},
9911		},
9912		addrs.AbsProviderConfig{
9913			Provider: addrs.NewDefaultProvider("test"),
9914			Module:   addrs.RootModule,
9915		},
9916	)
9917	root.SetResourceInstanceCurrent(
9918		addrs.Resource{
9919			Mode: addrs.ManagedResourceMode,
9920			Type: "test_instance",
9921			Name: "b",
9922		}.Instance(addrs.NoKey),
9923		&states.ResourceInstanceObjectSrc{
9924			Status:    states.ObjectReady,
9925			AttrsJSON: []byte(`{"id":"b","require_new":"old","foo":"c"}`),
9926			Dependencies: []addrs.ConfigResource{
9927				addrs.ConfigResource{
9928					Resource: addrs.Resource{
9929						Mode: addrs.ManagedResourceMode,
9930						Type: "test_instance",
9931						Name: "c",
9932					},
9933					Module: addrs.RootModule,
9934				},
9935			},
9936		},
9937		addrs.AbsProviderConfig{
9938			Provider: addrs.NewDefaultProvider("test"),
9939			Module:   addrs.RootModule,
9940		},
9941	)
9942	root.SetResourceInstanceCurrent(
9943		addrs.Resource{
9944			Mode: addrs.ManagedResourceMode,
9945			Type: "test_instance",
9946			Name: "c",
9947		}.Instance(addrs.NoKey),
9948		&states.ResourceInstanceObjectSrc{
9949			Status:    states.ObjectReady,
9950			AttrsJSON: []byte(`{"id":"c","require_new":"old"}`),
9951		},
9952		addrs.AbsProviderConfig{
9953			Provider: addrs.NewDefaultProvider("test"),
9954			Module:   addrs.RootModule,
9955		},
9956	)
9957
9958	Providers := map[addrs.Provider]providers.Factory{
9959		addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
9960	}
9961
9962	hook := &testHook{}
9963	ctx := testContext2(t, &ContextOpts{
9964		Config:    m,
9965		Providers: Providers,
9966		State:     state,
9967		Hooks:     []Hook{hook},
9968	})
9969
9970	plan, diags := ctx.Plan()
9971	diags.HasErrors()
9972	if diags.HasErrors() {
9973		t.Fatalf("diags: %s", diags.Err())
9974	}
9975
9976	// We'll marshal and unmarshal the plan here, to ensure that we have
9977	// a clean new context as would be created if we separately ran
9978	// terraform plan -out=tfplan && terraform apply tfplan
9979	ctxOpts, err := contextOptsForPlanViaFile(snap, plan)
9980	if err != nil {
9981		t.Fatal(err)
9982	}
9983	ctxOpts.Providers = Providers
9984	ctx, diags = NewContext(ctxOpts)
9985	if diags.HasErrors() {
9986		t.Fatalf("failed to create context for plan: %s", diags.Err())
9987	}
9988
9989	_, diags = ctx.Apply()
9990	if diags.HasErrors() {
9991		t.Fatalf("diags: %s", diags.Err())
9992	}
9993}
9994
9995func TestContext2Apply_ProviderMeta_apply_set(t *testing.T) {
9996	m := testModule(t, "provider-meta-set")
9997	p := testProvider("test")
9998	p.PlanResourceChangeFn = testDiffFn
9999	schema := p.ProviderSchema()
10000	schema.ProviderMeta = &configschema.Block{
10001		Attributes: map[string]*configschema.Attribute{
10002			"baz": {
10003				Type:     cty.String,
10004				Required: true,
10005			},
10006		},
10007	}
10008
10009	var pmMu sync.Mutex
10010	arcPMs := map[string]cty.Value{}
10011
10012	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
10013		pmMu.Lock()
10014		defer pmMu.Unlock()
10015		arcPMs[req.TypeName] = req.ProviderMeta
10016
10017		s := req.PlannedState.AsValueMap()
10018		s["id"] = cty.StringVal("ID")
10019		if ty, ok := s["type"]; ok && !ty.IsKnown() {
10020			s["type"] = cty.StringVal(req.TypeName)
10021		}
10022		return providers.ApplyResourceChangeResponse{
10023			NewState: cty.ObjectVal(s),
10024		}
10025	}
10026	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10027	ctx := testContext2(t, &ContextOpts{
10028		Config: m,
10029		Providers: map[addrs.Provider]providers.Factory{
10030			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10031		},
10032	})
10033
10034	_, diags := ctx.Plan()
10035	assertNoErrors(t, diags)
10036
10037	_, diags = ctx.Apply()
10038	assertNoErrors(t, diags)
10039
10040	if !p.ApplyResourceChangeCalled {
10041		t.Fatalf("ApplyResourceChange not called")
10042	}
10043
10044	expectations := map[string]cty.Value{}
10045
10046	if pm, ok := arcPMs["test_resource"]; !ok {
10047		t.Fatalf("sub-module ApplyResourceChange not called")
10048	} else if pm.IsNull() {
10049		t.Fatalf("null ProviderMeta in sub-module ApplyResourceChange")
10050	} else {
10051		expectations["quux-submodule"] = pm
10052	}
10053
10054	if pm, ok := arcPMs["test_instance"]; !ok {
10055		t.Fatalf("root module ApplyResourceChange not called")
10056	} else if pm.IsNull() {
10057		t.Fatalf("null ProviderMeta in root module ApplyResourceChange")
10058	} else {
10059		expectations["quux"] = pm
10060	}
10061
10062	type metaStruct struct {
10063		Baz string `cty:"baz"`
10064	}
10065
10066	for expected, v := range expectations {
10067		var meta metaStruct
10068		err := gocty.FromCtyValue(v, &meta)
10069		if err != nil {
10070			t.Fatalf("Error parsing cty value: %s", err)
10071		}
10072		if meta.Baz != expected {
10073			t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
10074		}
10075	}
10076}
10077
10078func TestContext2Apply_ProviderMeta_apply_unset(t *testing.T) {
10079	m := testModule(t, "provider-meta-unset")
10080	p := testProvider("test")
10081	p.PlanResourceChangeFn = testDiffFn
10082	schema := p.ProviderSchema()
10083	schema.ProviderMeta = &configschema.Block{
10084		Attributes: map[string]*configschema.Attribute{
10085			"baz": {
10086				Type:     cty.String,
10087				Required: true,
10088			},
10089		},
10090	}
10091	var pmMu sync.Mutex
10092	arcPMs := map[string]cty.Value{}
10093	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
10094		pmMu.Lock()
10095		defer pmMu.Unlock()
10096		arcPMs[req.TypeName] = req.ProviderMeta
10097
10098		s := req.PlannedState.AsValueMap()
10099		s["id"] = cty.StringVal("ID")
10100		if ty, ok := s["type"]; ok && !ty.IsKnown() {
10101			s["type"] = cty.StringVal(req.TypeName)
10102		}
10103		return providers.ApplyResourceChangeResponse{
10104			NewState: cty.ObjectVal(s),
10105		}
10106	}
10107	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10108	ctx := testContext2(t, &ContextOpts{
10109		Config: m,
10110		Providers: map[addrs.Provider]providers.Factory{
10111			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10112		},
10113	})
10114
10115	_, diags := ctx.Plan()
10116	assertNoErrors(t, diags)
10117
10118	_, diags = ctx.Apply()
10119	assertNoErrors(t, diags)
10120
10121	if !p.ApplyResourceChangeCalled {
10122		t.Fatalf("ApplyResourceChange not called")
10123	}
10124
10125	if pm, ok := arcPMs["test_resource"]; !ok {
10126		t.Fatalf("sub-module ApplyResourceChange not called")
10127	} else if !pm.IsNull() {
10128		t.Fatalf("non-null ProviderMeta in sub-module ApplyResourceChange: %+v", pm)
10129	}
10130
10131	if pm, ok := arcPMs["test_instance"]; !ok {
10132		t.Fatalf("root module ApplyResourceChange not called")
10133	} else if !pm.IsNull() {
10134		t.Fatalf("non-null ProviderMeta in root module ApplyResourceChange: %+v", pm)
10135	}
10136}
10137
10138func TestContext2Apply_ProviderMeta_plan_set(t *testing.T) {
10139	m := testModule(t, "provider-meta-set")
10140	p := testProvider("test")
10141	schema := p.ProviderSchema()
10142	schema.ProviderMeta = &configschema.Block{
10143		Attributes: map[string]*configschema.Attribute{
10144			"baz": {
10145				Type:     cty.String,
10146				Required: true,
10147			},
10148		},
10149	}
10150	prcPMs := map[string]cty.Value{}
10151	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
10152		prcPMs[req.TypeName] = req.ProviderMeta
10153		return providers.PlanResourceChangeResponse{
10154			PlannedState: req.ProposedNewState,
10155		}
10156	}
10157	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10158	ctx := testContext2(t, &ContextOpts{
10159		Config: m,
10160		Providers: map[addrs.Provider]providers.Factory{
10161			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10162		},
10163	})
10164
10165	_, diags := ctx.Plan()
10166	assertNoErrors(t, diags)
10167
10168	if !p.PlanResourceChangeCalled {
10169		t.Fatalf("PlanResourceChange not called")
10170	}
10171
10172	expectations := map[string]cty.Value{}
10173
10174	if pm, ok := prcPMs["test_resource"]; !ok {
10175		t.Fatalf("sub-module PlanResourceChange not called")
10176	} else if pm.IsNull() {
10177		t.Fatalf("null ProviderMeta in sub-module PlanResourceChange")
10178	} else {
10179		expectations["quux-submodule"] = pm
10180	}
10181
10182	if pm, ok := prcPMs["test_instance"]; !ok {
10183		t.Fatalf("root module PlanResourceChange not called")
10184	} else if pm.IsNull() {
10185		t.Fatalf("null ProviderMeta in root module PlanResourceChange")
10186	} else {
10187		expectations["quux"] = pm
10188	}
10189
10190	type metaStruct struct {
10191		Baz string `cty:"baz"`
10192	}
10193
10194	for expected, v := range expectations {
10195		var meta metaStruct
10196		err := gocty.FromCtyValue(v, &meta)
10197		if err != nil {
10198			t.Fatalf("Error parsing cty value: %s", err)
10199		}
10200		if meta.Baz != expected {
10201			t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
10202		}
10203	}
10204}
10205
10206func TestContext2Apply_ProviderMeta_plan_unset(t *testing.T) {
10207	m := testModule(t, "provider-meta-unset")
10208	p := testProvider("test")
10209	schema := p.ProviderSchema()
10210	schema.ProviderMeta = &configschema.Block{
10211		Attributes: map[string]*configschema.Attribute{
10212			"baz": {
10213				Type:     cty.String,
10214				Required: true,
10215			},
10216		},
10217	}
10218	prcPMs := map[string]cty.Value{}
10219	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
10220		prcPMs[req.TypeName] = req.ProviderMeta
10221		return providers.PlanResourceChangeResponse{
10222			PlannedState: req.ProposedNewState,
10223		}
10224	}
10225	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10226	ctx := testContext2(t, &ContextOpts{
10227		Config: m,
10228		Providers: map[addrs.Provider]providers.Factory{
10229			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10230		},
10231	})
10232
10233	_, diags := ctx.Plan()
10234	assertNoErrors(t, diags)
10235
10236	if !p.PlanResourceChangeCalled {
10237		t.Fatalf("PlanResourceChange not called")
10238	}
10239
10240	if pm, ok := prcPMs["test_resource"]; !ok {
10241		t.Fatalf("sub-module PlanResourceChange not called")
10242	} else if !pm.IsNull() {
10243		t.Fatalf("non-null ProviderMeta in sub-module PlanResourceChange: %+v", pm)
10244	}
10245
10246	if pm, ok := prcPMs["test_instance"]; !ok {
10247		t.Fatalf("root module PlanResourceChange not called")
10248	} else if !pm.IsNull() {
10249		t.Fatalf("non-null ProviderMeta in root module PlanResourceChange: %+v", pm)
10250	}
10251}
10252
10253func TestContext2Apply_ProviderMeta_plan_setNoSchema(t *testing.T) {
10254	m := testModule(t, "provider-meta-set")
10255	p := testProvider("test")
10256	p.PlanResourceChangeFn = testDiffFn
10257	ctx := testContext2(t, &ContextOpts{
10258		Config: m,
10259		Providers: map[addrs.Provider]providers.Factory{
10260			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10261		},
10262	})
10263
10264	_, diags := ctx.Plan()
10265	if !diags.HasErrors() {
10266		t.Fatalf("plan supposed to error, has no errors")
10267	}
10268
10269	var rootErr, subErr bool
10270	errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks"
10271	for _, diag := range diags {
10272		if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" {
10273			t.Errorf("Unexpected error: %+v", diag.Description())
10274		}
10275		switch diag.Description().Detail {
10276		case fmt.Sprintf(errorSummary, "instance"):
10277			rootErr = true
10278		case fmt.Sprintf(errorSummary, "resource"):
10279			subErr = true
10280		default:
10281			t.Errorf("Unexpected error: %s", diag.Description())
10282		}
10283	}
10284	if !rootErr {
10285		t.Errorf("Expected unsupported provider_meta block error for root module, none received")
10286	}
10287	if !subErr {
10288		t.Errorf("Expected unsupported provider_meta block error for sub-module, none received")
10289	}
10290}
10291
10292func TestContext2Apply_ProviderMeta_plan_setInvalid(t *testing.T) {
10293	m := testModule(t, "provider-meta-set")
10294	p := testProvider("test")
10295	p.PlanResourceChangeFn = testDiffFn
10296	schema := p.ProviderSchema()
10297	schema.ProviderMeta = &configschema.Block{
10298		Attributes: map[string]*configschema.Attribute{
10299			"quux": {
10300				Type:     cty.String,
10301				Required: true,
10302			},
10303		},
10304	}
10305	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10306	ctx := testContext2(t, &ContextOpts{
10307		Config: m,
10308		Providers: map[addrs.Provider]providers.Factory{
10309			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10310		},
10311	})
10312
10313	_, diags := ctx.Plan()
10314	if !diags.HasErrors() {
10315		t.Fatalf("plan supposed to error, has no errors")
10316	}
10317
10318	var reqErr, invalidErr bool
10319	for _, diag := range diags {
10320		switch diag.Description().Summary {
10321		case "Missing required argument":
10322			if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` {
10323				reqErr = true
10324			} else {
10325				t.Errorf("Unexpected error %+v", diag.Description())
10326			}
10327		case "Unsupported argument":
10328			if diag.Description().Detail == `An argument named "baz" is not expected here.` {
10329				invalidErr = true
10330			} else {
10331				t.Errorf("Unexpected error %+v", diag.Description())
10332			}
10333		default:
10334			t.Errorf("Unexpected error %+v", diag.Description())
10335		}
10336	}
10337	if !reqErr {
10338		t.Errorf("Expected missing required argument error, none received")
10339	}
10340	if !invalidErr {
10341		t.Errorf("Expected unsupported argument error, none received")
10342	}
10343}
10344
10345func TestContext2Apply_ProviderMeta_refresh_set(t *testing.T) {
10346	m := testModule(t, "provider-meta-set")
10347	p := testProvider("test")
10348	p.PlanResourceChangeFn = testDiffFn
10349	schema := p.ProviderSchema()
10350	schema.ProviderMeta = &configschema.Block{
10351		Attributes: map[string]*configschema.Attribute{
10352			"baz": {
10353				Type:     cty.String,
10354				Required: true,
10355			},
10356		},
10357	}
10358	rrcPMs := map[string]cty.Value{}
10359	p.ReadResourceFn = func(req providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
10360		rrcPMs[req.TypeName] = req.ProviderMeta
10361		newState, err := p.GetProviderSchemaResponse.ResourceTypes[req.TypeName].Block.CoerceValue(req.PriorState)
10362		if err != nil {
10363			panic(err)
10364		}
10365		resp.NewState = newState
10366		return resp
10367	}
10368	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10369	ctx := testContext2(t, &ContextOpts{
10370		Config: m,
10371		Providers: map[addrs.Provider]providers.Factory{
10372			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10373		},
10374	})
10375
10376	_, diags := ctx.Plan()
10377	assertNoErrors(t, diags)
10378
10379	_, diags = ctx.Apply()
10380	assertNoErrors(t, diags)
10381
10382	_, diags = ctx.Refresh()
10383	assertNoErrors(t, diags)
10384
10385	if !p.ReadResourceCalled {
10386		t.Fatalf("ReadResource not called")
10387	}
10388
10389	expectations := map[string]cty.Value{}
10390
10391	if pm, ok := rrcPMs["test_resource"]; !ok {
10392		t.Fatalf("sub-module ReadResource not called")
10393	} else if pm.IsNull() {
10394		t.Fatalf("null ProviderMeta in sub-module ReadResource")
10395	} else {
10396		expectations["quux-submodule"] = pm
10397	}
10398
10399	if pm, ok := rrcPMs["test_instance"]; !ok {
10400		t.Fatalf("root module ReadResource not called")
10401	} else if pm.IsNull() {
10402		t.Fatalf("null ProviderMeta in root module ReadResource")
10403	} else {
10404		expectations["quux"] = pm
10405	}
10406
10407	type metaStruct struct {
10408		Baz string `cty:"baz"`
10409	}
10410
10411	for expected, v := range expectations {
10412		var meta metaStruct
10413		err := gocty.FromCtyValue(v, &meta)
10414		if err != nil {
10415			t.Fatalf("Error parsing cty value: %s", err)
10416		}
10417		if meta.Baz != expected {
10418			t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
10419		}
10420	}
10421}
10422
10423func TestContext2Apply_ProviderMeta_refresh_setNoSchema(t *testing.T) {
10424	m := testModule(t, "provider-meta-set")
10425	p := testProvider("test")
10426	p.PlanResourceChangeFn = testDiffFn
10427
10428	// we need a schema for plan/apply so they don't error
10429	schema := p.ProviderSchema()
10430	schema.ProviderMeta = &configschema.Block{
10431		Attributes: map[string]*configschema.Attribute{
10432			"baz": {
10433				Type:     cty.String,
10434				Required: true,
10435			},
10436		},
10437	}
10438	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10439	ctx := testContext2(t, &ContextOpts{
10440		Config: m,
10441		Providers: map[addrs.Provider]providers.Factory{
10442			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10443		},
10444	})
10445
10446	_, diags := ctx.Plan()
10447	assertNoErrors(t, diags)
10448
10449	_, diags = ctx.Apply()
10450	assertNoErrors(t, diags)
10451
10452	// drop the schema before refresh, to test that it errors
10453	schema.ProviderMeta = nil
10454	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10455	ctx = testContext2(t, &ContextOpts{
10456		Config: m,
10457		Providers: map[addrs.Provider]providers.Factory{
10458			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10459		},
10460		State: ctx.State(),
10461	})
10462
10463	_, diags = ctx.Refresh()
10464	if !diags.HasErrors() {
10465		t.Fatalf("refresh supposed to error, has no errors")
10466	}
10467
10468	var rootErr, subErr bool
10469	errorSummary := "The resource test_%s.bar belongs to a provider that doesn't support provider_meta blocks"
10470	for _, diag := range diags {
10471		if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" {
10472			t.Errorf("Unexpected error: %+v", diag.Description())
10473		}
10474		switch diag.Description().Detail {
10475		case fmt.Sprintf(errorSummary, "instance"):
10476			rootErr = true
10477		case fmt.Sprintf(errorSummary, "resource"):
10478			subErr = true
10479		default:
10480			t.Errorf("Unexpected error: %s", diag.Description())
10481		}
10482	}
10483	if !rootErr {
10484		t.Errorf("Expected unsupported provider_meta block error for root module, none received")
10485	}
10486	if !subErr {
10487		t.Errorf("Expected unsupported provider_meta block error for sub-module, none received")
10488	}
10489}
10490
10491func TestContext2Apply_ProviderMeta_refresh_setInvalid(t *testing.T) {
10492	m := testModule(t, "provider-meta-set")
10493	p := testProvider("test")
10494	p.PlanResourceChangeFn = testDiffFn
10495
10496	// we need a matching schema for plan/apply so they don't error
10497	schema := p.ProviderSchema()
10498	schema.ProviderMeta = &configschema.Block{
10499		Attributes: map[string]*configschema.Attribute{
10500			"baz": {
10501				Type:     cty.String,
10502				Required: true,
10503			},
10504		},
10505	}
10506	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10507	ctx := testContext2(t, &ContextOpts{
10508		Config: m,
10509		Providers: map[addrs.Provider]providers.Factory{
10510			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10511		},
10512	})
10513
10514	_, diags := ctx.Plan()
10515	assertNoErrors(t, diags)
10516
10517	_, diags = ctx.Apply()
10518	assertNoErrors(t, diags)
10519
10520	// change the schema before refresh, to test that it errors
10521	schema.ProviderMeta = &configschema.Block{
10522		Attributes: map[string]*configschema.Attribute{
10523			"quux": {
10524				Type:     cty.String,
10525				Required: true,
10526			},
10527		},
10528	}
10529	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10530	ctx = testContext2(t, &ContextOpts{
10531		Config: m,
10532		Providers: map[addrs.Provider]providers.Factory{
10533			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10534		},
10535		State: ctx.State(),
10536	})
10537
10538	_, diags = ctx.Refresh()
10539	if !diags.HasErrors() {
10540		t.Fatalf("refresh supposed to error, has no errors")
10541	}
10542
10543	var reqErr, invalidErr bool
10544	for _, diag := range diags {
10545		switch diag.Description().Summary {
10546		case "Missing required argument":
10547			if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` {
10548				reqErr = true
10549			} else {
10550				t.Errorf("Unexpected error %+v", diag.Description())
10551			}
10552		case "Unsupported argument":
10553			if diag.Description().Detail == `An argument named "baz" is not expected here.` {
10554				invalidErr = true
10555			} else {
10556				t.Errorf("Unexpected error %+v", diag.Description())
10557			}
10558		default:
10559			t.Errorf("Unexpected error %+v", diag.Description())
10560		}
10561	}
10562	if !reqErr {
10563		t.Errorf("Expected missing required argument error, none received")
10564	}
10565	if !invalidErr {
10566		t.Errorf("Expected unsupported argument error, none received")
10567	}
10568}
10569
10570func TestContext2Apply_ProviderMeta_refreshdata_set(t *testing.T) {
10571	m := testModule(t, "provider-meta-data-set")
10572	p := testProvider("test")
10573	p.PlanResourceChangeFn = testDiffFn
10574	schema := p.ProviderSchema()
10575	schema.ProviderMeta = &configschema.Block{
10576		Attributes: map[string]*configschema.Attribute{
10577			"baz": {
10578				Type:     cty.String,
10579				Required: true,
10580			},
10581		},
10582	}
10583	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10584	ctx := testContext2(t, &ContextOpts{
10585		Config: m,
10586		Providers: map[addrs.Provider]providers.Factory{
10587			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10588		},
10589	})
10590	rdsPMs := map[string]cty.Value{}
10591	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
10592		rdsPMs[req.TypeName] = req.ProviderMeta
10593		switch req.TypeName {
10594		case "test_data_source":
10595			log.Printf("[TRACE] test_data_source RDSR returning")
10596			return providers.ReadDataSourceResponse{
10597				State: cty.ObjectVal(map[string]cty.Value{
10598					"id":  cty.StringVal("yo"),
10599					"foo": cty.StringVal("bar"),
10600				}),
10601			}
10602		case "test_file":
10603			log.Printf("[TRACE] test_file RDSR returning")
10604			return providers.ReadDataSourceResponse{
10605				State: cty.ObjectVal(map[string]cty.Value{
10606					"id":       cty.StringVal("bar"),
10607					"rendered": cty.StringVal("baz"),
10608					"template": cty.StringVal(""),
10609				}),
10610			}
10611		default:
10612			// config drift, oops
10613			log.Printf("[TRACE] unknown request TypeName: %q", req.TypeName)
10614			return providers.ReadDataSourceResponse{}
10615		}
10616	}
10617
10618	_, diags := ctx.Plan()
10619	assertNoErrors(t, diags)
10620
10621	_, diags = ctx.Apply()
10622	assertNoErrors(t, diags)
10623
10624	_, diags = ctx.Refresh()
10625	assertNoErrors(t, diags)
10626
10627	if !p.ReadDataSourceCalled {
10628		t.Fatalf("ReadDataSource not called")
10629	}
10630
10631	expectations := map[string]cty.Value{}
10632
10633	if pm, ok := rdsPMs["test_file"]; !ok {
10634		t.Fatalf("sub-module ReadDataSource not called")
10635	} else if pm.IsNull() {
10636		t.Fatalf("null ProviderMeta in sub-module ReadDataSource")
10637	} else {
10638		expectations["quux-submodule"] = pm
10639	}
10640
10641	if pm, ok := rdsPMs["test_data_source"]; !ok {
10642		t.Fatalf("root module ReadDataSource not called")
10643	} else if pm.IsNull() {
10644		t.Fatalf("null ProviderMeta in root module ReadDataSource")
10645	} else {
10646		expectations["quux"] = pm
10647	}
10648
10649	type metaStruct struct {
10650		Baz string `cty:"baz"`
10651	}
10652
10653	for expected, v := range expectations {
10654		var meta metaStruct
10655		err := gocty.FromCtyValue(v, &meta)
10656		if err != nil {
10657			t.Fatalf("Error parsing cty value: %s", err)
10658		}
10659		if meta.Baz != expected {
10660			t.Fatalf("Expected meta.Baz to be %q, got %q", expected, meta.Baz)
10661		}
10662	}
10663}
10664
10665func TestContext2Apply_ProviderMeta_refreshdata_unset(t *testing.T) {
10666	m := testModule(t, "provider-meta-data-unset")
10667	p := testProvider("test")
10668	p.PlanResourceChangeFn = testDiffFn
10669	schema := p.ProviderSchema()
10670	schema.ProviderMeta = &configschema.Block{
10671		Attributes: map[string]*configschema.Attribute{
10672			"baz": {
10673				Type:     cty.String,
10674				Required: true,
10675			},
10676		},
10677	}
10678	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10679	ctx := testContext2(t, &ContextOpts{
10680		Config: m,
10681		Providers: map[addrs.Provider]providers.Factory{
10682			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10683		},
10684	})
10685	rdsPMs := map[string]cty.Value{}
10686	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
10687		rdsPMs[req.TypeName] = req.ProviderMeta
10688		switch req.TypeName {
10689		case "test_data_source":
10690			return providers.ReadDataSourceResponse{
10691				State: cty.ObjectVal(map[string]cty.Value{
10692					"id":  cty.StringVal("yo"),
10693					"foo": cty.StringVal("bar"),
10694				}),
10695			}
10696		case "test_file":
10697			return providers.ReadDataSourceResponse{
10698				State: cty.ObjectVal(map[string]cty.Value{
10699					"id":       cty.StringVal("bar"),
10700					"rendered": cty.StringVal("baz"),
10701					"template": cty.StringVal(""),
10702				}),
10703			}
10704		default:
10705			// config drift, oops
10706			return providers.ReadDataSourceResponse{}
10707		}
10708	}
10709
10710	_, diags := ctx.Plan()
10711	assertNoErrors(t, diags)
10712
10713	_, diags = ctx.Apply()
10714	assertNoErrors(t, diags)
10715
10716	if !p.ReadDataSourceCalled {
10717		t.Fatalf("ReadDataSource not called")
10718	}
10719
10720	if pm, ok := rdsPMs["test_file"]; !ok {
10721		t.Fatalf("sub-module ReadDataSource not called")
10722	} else if !pm.IsNull() {
10723		t.Fatalf("non-null ProviderMeta in sub-module ReadDataSource")
10724	}
10725
10726	if pm, ok := rdsPMs["test_data_source"]; !ok {
10727		t.Fatalf("root module ReadDataSource not called")
10728	} else if !pm.IsNull() {
10729		t.Fatalf("non-null ProviderMeta in root module ReadDataSource")
10730	}
10731}
10732
10733func TestContext2Apply_ProviderMeta_refreshdata_setNoSchema(t *testing.T) {
10734	m := testModule(t, "provider-meta-data-set")
10735	p := testProvider("test")
10736	p.PlanResourceChangeFn = testDiffFn
10737	ctx := testContext2(t, &ContextOpts{
10738		Config: m,
10739		Providers: map[addrs.Provider]providers.Factory{
10740			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10741		},
10742	})
10743	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
10744		State: cty.ObjectVal(map[string]cty.Value{
10745			"id":  cty.StringVal("yo"),
10746			"foo": cty.StringVal("bar"),
10747		}),
10748	}
10749
10750	_, diags := ctx.Refresh()
10751	if !diags.HasErrors() {
10752		t.Fatalf("refresh supposed to error, has no errors")
10753	}
10754
10755	var rootErr, subErr bool
10756	errorSummary := "The resource data.test_%s.foo belongs to a provider that doesn't support provider_meta blocks"
10757	for _, diag := range diags {
10758		if diag.Description().Summary != "Provider registry.terraform.io/hashicorp/test doesn't support provider_meta" {
10759			t.Errorf("Unexpected error: %+v", diag.Description())
10760		}
10761		switch diag.Description().Detail {
10762		case fmt.Sprintf(errorSummary, "data_source"):
10763			rootErr = true
10764		case fmt.Sprintf(errorSummary, "file"):
10765			subErr = true
10766		default:
10767			t.Errorf("Unexpected error: %s", diag.Description())
10768		}
10769	}
10770	if !rootErr {
10771		t.Errorf("Expected unsupported provider_meta block error for root module, none received")
10772	}
10773	if !subErr {
10774		t.Errorf("Expected unsupported provider_meta block error for sub-module, none received")
10775	}
10776}
10777
10778func TestContext2Apply_ProviderMeta_refreshdata_setInvalid(t *testing.T) {
10779	m := testModule(t, "provider-meta-data-set")
10780	p := testProvider("test")
10781	p.PlanResourceChangeFn = testDiffFn
10782	schema := p.ProviderSchema()
10783	schema.ProviderMeta = &configschema.Block{
10784		Attributes: map[string]*configschema.Attribute{
10785			"quux": {
10786				Type:     cty.String,
10787				Required: true,
10788			},
10789		},
10790	}
10791	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schema)
10792	ctx := testContext2(t, &ContextOpts{
10793		Config: m,
10794		Providers: map[addrs.Provider]providers.Factory{
10795			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
10796		},
10797	})
10798	p.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
10799		State: cty.ObjectVal(map[string]cty.Value{
10800			"id":  cty.StringVal("yo"),
10801			"foo": cty.StringVal("bar"),
10802		}),
10803	}
10804
10805	_, diags := ctx.Refresh()
10806	if !diags.HasErrors() {
10807		t.Fatalf("refresh supposed to error, has no errors")
10808	}
10809
10810	var reqErr, invalidErr bool
10811	for _, diag := range diags {
10812		switch diag.Description().Summary {
10813		case "Missing required argument":
10814			if diag.Description().Detail == `The argument "quux" is required, but no definition was found.` {
10815				reqErr = true
10816			} else {
10817				t.Errorf("Unexpected error %+v", diag.Description())
10818			}
10819		case "Unsupported argument":
10820			if diag.Description().Detail == `An argument named "baz" is not expected here.` {
10821				invalidErr = true
10822			} else {
10823				t.Errorf("Unexpected error %+v", diag.Description())
10824			}
10825		default:
10826			t.Errorf("Unexpected error %+v", diag.Description())
10827		}
10828	}
10829	if !reqErr {
10830		t.Errorf("Expected missing required argument error, none received")
10831	}
10832	if !invalidErr {
10833		t.Errorf("Expected unsupported argument error, none received")
10834	}
10835}
10836
10837func TestContext2Apply_expandModuleVariables(t *testing.T) {
10838	m := testModuleInline(t, map[string]string{
10839		"main.tf": `
10840module "mod1" {
10841  for_each = toset(["a"])
10842  source = "./mod"
10843}
10844
10845module "mod2" {
10846  source = "./mod"
10847  in = module.mod1["a"].out
10848}
10849`,
10850		"mod/main.tf": `
10851resource "aws_instance" "foo" {
10852  foo = var.in
10853}
10854
10855variable "in" {
10856  type = string
10857  default = "default"
10858}
10859
10860output "out" {
10861  value = aws_instance.foo.id
10862}
10863`,
10864	})
10865
10866	p := testProvider("aws")
10867	p.PlanResourceChangeFn = testDiffFn
10868	p.ApplyResourceChangeFn = testApplyFn
10869	ctx := testContext2(t, &ContextOpts{
10870		Config: m,
10871		Providers: map[addrs.Provider]providers.Factory{
10872			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
10873		},
10874	})
10875
10876	_, diags := ctx.Plan()
10877	if diags.HasErrors() {
10878		t.Fatal(diags.ErrWithWarnings())
10879	}
10880
10881	state, diags := ctx.Apply()
10882	if diags.HasErrors() {
10883		t.Fatal(diags.ErrWithWarnings())
10884	}
10885
10886	expected := `<no state>
10887module.mod1["a"]:
10888  aws_instance.foo:
10889    ID = foo
10890    provider = provider["registry.terraform.io/hashicorp/aws"]
10891    foo = default
10892    type = aws_instance
10893
10894  Outputs:
10895
10896  out = foo
10897module.mod2:
10898  aws_instance.foo:
10899    ID = foo
10900    provider = provider["registry.terraform.io/hashicorp/aws"]
10901    foo = foo
10902    type = aws_instance
10903
10904    Dependencies:
10905      module.mod1.aws_instance.foo`
10906
10907	if state.String() != expected {
10908		t.Fatalf("expected:\n%s\ngot:\n%s\n", expected, state)
10909	}
10910}
10911
10912func TestContext2Apply_inheritAndStoreCBD(t *testing.T) {
10913	m := testModuleInline(t, map[string]string{
10914		"main.tf": `
10915resource "aws_instance" "foo" {
10916}
10917
10918resource "aws_instance" "cbd" {
10919  foo = aws_instance.foo.id
10920  lifecycle {
10921    create_before_destroy = true
10922  }
10923}
10924`,
10925	})
10926
10927	p := testProvider("aws")
10928	p.PlanResourceChangeFn = testDiffFn
10929	ctx := testContext2(t, &ContextOpts{
10930		Config: m,
10931		Providers: map[addrs.Provider]providers.Factory{
10932			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
10933		},
10934	})
10935
10936	_, diags := ctx.Plan()
10937	if diags.HasErrors() {
10938		t.Fatal(diags.ErrWithWarnings())
10939	}
10940
10941	state, diags := ctx.Apply()
10942	if diags.HasErrors() {
10943		t.Fatal(diags.ErrWithWarnings())
10944	}
10945
10946	foo := state.ResourceInstance(mustResourceInstanceAddr("aws_instance.foo"))
10947	if !foo.Current.CreateBeforeDestroy {
10948		t.Fatal("aws_instance.foo should also be create_before_destroy")
10949	}
10950}
10951
10952func TestContext2Apply_moduleDependsOn(t *testing.T) {
10953	m := testModule(t, "apply-module-depends-on")
10954
10955	p := testProvider("test")
10956
10957	// each instance being applied should happen in sequential order
10958	applied := int64(0)
10959
10960	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
10961		cfg := req.Config.AsValueMap()
10962		foo := cfg["foo"].AsString()
10963		ord := atomic.LoadInt64(&applied)
10964
10965		resp := providers.ReadDataSourceResponse{
10966			State: cty.ObjectVal(map[string]cty.Value{
10967				"id":  cty.StringVal("data"),
10968				"foo": cfg["foo"],
10969			}),
10970		}
10971
10972		if foo == "a" && ord < 4 {
10973			// due to data source "a"'s module depending on instance 4, this
10974			// should not be less than 4
10975			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source a read too early"))
10976		}
10977		if foo == "b" && ord < 1 {
10978			// due to data source "b"'s module depending on instance 1, this
10979			// should not be less than 1
10980			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source b read too early"))
10981		}
10982		return resp
10983	}
10984	p.PlanResourceChangeFn = testDiffFn
10985
10986	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
10987		state := req.PlannedState.AsValueMap()
10988		num, _ := state["num"].AsBigFloat().Float64()
10989		ord := int64(num)
10990		if !atomic.CompareAndSwapInt64(&applied, ord-1, ord) {
10991			actual := atomic.LoadInt64(&applied)
10992			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("instance %d was applied after %d", ord, actual))
10993		}
10994
10995		state["id"] = cty.StringVal(fmt.Sprintf("test_%d", ord))
10996		state["type"] = cty.StringVal("test_instance")
10997		resp.NewState = cty.ObjectVal(state)
10998
10999		return resp
11000	}
11001
11002	ctx := testContext2(t, &ContextOpts{
11003		Config: m,
11004		Providers: map[addrs.Provider]providers.Factory{
11005			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11006		},
11007	})
11008
11009	_, diags := ctx.Plan()
11010	if diags.HasErrors() {
11011		t.Fatal(diags.ErrWithWarnings())
11012	}
11013
11014	_, diags = ctx.Apply()
11015	if diags.HasErrors() {
11016		t.Fatal(diags.ErrWithWarnings())
11017	}
11018
11019	plan, diags := ctx.Plan()
11020	if diags.HasErrors() {
11021		t.Fatal(diags.ErrWithWarnings())
11022	}
11023
11024	for _, res := range plan.Changes.Resources {
11025		if res.Action != plans.NoOp {
11026			t.Fatalf("expected NoOp, got %s for %s", res.Action, res.Addr)
11027		}
11028	}
11029}
11030
11031func TestContext2Apply_moduleSelfReference(t *testing.T) {
11032	m := testModuleInline(t, map[string]string{
11033		"main.tf": `
11034module "test" {
11035  source = "./test"
11036
11037  a = module.test.b
11038}
11039
11040output "c" {
11041  value = module.test.c
11042}
11043`,
11044		"test/main.tf": `
11045variable "a" {}
11046
11047resource "test_instance" "test" {
11048}
11049
11050output "b" {
11051  value = test_instance.test.id
11052}
11053
11054output "c" {
11055  value = var.a
11056}`})
11057
11058	p := testProvider("test")
11059	p.PlanResourceChangeFn = testDiffFn
11060	ctx := testContext2(t, &ContextOpts{
11061		Config: m,
11062		Providers: map[addrs.Provider]providers.Factory{
11063			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11064		},
11065	})
11066
11067	_, diags := ctx.Plan()
11068	if diags.HasErrors() {
11069		t.Fatal(diags.ErrWithWarnings())
11070	}
11071
11072	_, diags = ctx.Apply()
11073	if diags.HasErrors() {
11074		t.Fatal(diags.ErrWithWarnings())
11075	}
11076
11077	ctx = testContext2(t, &ContextOpts{
11078		Config: m,
11079		Providers: map[addrs.Provider]providers.Factory{
11080			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11081		},
11082		PlanMode: plans.DestroyMode,
11083	})
11084
11085	_, diags = ctx.Plan()
11086	if diags.HasErrors() {
11087		t.Fatal(diags.ErrWithWarnings())
11088	}
11089
11090	state, diags := ctx.Apply()
11091	if diags.HasErrors() {
11092		t.Fatal(diags.ErrWithWarnings())
11093	}
11094
11095	if !state.Empty() {
11096		t.Fatal("expected empty state, got:", state)
11097	}
11098}
11099
11100func TestContext2Apply_moduleExpandDependsOn(t *testing.T) {
11101	m := testModuleInline(t, map[string]string{
11102		"main.tf": `
11103module "child" {
11104  count = 1
11105  source = "./child"
11106
11107  depends_on = [test_instance.a, test_instance.b]
11108}
11109
11110resource "test_instance" "a" {
11111}
11112
11113
11114resource "test_instance" "b" {
11115}
11116`,
11117		"child/main.tf": `
11118resource "test_instance" "foo" {
11119}
11120
11121output "myoutput" {
11122  value = "literal string"
11123}
11124`})
11125
11126	p := testProvider("test")
11127	p.PlanResourceChangeFn = testDiffFn
11128	ctx := testContext2(t, &ContextOpts{
11129		Config: m,
11130		Providers: map[addrs.Provider]providers.Factory{
11131			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11132		},
11133	})
11134
11135	_, diags := ctx.Plan()
11136	if diags.HasErrors() {
11137		t.Fatal(diags.ErrWithWarnings())
11138	}
11139
11140	state, diags := ctx.Apply()
11141	if diags.HasErrors() {
11142		t.Fatal(diags.ErrWithWarnings())
11143	}
11144
11145	ctx = testContext2(t, &ContextOpts{
11146		Config: m,
11147		Providers: map[addrs.Provider]providers.Factory{
11148			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11149		},
11150		PlanMode: plans.DestroyMode,
11151		State:    state,
11152	})
11153
11154	_, diags = ctx.Plan()
11155	if diags.HasErrors() {
11156		t.Fatal(diags.ErrWithWarnings())
11157	}
11158
11159	state, diags = ctx.Apply()
11160	if diags.HasErrors() {
11161		t.Fatal(diags.ErrWithWarnings())
11162	}
11163
11164	if !state.Empty() {
11165		t.Fatal("expected empty state, got:", state)
11166	}
11167}
11168
11169func TestContext2Apply_scaleInCBD(t *testing.T) {
11170	m := testModuleInline(t, map[string]string{
11171		"main.tf": `
11172variable "ct" {
11173  type = number
11174}
11175
11176resource "test_instance" "a" {
11177  count = var.ct
11178}
11179
11180resource "test_instance" "b" {
11181  require_new = local.removable
11182  lifecycle {
11183	create_before_destroy = true
11184  }
11185}
11186
11187resource "test_instance" "c" {
11188  require_new = test_instance.b.id
11189  lifecycle {
11190	create_before_destroy = true
11191  }
11192}
11193
11194output "out" {
11195  value = join(".", test_instance.a[*].id)
11196}
11197
11198locals {
11199  removable = join(".", test_instance.a[*].id)
11200}
11201`})
11202
11203	state := states.NewState()
11204	root := state.EnsureModule(addrs.RootModuleInstance)
11205	root.SetResourceInstanceCurrent(
11206		mustResourceInstanceAddr("test_instance.a[0]").Resource,
11207		&states.ResourceInstanceObjectSrc{
11208			Status:              states.ObjectReady,
11209			AttrsJSON:           []byte(`{"id":"a0"}`),
11210			Dependencies:        []addrs.ConfigResource{},
11211			CreateBeforeDestroy: true,
11212		},
11213		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
11214	)
11215	root.SetResourceInstanceCurrent(
11216		mustResourceInstanceAddr("test_instance.a[1]").Resource,
11217		&states.ResourceInstanceObjectSrc{
11218			Status:              states.ObjectReady,
11219			AttrsJSON:           []byte(`{"id":"a1"}`),
11220			Dependencies:        []addrs.ConfigResource{},
11221			CreateBeforeDestroy: true,
11222		},
11223		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
11224	)
11225	root.SetResourceInstanceCurrent(
11226		mustResourceInstanceAddr("test_instance.b").Resource,
11227		&states.ResourceInstanceObjectSrc{
11228			Status:              states.ObjectReady,
11229			AttrsJSON:           []byte(`{"id":"b", "require_new":"old.old"}`),
11230			Dependencies:        []addrs.ConfigResource{mustConfigResourceAddr("test_instance.a")},
11231			CreateBeforeDestroy: true,
11232		},
11233		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
11234	)
11235	root.SetResourceInstanceCurrent(
11236		mustResourceInstanceAddr("test_instance.c").Resource,
11237		&states.ResourceInstanceObjectSrc{
11238			Status:    states.ObjectReady,
11239			AttrsJSON: []byte(`{"id":"c", "require_new":"b"}`),
11240			Dependencies: []addrs.ConfigResource{
11241				mustConfigResourceAddr("test_instance.a"),
11242				mustConfigResourceAddr("test_instance.b"),
11243			},
11244			CreateBeforeDestroy: true,
11245		},
11246		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
11247	)
11248
11249	p := testProvider("test")
11250
11251	p.PlanResourceChangeFn = func(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
11252		n := r.ProposedNewState.AsValueMap()
11253
11254		if r.PriorState.IsNull() {
11255			n["id"] = cty.UnknownVal(cty.String)
11256			resp.PlannedState = cty.ObjectVal(n)
11257			return resp
11258		}
11259
11260		p := r.PriorState.AsValueMap()
11261
11262		priorRN := p["require_new"]
11263		newRN := n["require_new"]
11264
11265		if eq := priorRN.Equals(newRN); !eq.IsKnown() || eq.False() {
11266			resp.RequiresReplace = []cty.Path{{cty.GetAttrStep{Name: "require_new"}}}
11267			n["id"] = cty.UnknownVal(cty.String)
11268		}
11269
11270		resp.PlannedState = cty.ObjectVal(n)
11271		return resp
11272	}
11273
11274	// reduce the count to 1
11275	ctx := testContext2(t, &ContextOpts{
11276		Variables: InputValues{
11277			"ct": &InputValue{
11278				Value:      cty.NumberIntVal(1),
11279				SourceType: ValueFromCaller,
11280			},
11281		},
11282		Config: m,
11283		Providers: map[addrs.Provider]providers.Factory{
11284			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11285		},
11286		State: state,
11287	})
11288
11289	_, diags := ctx.Plan()
11290	if diags.HasErrors() {
11291		t.Fatal(diags.ErrWithWarnings())
11292	}
11293
11294	state, diags = ctx.Apply()
11295	if diags.HasErrors() {
11296		log.Fatal(diags.ErrWithWarnings())
11297	}
11298
11299	// check the output, as those can't cause an error planning the value
11300	out := state.RootModule().OutputValues["out"].Value.AsString()
11301	if out != "a0" {
11302		t.Fatalf(`expected output "a0", got: %q`, out)
11303	}
11304
11305	// reduce the count to 0
11306	ctx = testContext2(t, &ContextOpts{
11307		Variables: InputValues{
11308			"ct": &InputValue{
11309				Value:      cty.NumberIntVal(0),
11310				SourceType: ValueFromCaller,
11311			},
11312		},
11313		Config: m,
11314		Providers: map[addrs.Provider]providers.Factory{
11315			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11316		},
11317		State: state,
11318	})
11319
11320	_, diags = ctx.Plan()
11321	if diags.HasErrors() {
11322		t.Fatal(diags.ErrWithWarnings())
11323	}
11324
11325	state, diags = ctx.Apply()
11326	if diags.HasErrors() {
11327		t.Fatal(diags.ErrWithWarnings())
11328	}
11329
11330	// check the output, as those can't cause an error planning the value
11331	out = state.RootModule().OutputValues["out"].Value.AsString()
11332	if out != "" {
11333		t.Fatalf(`expected output "", got: %q`, out)
11334	}
11335}
11336
11337// Ensure that we can destroy when a provider references a resource that will
11338// also be destroyed
11339func TestContext2Apply_destroyProviderReference(t *testing.T) {
11340	m := testModuleInline(t, map[string]string{
11341		"main.tf": `
11342provider "null" {
11343  value = ""
11344}
11345
11346module "mod" {
11347  source = "./mod"
11348}
11349
11350provider "test" {
11351  value = module.mod.output
11352}
11353
11354resource "test_instance" "bar" {
11355}
11356`,
11357		"mod/main.tf": `
11358data "null_data_source" "foo" {
11359       count = 1
11360}
11361
11362
11363output "output" {
11364  value = data.null_data_source.foo[0].output
11365}
11366`})
11367
11368	schemaFn := func(name string) *ProviderSchema {
11369		return &ProviderSchema{
11370			Provider: &configschema.Block{
11371				Attributes: map[string]*configschema.Attribute{
11372					"value": {
11373						Type:     cty.String,
11374						Required: true,
11375					},
11376				},
11377			},
11378			ResourceTypes: map[string]*configschema.Block{
11379				name + "_instance": {
11380					Attributes: map[string]*configschema.Attribute{
11381						"id": {
11382							Type:     cty.String,
11383							Computed: true,
11384						},
11385						"foo": {
11386							Type:     cty.String,
11387							Optional: true,
11388						},
11389					},
11390				},
11391			},
11392			DataSources: map[string]*configschema.Block{
11393				name + "_data_source": {
11394					Attributes: map[string]*configschema.Attribute{
11395						"id": {
11396							Type:     cty.String,
11397							Computed: true,
11398						},
11399						"output": {
11400							Type:     cty.String,
11401							Computed: true,
11402						},
11403					},
11404				},
11405			},
11406		}
11407	}
11408
11409	testP := new(MockProvider)
11410	testP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
11411		return providers.ReadResourceResponse{NewState: req.PriorState}
11412	}
11413	testP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("test"))
11414
11415	providerConfig := ""
11416	testP.ConfigureProviderFn = func(req providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
11417		value := req.Config.GetAttr("value")
11418		if value.IsKnown() && !value.IsNull() {
11419			providerConfig = value.AsString()
11420		} else {
11421			providerConfig = ""
11422		}
11423		return resp
11424	}
11425	testP.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
11426		if providerConfig != "valid" {
11427			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("provider config is %q", providerConfig))
11428			return
11429		}
11430		return testApplyFn(req)
11431	}
11432	testP.PlanResourceChangeFn = testDiffFn
11433
11434	nullP := new(MockProvider)
11435	nullP.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
11436		return providers.ReadResourceResponse{NewState: req.PriorState}
11437	}
11438	nullP.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(schemaFn("null"))
11439
11440	nullP.ApplyResourceChangeFn = testApplyFn
11441	nullP.PlanResourceChangeFn = testDiffFn
11442
11443	nullP.ReadDataSourceResponse = &providers.ReadDataSourceResponse{
11444		State: cty.ObjectVal(map[string]cty.Value{
11445			"id":     cty.StringVal("ID"),
11446			"output": cty.StringVal("valid"),
11447		}),
11448	}
11449
11450	ctx := testContext2(t, &ContextOpts{
11451		Config: m,
11452		Providers: map[addrs.Provider]providers.Factory{
11453			addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP),
11454			addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP),
11455		},
11456	})
11457
11458	if _, diags := ctx.Plan(); diags.HasErrors() {
11459		t.Fatalf("plan errors: %s", diags.Err())
11460	}
11461
11462	state, diags := ctx.Apply()
11463	if diags.HasErrors() {
11464		t.Fatalf("apply errors: %s", diags.Err())
11465	}
11466
11467	ctx = testContext2(t, &ContextOpts{
11468		Config: m,
11469		Providers: map[addrs.Provider]providers.Factory{
11470			addrs.NewDefaultProvider("test"): testProviderFuncFixed(testP),
11471			addrs.NewDefaultProvider("null"): testProviderFuncFixed(nullP),
11472		},
11473
11474		State:    state,
11475		PlanMode: plans.DestroyMode,
11476	})
11477
11478	if _, diags := ctx.Plan(); diags.HasErrors() {
11479		t.Fatalf("destroy plan errors: %s", diags.Err())
11480	}
11481
11482	if _, diags := ctx.Apply(); diags.HasErrors() {
11483		t.Fatalf("destroy apply errors: %s", diags.Err())
11484	}
11485}
11486
11487// Destroying properly requires pruning out all unneeded config nodes to
11488// prevent incorrect expansion evaluation.
11489func TestContext2Apply_destroyInterModuleExpansion(t *testing.T) {
11490	m := testModuleInline(t, map[string]string{
11491		"main.tf": `
11492data "test_data_source" "a" {
11493  for_each = {
11494    one = "thing"
11495  }
11496}
11497
11498locals {
11499  module_input = {
11500    for k, v in data.test_data_source.a : k => v.id
11501  }
11502}
11503
11504module "mod1" {
11505  source = "./mod"
11506  input = local.module_input
11507}
11508
11509module "mod2" {
11510  source = "./mod"
11511  input = module.mod1.outputs
11512}
11513
11514resource "test_instance" "bar" {
11515  for_each = module.mod2.outputs
11516}
11517
11518output "module_output" {
11519  value = module.mod2.outputs
11520}
11521output "test_instances" {
11522  value = test_instance.bar
11523}
11524`,
11525		"mod/main.tf": `
11526variable "input" {
11527}
11528
11529data "test_data_source" "foo" {
11530  for_each = var.input
11531}
11532
11533output "outputs" {
11534  value = data.test_data_source.foo
11535}
11536`})
11537
11538	p := testProvider("test")
11539	p.PlanResourceChangeFn = testDiffFn
11540	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
11541		return providers.ReadDataSourceResponse{
11542			State: cty.ObjectVal(map[string]cty.Value{
11543				"id":  cty.StringVal("data_source"),
11544				"foo": cty.StringVal("output"),
11545			}),
11546		}
11547	}
11548
11549	ctx := testContext2(t, &ContextOpts{
11550		Config: m,
11551		Providers: map[addrs.Provider]providers.Factory{
11552			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11553		},
11554	})
11555
11556	if _, diags := ctx.Plan(); diags.HasErrors() {
11557		t.Fatalf("plan errors: %s", diags.Err())
11558	}
11559
11560	state, diags := ctx.Apply()
11561	if diags.HasErrors() {
11562		t.Fatalf("apply errors: %s", diags.Err())
11563	}
11564
11565	destroy := func() {
11566		ctx = testContext2(t, &ContextOpts{
11567			Config: m,
11568			Providers: map[addrs.Provider]providers.Factory{
11569				addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11570			},
11571
11572			State:    state,
11573			PlanMode: plans.DestroyMode,
11574		})
11575
11576		if _, diags := ctx.Plan(); diags.HasErrors() {
11577			t.Fatalf("destroy plan errors: %s", diags.Err())
11578		}
11579
11580		state, diags = ctx.Apply()
11581		if diags.HasErrors() {
11582			t.Fatalf("destroy apply errors: %s", diags.Err())
11583		}
11584	}
11585
11586	destroy()
11587	// Destroying again from the empty state should not cause any errors either
11588	destroy()
11589}
11590
11591func TestContext2Apply_createBeforeDestroyWithModule(t *testing.T) {
11592	m := testModuleInline(t, map[string]string{
11593		"main.tf": `
11594variable "v" {}
11595
11596module "mod" {
11597    source = "./mod"
11598    in = var.v
11599}
11600
11601resource "test_resource" "a" {
11602  value = var.v
11603  depends_on = [module.mod]
11604  lifecycle {
11605    create_before_destroy = true
11606  }
11607}
11608`,
11609		"mod/main.tf": `
11610variable "in" {}
11611
11612resource "test_resource" "a" {
11613  value = var.in
11614}
11615`})
11616
11617	p := testProvider("test")
11618	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
11619		proposed := req.ProposedNewState.AsValueMap()
11620		proposed["id"] = cty.UnknownVal(cty.String)
11621		return providers.PlanResourceChangeResponse{
11622			PlannedState:    cty.ObjectVal(proposed),
11623			RequiresReplace: []cty.Path{cty.Path{cty.GetAttrStep{Name: "value"}}},
11624		}
11625	}
11626
11627	ctx := testContext2(t, &ContextOpts{
11628		Config: m,
11629		Providers: map[addrs.Provider]providers.Factory{
11630			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11631		},
11632		Variables: InputValues{
11633			"v": &InputValue{
11634				Value: cty.StringVal("A"),
11635			},
11636		},
11637	})
11638
11639	if _, diags := ctx.Plan(); diags.HasErrors() {
11640		t.Fatalf("plan errors: %s", diags.Err())
11641	}
11642
11643	state, diags := ctx.Apply()
11644	if diags.HasErrors() {
11645		t.Fatalf("apply errors: %s", diags.Err())
11646	}
11647
11648	ctx = testContext2(t, &ContextOpts{
11649		Config: m,
11650		Providers: map[addrs.Provider]providers.Factory{
11651			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11652		},
11653		Variables: InputValues{
11654			"v": &InputValue{
11655				Value: cty.StringVal("B"),
11656			},
11657		},
11658		State: state,
11659	})
11660
11661	if _, diags := ctx.Plan(); diags.HasErrors() {
11662		t.Fatalf("plan errors: %s", diags.Err())
11663	}
11664
11665	_, diags = ctx.Apply()
11666	if diags.HasErrors() {
11667		t.Fatalf("apply errors: %s", diags.Err())
11668	}
11669}
11670
11671func TestContext2Apply_forcedCBD(t *testing.T) {
11672	m := testModuleInline(t, map[string]string{
11673		"main.tf": `
11674variable "v" {}
11675
11676resource "test_instance" "a" {
11677  require_new = var.v
11678}
11679
11680resource "test_instance" "b" {
11681  depends_on = [test_instance.a]
11682  lifecycle {
11683    create_before_destroy = true
11684  }
11685}
11686`})
11687
11688	p := testProvider("test")
11689	p.PlanResourceChangeFn = testDiffFn
11690
11691	ctx := testContext2(t, &ContextOpts{
11692		Config: m,
11693		Providers: map[addrs.Provider]providers.Factory{
11694			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11695		},
11696		Variables: InputValues{
11697			"v": &InputValue{
11698				Value: cty.StringVal("A"),
11699			},
11700		},
11701	})
11702
11703	if _, diags := ctx.Plan(); diags.HasErrors() {
11704		t.Fatalf("plan errors: %s", diags.Err())
11705	}
11706
11707	state, diags := ctx.Apply()
11708	if diags.HasErrors() {
11709		t.Fatalf("apply errors: %s", diags.Err())
11710	}
11711
11712	ctx = testContext2(t, &ContextOpts{
11713		Config: m,
11714		Providers: map[addrs.Provider]providers.Factory{
11715			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11716		},
11717		Variables: InputValues{
11718			"v": &InputValue{
11719				Value: cty.StringVal("B"),
11720			},
11721		},
11722		State: state,
11723	})
11724
11725	if _, diags := ctx.Plan(); diags.HasErrors() {
11726		t.Fatalf("plan errors: %s", diags.Err())
11727	}
11728
11729	_, diags = ctx.Apply()
11730	if diags.HasErrors() {
11731		t.Fatalf("apply errors: %s", diags.Err())
11732	}
11733}
11734
11735func TestContext2Apply_removeReferencedResource(t *testing.T) {
11736	m := testModuleInline(t, map[string]string{
11737		"main.tf": `
11738variable "ct" {
11739}
11740
11741resource "test_resource" "to_remove" {
11742  count = var.ct
11743}
11744
11745resource "test_resource" "c" {
11746  value = join("", test_resource.to_remove[*].id)
11747}
11748`})
11749
11750	p := testProvider("test")
11751	p.PlanResourceChangeFn = testDiffFn
11752
11753	ctx := testContext2(t, &ContextOpts{
11754		Config: m,
11755		Providers: map[addrs.Provider]providers.Factory{
11756			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11757		},
11758		Variables: InputValues{
11759			"ct": &InputValue{
11760				Value: cty.NumberIntVal(1),
11761			},
11762		},
11763	})
11764
11765	if _, diags := ctx.Plan(); diags.HasErrors() {
11766		t.Fatalf("plan errors: %s", diags.Err())
11767	}
11768
11769	state, diags := ctx.Apply()
11770	if diags.HasErrors() {
11771		t.Fatalf("apply errors: %s", diags.Err())
11772	}
11773
11774	ctx = testContext2(t, &ContextOpts{
11775		Config: m,
11776		Providers: map[addrs.Provider]providers.Factory{
11777			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11778		},
11779		Variables: InputValues{
11780			"ct": &InputValue{
11781				Value: cty.NumberIntVal(0),
11782			},
11783		},
11784		State: state,
11785	})
11786
11787	if _, diags := ctx.Plan(); diags.HasErrors() {
11788		t.Fatalf("plan errors: %s", diags.Err())
11789	}
11790
11791	_, diags = ctx.Apply()
11792	if diags.HasErrors() {
11793		t.Fatalf("apply errors: %s", diags.Err())
11794	}
11795}
11796
11797func TestContext2Apply_variableSensitivity(t *testing.T) {
11798	m := testModuleInline(t, map[string]string{
11799		"main.tf": `
11800variable "sensitive_var" {
11801	default = "foo"
11802	sensitive = true
11803}
11804
11805variable "sensitive_id" {
11806	default = "secret id"
11807	sensitive = true
11808}
11809
11810resource "test_resource" "foo" {
11811	value   = var.sensitive_var
11812
11813	network_interface {
11814		network_interface_id = var.sensitive_id
11815	}
11816}`,
11817	})
11818
11819	p := new(MockProvider)
11820	p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
11821		return providers.ReadResourceResponse{NewState: req.PriorState}
11822	}
11823	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
11824		Provider: &configschema.Block{},
11825		ResourceTypes: map[string]*configschema.Block{
11826			"test_resource": {
11827				Attributes: map[string]*configschema.Attribute{
11828					"id": {
11829						Type:     cty.String,
11830						Computed: true,
11831					},
11832					"value": {
11833						Type:     cty.String,
11834						Optional: true,
11835						Computed: true,
11836					},
11837				},
11838				BlockTypes: map[string]*configschema.NestedBlock{
11839					"network_interface": {
11840						Block: configschema.Block{
11841							Attributes: map[string]*configschema.Attribute{
11842								"network_interface_id": {Type: cty.String, Optional: true},
11843								"device_index":         {Type: cty.Number, Optional: true},
11844							},
11845						},
11846						Nesting: configschema.NestingSet,
11847					},
11848				},
11849			},
11850		},
11851	})
11852	p.PlanResourceChangeFn = testDiffFn
11853
11854	ctx := testContext2(t, &ContextOpts{
11855		Config: m,
11856		Providers: map[addrs.Provider]providers.Factory{
11857			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11858		},
11859	})
11860
11861	if _, diags := ctx.Plan(); diags.HasErrors() {
11862		t.Fatalf("plan errors: %s", diags.Err())
11863	}
11864
11865	state, diags := ctx.Apply()
11866	if diags.HasErrors() {
11867		t.Fatalf("apply errors: %s", diags.Err())
11868	}
11869
11870	// Run a second apply with no changes
11871	ctx = testContext2(t, &ContextOpts{
11872		Config: m,
11873		Providers: map[addrs.Provider]providers.Factory{
11874			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11875		},
11876		State: state,
11877	})
11878
11879	if _, diags := ctx.Plan(); diags.HasErrors() {
11880		t.Fatalf("plan errors: %s", diags.Err())
11881	}
11882
11883	state, diags = ctx.Apply()
11884	if diags.HasErrors() {
11885		t.Fatalf("apply errors: %s", diags.Err())
11886	}
11887
11888	// Now change the variable value for sensitive_var
11889	ctx = testContext2(t, &ContextOpts{
11890		Config: m,
11891		Providers: map[addrs.Provider]providers.Factory{
11892			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11893		},
11894		Variables: InputValues{
11895			"sensitive_var": &InputValue{
11896				Value: cty.StringVal("bar"),
11897			},
11898		},
11899		State: state,
11900	})
11901
11902	if _, diags := ctx.Plan(); diags.HasErrors() {
11903		t.Fatalf("plan errors: %s", diags.Err())
11904	}
11905
11906	_, diags = ctx.Apply()
11907	if diags.HasErrors() {
11908		t.Fatalf("apply errors: %s", diags.Err())
11909	}
11910}
11911
11912func TestContext2Apply_variableSensitivityPropagation(t *testing.T) {
11913	m := testModuleInline(t, map[string]string{
11914		"main.tf": `
11915variable "sensitive_map" {
11916	type = map(string)
11917	default = {
11918		"x" = "foo"
11919	}
11920	sensitive = true
11921}
11922
11923resource "test_resource" "foo" {
11924	value = var.sensitive_map.x
11925}
11926`,
11927	})
11928
11929	p := testProvider("test")
11930	p.PlanResourceChangeFn = testDiffFn
11931
11932	ctx := testContext2(t, &ContextOpts{
11933		Config: m,
11934		Providers: map[addrs.Provider]providers.Factory{
11935			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
11936		},
11937	})
11938
11939	plan, diags := ctx.Plan()
11940	if diags.HasErrors() {
11941		t.Fatalf("plan errors: %s", diags.Err())
11942	}
11943
11944	verifySensitiveValue := func(pvms []cty.PathValueMarks) {
11945		if len(pvms) != 1 {
11946			t.Fatalf("expected 1 sensitive path, got %d", len(pvms))
11947		}
11948		pvm := pvms[0]
11949		if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) {
11950			t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath)
11951		}
11952		if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) {
11953			t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks)
11954		}
11955	}
11956
11957	addr := mustResourceInstanceAddr("test_resource.foo")
11958	fooChangeSrc := plan.Changes.ResourceInstance(addr)
11959	verifySensitiveValue(fooChangeSrc.AfterValMarks)
11960
11961	state, diags := ctx.Apply()
11962	if diags.HasErrors() {
11963		t.Fatalf("apply errors: %s", diags.Err())
11964	}
11965
11966	fooState := state.ResourceInstance(addr)
11967	verifySensitiveValue(fooState.Current.AttrSensitivePaths)
11968}
11969
11970func TestContext2Apply_variableSensitivityProviders(t *testing.T) {
11971	m := testModuleInline(t, map[string]string{
11972		"main.tf": `
11973resource "test_resource" "foo" {
11974	sensitive_value = "should get marked"
11975}
11976
11977resource "test_resource" "bar" {
11978	value  = test_resource.foo.sensitive_value
11979	random = test_resource.foo.id # not sensitive
11980
11981	nesting_single {
11982		value           = "abc"
11983		sensitive_value = "xyz"
11984	}
11985}
11986
11987resource "test_resource" "baz" {
11988	value = test_resource.bar.nesting_single.sensitive_value
11989}
11990`,
11991	})
11992
11993	p := testProvider("test")
11994	p.PlanResourceChangeFn = testDiffFn
11995
11996	ctx := testContext2(t, &ContextOpts{
11997		Config: m,
11998		Providers: map[addrs.Provider]providers.Factory{
11999			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12000		},
12001	})
12002
12003	plan, diags := ctx.Plan()
12004	if diags.HasErrors() {
12005		t.Fatalf("plan errors: %s", diags.Err())
12006	}
12007
12008	verifySensitiveValue := func(pvms []cty.PathValueMarks) {
12009		if len(pvms) != 1 {
12010			t.Fatalf("expected 1 sensitive path, got %d", len(pvms))
12011		}
12012		pvm := pvms[0]
12013		if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) {
12014			t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath)
12015		}
12016		if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) {
12017			t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks)
12018		}
12019	}
12020
12021	// Sensitive attributes (defined by the provider) are marked
12022	// as sensitive when referenced from another resource
12023	// "bar" references sensitive resources in "foo"
12024	barAddr := mustResourceInstanceAddr("test_resource.bar")
12025	barChangeSrc := plan.Changes.ResourceInstance(barAddr)
12026	verifySensitiveValue(barChangeSrc.AfterValMarks)
12027
12028	bazAddr := mustResourceInstanceAddr("test_resource.baz")
12029	bazChangeSrc := plan.Changes.ResourceInstance(bazAddr)
12030	verifySensitiveValue(bazChangeSrc.AfterValMarks)
12031
12032	state, diags := ctx.Apply()
12033	if diags.HasErrors() {
12034		t.Fatalf("apply errors: %s", diags.Err())
12035	}
12036
12037	barState := state.ResourceInstance(barAddr)
12038	verifySensitiveValue(barState.Current.AttrSensitivePaths)
12039
12040	bazState := state.ResourceInstance(bazAddr)
12041	verifySensitiveValue(bazState.Current.AttrSensitivePaths)
12042}
12043
12044func TestContext2Apply_variableSensitivityChange(t *testing.T) {
12045	m := testModuleInline(t, map[string]string{
12046		"main.tf": `
12047variable "sensitive_var" {
12048	default = "hello"
12049	sensitive = true
12050}
12051
12052resource "test_resource" "foo" {
12053	value = var.sensitive_var
12054}`,
12055	})
12056
12057	p := testProvider("test")
12058	p.PlanResourceChangeFn = testDiffFn
12059
12060	ctx := testContext2(t, &ContextOpts{
12061		Config: m,
12062		Providers: map[addrs.Provider]providers.Factory{
12063			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12064		},
12065		State: states.BuildState(func(s *states.SyncState) {
12066			s.SetResourceInstanceCurrent(
12067				addrs.Resource{
12068					Mode: addrs.ManagedResourceMode,
12069					Type: "test_resource",
12070					Name: "foo",
12071				}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
12072				&states.ResourceInstanceObjectSrc{
12073					Status:    states.ObjectReady,
12074					AttrsJSON: []byte(`{"id":"foo", "value":"hello"}`),
12075					// No AttrSensitivePaths present
12076				},
12077				addrs.AbsProviderConfig{
12078					Provider: addrs.NewDefaultProvider("test"),
12079					Module:   addrs.RootModule,
12080				},
12081			)
12082		}),
12083	})
12084
12085	_, diags := ctx.Plan()
12086	assertNoErrors(t, diags)
12087
12088	addr := mustResourceInstanceAddr("test_resource.foo")
12089
12090	state, diags := ctx.Apply()
12091	assertNoErrors(t, diags)
12092
12093	fooState := state.ResourceInstance(addr)
12094
12095	if len(fooState.Current.AttrSensitivePaths) != 1 {
12096		t.Fatalf("wrong number of sensitive paths, expected 1, got, %v", len(fooState.Current.AttrSensitivePaths))
12097	}
12098	got := fooState.Current.AttrSensitivePaths[0]
12099	want := cty.PathValueMarks{
12100		Path:  cty.GetAttrPath("value"),
12101		Marks: cty.NewValueMarks("sensitive"),
12102	}
12103
12104	if !got.Equal(want) {
12105		t.Fatalf("wrong value marks; got:\n%#v\n\nwant:\n%#v\n", got, want)
12106	}
12107
12108	m2 := testModuleInline(t, map[string]string{
12109		"main.tf": `
12110variable "sensitive_var" {
12111	default = "hello"
12112	sensitive = false
12113}
12114
12115resource "test_resource" "foo" {
12116	value = var.sensitive_var
12117}`,
12118	})
12119
12120	ctx2 := testContext2(t, &ContextOpts{
12121		Config: m2,
12122		Providers: map[addrs.Provider]providers.Factory{
12123			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12124		},
12125		State: state,
12126	})
12127
12128	_, diags = ctx2.Plan()
12129	assertNoErrors(t, diags)
12130
12131	stateWithoutSensitive, diags := ctx.Apply()
12132	assertNoErrors(t, diags)
12133
12134	fooState2 := stateWithoutSensitive.ResourceInstance(addr)
12135	if len(fooState2.Current.AttrSensitivePaths) > 0 {
12136		t.Fatalf("wrong number of sensitive paths, expected 0, got, %v", len(fooState2.Current.AttrSensitivePaths))
12137	}
12138}
12139
12140func TestContext2Apply_moduleVariableOptionalAttributes(t *testing.T) {
12141	m := testModuleInline(t, map[string]string{
12142		"main.tf": `
12143terraform {
12144  experiments = [module_variable_optional_attrs]
12145}
12146
12147variable "in" {
12148  type = object({
12149	required = string
12150	optional = optional(string)
12151  })
12152}
12153
12154output "out" {
12155  value = var.in
12156}
12157`})
12158
12159	ctx := testContext2(t, &ContextOpts{
12160		Variables: InputValues{
12161			"in": &InputValue{
12162				Value: cty.MapVal(map[string]cty.Value{
12163					"required": cty.StringVal("boop"),
12164				}),
12165				SourceType: ValueFromCaller,
12166			},
12167		},
12168		Config: m,
12169	})
12170
12171	_, diags := ctx.Plan()
12172	if diags.HasErrors() {
12173		t.Fatal(diags.ErrWithWarnings())
12174	}
12175
12176	state, diags := ctx.Apply()
12177	if diags.HasErrors() {
12178		t.Fatal(diags.ErrWithWarnings())
12179	}
12180
12181	got := state.RootModule().OutputValues["out"].Value
12182	want := cty.ObjectVal(map[string]cty.Value{
12183		"required": cty.StringVal("boop"),
12184
12185		// Because "optional" was marked as optional, it got silently filled
12186		// in as a null value of string type rather than returning an error.
12187		"optional": cty.NullVal(cty.String),
12188	})
12189	if !want.RawEquals(got) {
12190		t.Fatalf("wrong result\ngot:  %#v\nwant: %#v", got, want)
12191	}
12192}
12193
12194func TestContext2Apply_provisionerSensitive(t *testing.T) {
12195	m := testModule(t, "apply-provisioner-sensitive")
12196	p := testProvider("aws")
12197
12198	pr := testProvisioner()
12199	pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
12200		if req.Config.ContainsMarked() {
12201			t.Fatalf("unexpectedly marked config value: %#v", req.Config)
12202		}
12203		command := req.Config.GetAttr("command")
12204		if command.IsMarked() {
12205			t.Fatalf("unexpectedly marked command argument: %#v", command.Marks())
12206		}
12207		req.UIOutput.Output(fmt.Sprintf("Executing: %q", command.AsString()))
12208		return
12209	}
12210	p.PlanResourceChangeFn = testDiffFn
12211	p.ApplyResourceChangeFn = testApplyFn
12212
12213	h := new(MockHook)
12214	ctx := testContext2(t, &ContextOpts{
12215		Config: m,
12216		Hooks:  []Hook{h},
12217		Providers: map[addrs.Provider]providers.Factory{
12218			addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
12219		},
12220		Provisioners: map[string]provisioners.Factory{
12221			"shell": testProvisionerFuncFixed(pr),
12222		},
12223		Variables: InputValues{
12224			"password": &InputValue{
12225				Value:      cty.StringVal("secret"),
12226				SourceType: ValueFromCaller,
12227			},
12228		},
12229	})
12230
12231	if _, diags := ctx.Plan(); diags.HasErrors() {
12232		logDiagnostics(t, diags)
12233		t.Fatal("plan failed")
12234	}
12235
12236	// "restart" provisioner
12237	pr.CloseCalled = false
12238
12239	state, diags := ctx.Apply()
12240	if diags.HasErrors() {
12241		logDiagnostics(t, diags)
12242		t.Fatal("apply failed")
12243	}
12244
12245	actual := strings.TrimSpace(state.String())
12246	expected := strings.TrimSpace(testTerraformApplyProvisionerSensitiveStr)
12247	if actual != expected {
12248		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
12249	}
12250
12251	// Verify apply was invoked
12252	if !pr.ProvisionResourceCalled {
12253		t.Fatalf("provisioner was not called on apply")
12254	}
12255
12256	// Verify output was suppressed
12257	if !h.ProvisionOutputCalled {
12258		t.Fatalf("ProvisionOutput hook not called")
12259	}
12260	if got, doNotWant := h.ProvisionOutputMessage, "secret"; strings.Contains(got, doNotWant) {
12261		t.Errorf("sensitive value %q included in output:\n%s", doNotWant, got)
12262	}
12263	if got, want := h.ProvisionOutputMessage, "output suppressed"; !strings.Contains(got, want) {
12264		t.Errorf("expected hook to be called with %q, but was:\n%s", want, got)
12265	}
12266}
12267
12268func TestContext2Apply_warnings(t *testing.T) {
12269	m := testModuleInline(t, map[string]string{
12270		"main.tf": `
12271resource "test_resource" "foo" {
12272}`,
12273	})
12274
12275	p := testProvider("test")
12276	p.PlanResourceChangeFn = testDiffFn
12277
12278	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
12279		resp := testApplyFn(req)
12280
12281		resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warning"))
12282		return resp
12283	}
12284
12285	ctx := testContext2(t, &ContextOpts{
12286		Config: m,
12287		Providers: map[addrs.Provider]providers.Factory{
12288			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12289		},
12290	})
12291
12292	if _, diags := ctx.Plan(); diags.HasErrors() {
12293		t.Fatalf("plan errors: %s", diags.Err())
12294	}
12295
12296	state, diags := ctx.Apply()
12297	if diags.HasErrors() {
12298		t.Fatalf("diags: %s", diags.Err())
12299	}
12300
12301	inst := state.ResourceInstance(mustResourceInstanceAddr("test_resource.foo"))
12302	if inst == nil {
12303		t.Fatal("missing 'test_resource.foo' in state:", state)
12304	}
12305}
12306
12307func TestContext2Apply_rpcDiagnostics(t *testing.T) {
12308	m := testModuleInline(t, map[string]string{
12309		"main.tf": `
12310resource "test_instance" "a" {
12311}
12312`,
12313	})
12314
12315	p := testProvider("test")
12316	p.PlanResourceChangeFn = testDiffFn
12317	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
12318		resp = testApplyFn(req)
12319		resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("don't frobble"))
12320		return resp
12321	}
12322
12323	p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{
12324		ResourceTypes: map[string]*configschema.Block{
12325			"test_instance": {
12326				Attributes: map[string]*configschema.Attribute{
12327					"id": {Type: cty.String, Computed: true},
12328				},
12329			},
12330		},
12331	})
12332
12333	ctx := testContext2(t, &ContextOpts{
12334		Config: m,
12335		Providers: map[addrs.Provider]providers.Factory{
12336			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12337		},
12338	})
12339	_, diags := ctx.Plan()
12340	if diags.HasErrors() {
12341		t.Fatal(diags.Err())
12342	}
12343
12344	_, diags = ctx.Apply()
12345	if diags.HasErrors() {
12346		t.Fatal(diags.Err())
12347	}
12348
12349	if len(diags) == 0 {
12350		t.Fatal("expected warnings")
12351	}
12352
12353	for _, d := range diags {
12354		des := d.Description().Summary
12355		if !strings.Contains(des, "frobble") {
12356			t.Fatalf(`expected frobble, got %q`, des)
12357		}
12358	}
12359}
12360
12361func TestContext2Apply_dataSensitive(t *testing.T) {
12362	m := testModule(t, "apply-data-sensitive")
12363	p := testProvider("null")
12364	p.PlanResourceChangeFn = testDiffFn
12365	p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
12366		// add the required id
12367		m := req.Config.AsValueMap()
12368		m["id"] = cty.StringVal("foo")
12369
12370		return providers.ReadDataSourceResponse{
12371			State: cty.ObjectVal(m),
12372		}
12373	}
12374
12375	ctx := testContext2(t, &ContextOpts{
12376		Config: m,
12377		Providers: map[addrs.Provider]providers.Factory{
12378			addrs.NewDefaultProvider("null"): testProviderFuncFixed(p),
12379		},
12380	})
12381
12382	if p, diags := ctx.Plan(); diags.HasErrors() {
12383		t.Fatalf("diags: %s", diags.Err())
12384	} else {
12385		t.Logf(legacyDiffComparisonString(p.Changes))
12386	}
12387
12388	state, diags := ctx.Apply()
12389	assertNoErrors(t, diags)
12390
12391	addr := mustResourceInstanceAddr("data.null_data_source.testing")
12392
12393	dataSourceState := state.ResourceInstance(addr)
12394	pvms := dataSourceState.Current.AttrSensitivePaths
12395	if len(pvms) != 1 {
12396		t.Fatalf("expected 1 sensitive path, got %d", len(pvms))
12397	}
12398	pvm := pvms[0]
12399	if gotPath, wantPath := pvm.Path, cty.GetAttrPath("foo"); !gotPath.Equals(wantPath) {
12400		t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath)
12401	}
12402	if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) {
12403		t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks)
12404	}
12405}
12406
12407func TestContext2Apply_errorRestorePrivateData(t *testing.T) {
12408	// empty config to remove our resource
12409	m := testModuleInline(t, map[string]string{
12410		"main.tf": "",
12411	})
12412
12413	p := simpleMockProvider()
12414	p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{
12415		// we error during apply, which will trigger core to preserve the last
12416		// known state, including private data
12417		Diagnostics: tfdiags.Diagnostics(nil).Append(errors.New("oops")),
12418	}
12419
12420	addr := mustResourceInstanceAddr("test_object.a")
12421
12422	state := states.BuildState(func(s *states.SyncState) {
12423		s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{
12424			Status:    states.ObjectReady,
12425			AttrsJSON: []byte(`{"id":"foo"}`),
12426			Private:   []byte("private"),
12427		}, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`))
12428	})
12429
12430	ctx := testContext2(t, &ContextOpts{
12431		Config: m,
12432		State:  state,
12433		Providers: map[addrs.Provider]providers.Factory{
12434			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12435		},
12436	})
12437
12438	_, diags := ctx.Plan()
12439	if diags.HasErrors() {
12440		t.Fatal(diags.Err())
12441	}
12442
12443	state, _ = ctx.Apply()
12444	if string(state.ResourceInstance(addr).Current.Private) != "private" {
12445		t.Fatal("missing private data in state")
12446	}
12447}
12448
12449func TestContext2Apply_errorRestoreStatus(t *testing.T) {
12450	// empty config to remove our resource
12451	m := testModuleInline(t, map[string]string{
12452		"main.tf": "",
12453	})
12454
12455	p := simpleMockProvider()
12456	p.ApplyResourceChangeFn = func(req providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
12457		// We error during apply, but return the current object state.
12458		resp.Diagnostics = resp.Diagnostics.Append(errors.New("oops"))
12459		// return a warning too to make sure it isn't dropped
12460		resp.Diagnostics = resp.Diagnostics.Append(tfdiags.SimpleWarning("warned"))
12461		resp.NewState = req.PriorState
12462		resp.Private = req.PlannedPrivate
12463		return resp
12464	}
12465
12466	addr := mustResourceInstanceAddr("test_object.a")
12467
12468	state := states.BuildState(func(s *states.SyncState) {
12469		s.SetResourceInstanceCurrent(addr, &states.ResourceInstanceObjectSrc{
12470			Status:       states.ObjectTainted,
12471			AttrsJSON:    []byte(`{"test_string":"foo"}`),
12472			Private:      []byte("private"),
12473			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.b")},
12474		}, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`))
12475	})
12476
12477	ctx := testContext2(t, &ContextOpts{
12478		Config: m,
12479		State:  state,
12480		Providers: map[addrs.Provider]providers.Factory{
12481			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12482		},
12483	})
12484
12485	_, diags := ctx.Plan()
12486	if diags.HasErrors() {
12487		t.Fatal(diags.Err())
12488	}
12489
12490	state, diags = ctx.Apply()
12491
12492	errString := diags.ErrWithWarnings().Error()
12493	if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") {
12494		t.Fatalf("error missing expected info: %q", errString)
12495	}
12496
12497	if len(diags) != 2 {
12498		t.Fatalf("expected 1 error and 1 warning, got: %q", errString)
12499	}
12500
12501	res := state.ResourceInstance(addr)
12502	if res == nil {
12503		t.Fatal("resource was removed from state")
12504	}
12505
12506	if res.Current.Status != states.ObjectTainted {
12507		t.Fatal("resource should still be tainted in the state")
12508	}
12509
12510	if len(res.Current.Dependencies) != 1 || !res.Current.Dependencies[0].Equal(mustConfigResourceAddr("test_object.b")) {
12511		t.Fatalf("incorrect dependencies, got %q", res.Current.Dependencies)
12512	}
12513
12514	if string(res.Current.Private) != "private" {
12515		t.Fatalf("incorrect private data, got %q", res.Current.Private)
12516	}
12517}
12518
12519func TestContext2Apply_nonConformingResponse(t *testing.T) {
12520	// empty config to remove our resource
12521	m := testModuleInline(t, map[string]string{
12522		"main.tf": `
12523resource "test_object" "a" {
12524  test_string = "x"
12525}
12526`,
12527	})
12528
12529	p := simpleMockProvider()
12530	respDiags := tfdiags.Diagnostics(nil).Append(tfdiags.SimpleWarning("warned"))
12531	respDiags = respDiags.Append(errors.New("oops"))
12532	p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{
12533		// Don't lose these diagnostics
12534		Diagnostics: respDiags,
12535		// This state is missing required attributes, and should produce an error
12536		NewState: cty.ObjectVal(map[string]cty.Value{
12537			"test_string": cty.StringVal("x"),
12538		}),
12539	}
12540
12541	ctx := testContext2(t, &ContextOpts{
12542		Config: m,
12543		Providers: map[addrs.Provider]providers.Factory{
12544			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12545		},
12546	})
12547
12548	_, diags := ctx.Plan()
12549	if diags.HasErrors() {
12550		t.Fatal(diags.Err())
12551	}
12552
12553	_, diags = ctx.Apply()
12554	errString := diags.ErrWithWarnings().Error()
12555	if !strings.Contains(errString, "oops") || !strings.Contains(errString, "warned") {
12556		t.Fatalf("error missing expected info: %q", errString)
12557	}
12558
12559	// we should have more than the ones returned from the provider, and they
12560	// should not be coalesced into a single value
12561	if len(diags) < 3 {
12562		t.Fatalf("incorrect diagnostics, got %d values with %s", len(diags), diags.ErrWithWarnings())
12563	}
12564}
12565
12566func TestContext2Apply_nilResponse(t *testing.T) {
12567	// empty config to remove our resource
12568	m := testModuleInline(t, map[string]string{
12569		"main.tf": `
12570resource "test_object" "a" {
12571}
12572`,
12573	})
12574
12575	p := simpleMockProvider()
12576	p.ApplyResourceChangeResponse = &providers.ApplyResourceChangeResponse{}
12577
12578	ctx := testContext2(t, &ContextOpts{
12579		Config: m,
12580		Providers: map[addrs.Provider]providers.Factory{
12581			addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
12582		},
12583	})
12584
12585	_, diags := ctx.Plan()
12586	if diags.HasErrors() {
12587		t.Fatal(diags.Err())
12588	}
12589
12590	_, diags = ctx.Apply()
12591	if !diags.HasErrors() {
12592		t.Fatal("expected and error")
12593	}
12594
12595	errString := diags.ErrWithWarnings().Error()
12596	if !strings.Contains(errString, "invalid nil value") {
12597		t.Fatalf("error missing expected info: %q", errString)
12598	}
12599}
12600
12601////////////////////////////////////////////////////////////////////////////////
12602// NOTE: Due to the size of this file, new tests should be added to
12603// context_apply2_test.go.
12604////////////////////////////////////////////////////////////////////////////////
12605