diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index e9cb4a1d4..7d9e59bcb 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -6370,3 +6370,41 @@ data "test_data_source" "d" { t.Fatal("expected data.test_data_source.d to be fully read in refreshed state, got status", d.Current.Status) } } + +func TestContext2Plan_dataReferencesResource(t *testing.T) { + p := testProvider("test") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + + p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) { + resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("data source should not be read")) + return resp + } + + m := testModuleInline(t, map[string]string{ + "main.tf": ` +locals { + x = "value" +} + +resource "test_resource" "a" { + value = local.x +} + +// test_resource.a.value can be resolved during plan, but the reference implies +// that the data source should wait until the resource is created. +data "test_data_source" "d" { + foo = test_resource.a.value +} +`}) + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan() + assertNoErrors(t, diags) +} diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index 5c1771052..44aae0fc8 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -46,6 +46,7 @@ type GraphNodeAttachDependencies interface { // graphNodeDependsOn is implemented by resources that need to expose any // references set via DependsOn in their configuration. type graphNodeDependsOn interface { + GraphNodeReferencer DependsOn() []*addrs.Reference } @@ -327,6 +328,31 @@ func (m ReferenceMap) dependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Ve refs := depender.DependsOn() + // For data sources we implicitly treat references as depends_on entries. + // If a data source references a resource, even if that reference is + // resolvable, it stands to reason that the user intends for the data + // source to require that resource in some way. + if n, ok := depender.(GraphNodeConfigResource); ok && + n.ResourceAddr().Resource.Mode == addrs.DataResourceMode { + for _, r := range depender.References() { + + // We don't need to wait on referenced data sources. They have no + // side effects, so our configuration reference should suffice for + // proper ordering. + var resAddr addrs.Resource + switch s := r.Subject.(type) { + case addrs.Resource: + resAddr = s + case addrs.ResourceInstance: + resAddr = s.Resource + } + + if resAddr.Mode == addrs.ManagedResourceMode { + refs = append(refs, r) + } + } + } + // This is where we record that a module has depends_on configured. if _, ok := depender.(*nodeExpandModule); ok && len(refs) > 0 { fromModule = true