github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/node_resource_validate.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/hashicorp/terraform/internal/addrs"
     9  	"github.com/hashicorp/terraform/internal/configs"
    10  	"github.com/hashicorp/terraform/internal/configs/configschema"
    11  	"github.com/hashicorp/terraform/internal/didyoumean"
    12  	"github.com/hashicorp/terraform/internal/instances"
    13  	"github.com/hashicorp/terraform/internal/lang"
    14  	"github.com/hashicorp/terraform/internal/providers"
    15  	"github.com/hashicorp/terraform/internal/provisioners"
    16  	"github.com/hashicorp/terraform/internal/tfdiags"
    17  	"github.com/zclconf/go-cty/cty"
    18  )
    19  
    20  // NodeValidatableResource represents a resource that is used for validation
    21  // only.
    22  type NodeValidatableResource struct {
    23  	*NodeAbstractResource
    24  }
    25  
    26  var (
    27  	_ GraphNodeModuleInstance            = (*NodeValidatableResource)(nil)
    28  	_ GraphNodeExecutable                = (*NodeValidatableResource)(nil)
    29  	_ GraphNodeReferenceable             = (*NodeValidatableResource)(nil)
    30  	_ GraphNodeReferencer                = (*NodeValidatableResource)(nil)
    31  	_ GraphNodeConfigResource            = (*NodeValidatableResource)(nil)
    32  	_ GraphNodeAttachResourceConfig      = (*NodeValidatableResource)(nil)
    33  	_ GraphNodeAttachProviderMetaConfigs = (*NodeValidatableResource)(nil)
    34  )
    35  
    36  func (n *NodeValidatableResource) Path() addrs.ModuleInstance {
    37  	// There is no expansion during validation, so we evaluate everything as
    38  	// single module instances.
    39  	return n.Addr.Module.UnkeyedInstanceShim()
    40  }
    41  
    42  // GraphNodeEvalable
    43  func (n *NodeValidatableResource) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
    44  	diags = diags.Append(n.validateResource(ctx))
    45  
    46  	diags = diags.Append(n.validateCheckRules(ctx, n.Config))
    47  
    48  	if managed := n.Config.Managed; managed != nil {
    49  		// Validate all the provisioners
    50  		for _, p := range managed.Provisioners {
    51  			if p.Connection == nil {
    52  				p.Connection = n.Config.Managed.Connection
    53  			} else if n.Config.Managed.Connection != nil {
    54  				p.Connection.Config = configs.MergeBodies(n.Config.Managed.Connection.Config, p.Connection.Config)
    55  			}
    56  
    57  			// Validate Provisioner Config
    58  			diags = diags.Append(n.validateProvisioner(ctx, p))
    59  			if diags.HasErrors() {
    60  				return diags
    61  			}
    62  		}
    63  	}
    64  	return diags
    65  }
    66  
    67  // validateProvisioner validates the configuration of a provisioner belonging to
    68  // a resource. The provisioner config is expected to contain the merged
    69  // connection configurations.
    70  func (n *NodeValidatableResource) validateProvisioner(ctx EvalContext, p *configs.Provisioner) tfdiags.Diagnostics {
    71  	var diags tfdiags.Diagnostics
    72  
    73  	provisioner, err := ctx.Provisioner(p.Type)
    74  	if err != nil {
    75  		diags = diags.Append(err)
    76  		return diags
    77  	}
    78  
    79  	if provisioner == nil {
    80  		return diags.Append(fmt.Errorf("provisioner %s not initialized", p.Type))
    81  	}
    82  	provisionerSchema, err := ctx.ProvisionerSchema(p.Type)
    83  	if err != nil {
    84  		return diags.Append(fmt.Errorf("failed to read schema for provisioner %s: %s", p.Type, err))
    85  	}
    86  	if provisionerSchema == nil {
    87  		return diags.Append(fmt.Errorf("provisioner %s has no schema", p.Type))
    88  	}
    89  
    90  	// Validate the provisioner's own config first
    91  	configVal, _, configDiags := n.evaluateBlock(ctx, p.Config, provisionerSchema)
    92  	diags = diags.Append(configDiags)
    93  
    94  	if configVal == cty.NilVal {
    95  		// Should never happen for a well-behaved EvaluateBlock implementation
    96  		return diags.Append(fmt.Errorf("EvaluateBlock returned nil value"))
    97  	}
    98  
    99  	// Use unmarked value for validate request
   100  	unmarkedConfigVal, _ := configVal.UnmarkDeep()
   101  	req := provisioners.ValidateProvisionerConfigRequest{
   102  		Config: unmarkedConfigVal,
   103  	}
   104  
   105  	resp := provisioner.ValidateProvisionerConfig(req)
   106  	diags = diags.Append(resp.Diagnostics)
   107  
   108  	if p.Connection != nil {
   109  		// We can't comprehensively validate the connection config since its
   110  		// final structure is decided by the communicator and we can't instantiate
   111  		// that until we have a complete instance state. However, we *can* catch
   112  		// configuration keys that are not valid for *any* communicator, catching
   113  		// typos early rather than waiting until we actually try to run one of
   114  		// the resource's provisioners.
   115  		_, _, connDiags := n.evaluateBlock(ctx, p.Connection.Config, connectionBlockSupersetSchema)
   116  		diags = diags.Append(connDiags)
   117  	}
   118  	return diags
   119  }
   120  
   121  func (n *NodeValidatableResource) evaluateBlock(ctx EvalContext, body hcl.Body, schema *configschema.Block) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
   122  	keyData, selfAddr := n.stubRepetitionData(n.Config.Count != nil, n.Config.ForEach != nil)
   123  
   124  	return ctx.EvaluateBlock(body, schema, selfAddr, keyData)
   125  }
   126  
   127  // connectionBlockSupersetSchema is a schema representing the superset of all
   128  // possible arguments for "connection" blocks across all supported connection
   129  // types.
   130  //
   131  // This currently lives here because we've not yet updated our communicator
   132  // subsystem to be aware of schema itself. Once that is done, we can remove
   133  // this and use a type-specific schema from the communicator to validate
   134  // exactly what is expected for a given connection type.
   135  var connectionBlockSupersetSchema = &configschema.Block{
   136  	Attributes: map[string]*configschema.Attribute{
   137  		// NOTE: "type" is not included here because it's treated special
   138  		// by the config loader and stored away in a separate field.
   139  
   140  		// Common attributes for both connection types
   141  		"host": {
   142  			Type:     cty.String,
   143  			Required: true,
   144  		},
   145  		"type": {
   146  			Type:     cty.String,
   147  			Optional: true,
   148  		},
   149  		"user": {
   150  			Type:     cty.String,
   151  			Optional: true,
   152  		},
   153  		"password": {
   154  			Type:     cty.String,
   155  			Optional: true,
   156  		},
   157  		"port": {
   158  			Type:     cty.Number,
   159  			Optional: true,
   160  		},
   161  		"timeout": {
   162  			Type:     cty.String,
   163  			Optional: true,
   164  		},
   165  		"script_path": {
   166  			Type:     cty.String,
   167  			Optional: true,
   168  		},
   169  		// For type=ssh only (enforced in ssh communicator)
   170  		"target_platform": {
   171  			Type:     cty.String,
   172  			Optional: true,
   173  		},
   174  		"private_key": {
   175  			Type:     cty.String,
   176  			Optional: true,
   177  		},
   178  		"certificate": {
   179  			Type:     cty.String,
   180  			Optional: true,
   181  		},
   182  		"host_key": {
   183  			Type:     cty.String,
   184  			Optional: true,
   185  		},
   186  		"agent": {
   187  			Type:     cty.Bool,
   188  			Optional: true,
   189  		},
   190  		"agent_identity": {
   191  			Type:     cty.String,
   192  			Optional: true,
   193  		},
   194  		"proxy_scheme": {
   195  			Type:     cty.String,
   196  			Optional: true,
   197  		},
   198  		"proxy_host": {
   199  			Type:     cty.String,
   200  			Optional: true,
   201  		},
   202  		"proxy_port": {
   203  			Type:     cty.Number,
   204  			Optional: true,
   205  		},
   206  		"proxy_user_name": {
   207  			Type:     cty.String,
   208  			Optional: true,
   209  		},
   210  		"proxy_user_password": {
   211  			Type:     cty.String,
   212  			Optional: true,
   213  		},
   214  		"bastion_host": {
   215  			Type:     cty.String,
   216  			Optional: true,
   217  		},
   218  		"bastion_host_key": {
   219  			Type:     cty.String,
   220  			Optional: true,
   221  		},
   222  		"bastion_port": {
   223  			Type:     cty.Number,
   224  			Optional: true,
   225  		},
   226  		"bastion_user": {
   227  			Type:     cty.String,
   228  			Optional: true,
   229  		},
   230  		"bastion_password": {
   231  			Type:     cty.String,
   232  			Optional: true,
   233  		},
   234  		"bastion_private_key": {
   235  			Type:     cty.String,
   236  			Optional: true,
   237  		},
   238  		"bastion_certificate": {
   239  			Type:     cty.String,
   240  			Optional: true,
   241  		},
   242  
   243  		// For type=winrm only (enforced in winrm communicator)
   244  		"https": {
   245  			Type:     cty.Bool,
   246  			Optional: true,
   247  		},
   248  		"insecure": {
   249  			Type:     cty.Bool,
   250  			Optional: true,
   251  		},
   252  		"cacert": {
   253  			Type:     cty.String,
   254  			Optional: true,
   255  		},
   256  		"use_ntlm": {
   257  			Type:     cty.Bool,
   258  			Optional: true,
   259  		},
   260  	},
   261  }
   262  
   263  func (n *NodeValidatableResource) validateResource(ctx EvalContext) tfdiags.Diagnostics {
   264  	var diags tfdiags.Diagnostics
   265  
   266  	provider, providerSchema, err := getProvider(ctx, n.ResolvedProvider)
   267  	diags = diags.Append(err)
   268  	if diags.HasErrors() {
   269  		return diags
   270  	}
   271  	if providerSchema == nil {
   272  		diags = diags.Append(fmt.Errorf("validateResource has nil schema for %s", n.Addr))
   273  		return diags
   274  	}
   275  
   276  	keyData := EvalDataForNoInstanceKey
   277  
   278  	switch {
   279  	case n.Config.Count != nil:
   280  		// If the config block has count, we'll evaluate with an unknown
   281  		// number as count.index so we can still type check even though
   282  		// we won't expand count until the plan phase.
   283  		keyData = InstanceKeyEvalData{
   284  			CountIndex: cty.UnknownVal(cty.Number),
   285  		}
   286  
   287  		// Basic type-checking of the count argument. More complete validation
   288  		// of this will happen when we DynamicExpand during the plan walk.
   289  		countDiags := validateCount(ctx, n.Config.Count)
   290  		diags = diags.Append(countDiags)
   291  
   292  	case n.Config.ForEach != nil:
   293  		keyData = InstanceKeyEvalData{
   294  			EachKey:   cty.UnknownVal(cty.String),
   295  			EachValue: cty.UnknownVal(cty.DynamicPseudoType),
   296  		}
   297  
   298  		// Evaluate the for_each expression here so we can expose the diagnostics
   299  		forEachDiags := validateForEach(ctx, n.Config.ForEach)
   300  		diags = diags.Append(forEachDiags)
   301  	}
   302  
   303  	diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn))
   304  
   305  	// Validate the provider_meta block for the provider this resource
   306  	// belongs to, if there is one.
   307  	//
   308  	// Note: this will return an error for every resource a provider
   309  	// uses in a module, if the provider_meta for that module is
   310  	// incorrect. The only way to solve this that we've found is to
   311  	// insert a new ProviderMeta graph node in the graph, and make all
   312  	// that provider's resources in the module depend on the node. That's
   313  	// an awful heavy hammer to swing for this feature, which should be
   314  	// used only in limited cases with heavy coordination with the
   315  	// Terraform team, so we're going to defer that solution for a future
   316  	// enhancement to this functionality.
   317  	/*
   318  		if n.ProviderMetas != nil {
   319  			if m, ok := n.ProviderMetas[n.ProviderAddr.ProviderConfig.Type]; ok && m != nil {
   320  				// if the provider doesn't support this feature, throw an error
   321  				if (*n.ProviderSchema).ProviderMeta == nil {
   322  					diags = diags.Append(&hcl.Diagnostic{
   323  						Severity: hcl.DiagError,
   324  						Summary:  fmt.Sprintf("Provider %s doesn't support provider_meta", cfg.ProviderConfigAddr()),
   325  						Detail:   fmt.Sprintf("The resource %s belongs to a provider that doesn't support provider_meta blocks", n.Addr),
   326  						Subject:  &m.ProviderRange,
   327  					})
   328  				} else {
   329  					_, _, metaDiags := ctx.EvaluateBlock(m.Config, (*n.ProviderSchema).ProviderMeta, nil, EvalDataForNoInstanceKey)
   330  					diags = diags.Append(metaDiags)
   331  				}
   332  			}
   333  		}
   334  	*/
   335  	// BUG(paddy): we're not validating provider_meta blocks on EvalValidate right now
   336  	// because the ProviderAddr for the resource isn't available on the EvalValidate
   337  	// struct.
   338  
   339  	// Provider entry point varies depending on resource mode, because
   340  	// managed resources and data resources are two distinct concepts
   341  	// in the provider abstraction.
   342  	switch n.Config.Mode {
   343  	case addrs.ManagedResourceMode:
   344  		schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
   345  		if schema == nil {
   346  			var suggestion string
   347  			if dSchema, _ := providerSchema.SchemaForResourceType(addrs.DataResourceMode, n.Config.Type); dSchema != nil {
   348  				suggestion = fmt.Sprintf("\n\nDid you intend to use the data source %q? If so, declare this using a \"data\" block instead of a \"resource\" block.", n.Config.Type)
   349  			} else if len(providerSchema.ResourceTypes) > 0 {
   350  				suggestions := make([]string, 0, len(providerSchema.ResourceTypes))
   351  				for name := range providerSchema.ResourceTypes {
   352  					suggestions = append(suggestions, name)
   353  				}
   354  				if suggestion = didyoumean.NameSuggestion(n.Config.Type, suggestions); suggestion != "" {
   355  					suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   356  				}
   357  			}
   358  
   359  			diags = diags.Append(&hcl.Diagnostic{
   360  				Severity: hcl.DiagError,
   361  				Summary:  "Invalid resource type",
   362  				Detail:   fmt.Sprintf("The provider %s does not support resource type %q.%s", n.Provider().ForDisplay(), n.Config.Type, suggestion),
   363  				Subject:  &n.Config.TypeRange,
   364  			})
   365  			return diags
   366  		}
   367  
   368  		configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData)
   369  		diags = diags.Append(valDiags)
   370  		if valDiags.HasErrors() {
   371  			return diags
   372  		}
   373  
   374  		if n.Config.Managed != nil { // can be nil only in tests with poorly-configured mocks
   375  			for _, traversal := range n.Config.Managed.IgnoreChanges {
   376  				// validate the ignore_changes traversals apply.
   377  				moreDiags := schema.StaticValidateTraversal(traversal)
   378  				diags = diags.Append(moreDiags)
   379  
   380  				// ignore_changes cannot be used for Computed attributes,
   381  				// unless they are also Optional.
   382  				// If the traversal was valid, convert it to a cty.Path and
   383  				// use that to check whether the Attribute is Computed and
   384  				// non-Optional.
   385  				if !diags.HasErrors() {
   386  					path := traversalToPath(traversal)
   387  
   388  					attrSchema := schema.AttributeByPath(path)
   389  
   390  					if attrSchema != nil && !attrSchema.Optional && attrSchema.Computed {
   391  						// ignore_changes uses absolute traversal syntax in config despite
   392  						// using relative traversals, so we strip the leading "." added by
   393  						// FormatCtyPath for a better error message.
   394  						attrDisplayPath := strings.TrimPrefix(tfdiags.FormatCtyPath(path), ".")
   395  
   396  						diags = diags.Append(&hcl.Diagnostic{
   397  							Severity: hcl.DiagWarning,
   398  							Summary:  "Redundant ignore_changes element",
   399  							Detail:   fmt.Sprintf("Adding an attribute name to ignore_changes tells Terraform to ignore future changes to the argument in configuration after the object has been created, retaining the value originally configured.\n\nThe attribute %s is decided by the provider alone and therefore there can be no configured value to compare with. Including this attribute in ignore_changes has no effect. Remove the attribute from ignore_changes to quiet this warning.", attrDisplayPath),
   400  							Subject:  &n.Config.TypeRange,
   401  						})
   402  					}
   403  				}
   404  			}
   405  		}
   406  
   407  		// Use unmarked value for validate request
   408  		unmarkedConfigVal, _ := configVal.UnmarkDeep()
   409  		req := providers.ValidateResourceConfigRequest{
   410  			TypeName: n.Config.Type,
   411  			Config:   unmarkedConfigVal,
   412  		}
   413  
   414  		resp := provider.ValidateResourceConfig(req)
   415  		diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
   416  
   417  	case addrs.DataResourceMode:
   418  		schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
   419  		if schema == nil {
   420  			var suggestion string
   421  			if dSchema, _ := providerSchema.SchemaForResourceType(addrs.ManagedResourceMode, n.Config.Type); dSchema != nil {
   422  				suggestion = fmt.Sprintf("\n\nDid you intend to use the managed resource type %q? If so, declare this using a \"resource\" block instead of a \"data\" block.", n.Config.Type)
   423  			} else if len(providerSchema.DataSources) > 0 {
   424  				suggestions := make([]string, 0, len(providerSchema.DataSources))
   425  				for name := range providerSchema.DataSources {
   426  					suggestions = append(suggestions, name)
   427  				}
   428  				if suggestion = didyoumean.NameSuggestion(n.Config.Type, suggestions); suggestion != "" {
   429  					suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   430  				}
   431  			}
   432  
   433  			diags = diags.Append(&hcl.Diagnostic{
   434  				Severity: hcl.DiagError,
   435  				Summary:  "Invalid data source",
   436  				Detail:   fmt.Sprintf("The provider %s does not support data source %q.%s", n.Provider().ForDisplay(), n.Config.Type, suggestion),
   437  				Subject:  &n.Config.TypeRange,
   438  			})
   439  			return diags
   440  		}
   441  
   442  		configVal, _, valDiags := ctx.EvaluateBlock(n.Config.Config, schema, nil, keyData)
   443  		diags = diags.Append(valDiags)
   444  		if valDiags.HasErrors() {
   445  			return diags
   446  		}
   447  
   448  		// Use unmarked value for validate request
   449  		unmarkedConfigVal, _ := configVal.UnmarkDeep()
   450  		req := providers.ValidateDataResourceConfigRequest{
   451  			TypeName: n.Config.Type,
   452  			Config:   unmarkedConfigVal,
   453  		}
   454  
   455  		resp := provider.ValidateDataResourceConfig(req)
   456  		diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
   457  	}
   458  
   459  	return diags
   460  }
   461  
   462  func (n *NodeValidatableResource) evaluateExpr(ctx EvalContext, expr hcl.Expression, wantTy cty.Type, self addrs.Referenceable, keyData instances.RepetitionData) (cty.Value, tfdiags.Diagnostics) {
   463  	var diags tfdiags.Diagnostics
   464  
   465  	refs, refDiags := lang.ReferencesInExpr(expr)
   466  	diags = diags.Append(refDiags)
   467  
   468  	scope := ctx.EvaluationScope(self, keyData)
   469  
   470  	hclCtx, moreDiags := scope.EvalContext(refs)
   471  	diags = diags.Append(moreDiags)
   472  
   473  	result, hclDiags := expr.Value(hclCtx)
   474  	diags = diags.Append(hclDiags)
   475  
   476  	return result, diags
   477  }
   478  
   479  func (n *NodeValidatableResource) stubRepetitionData(hasCount, hasForEach bool) (instances.RepetitionData, addrs.Referenceable) {
   480  	keyData := EvalDataForNoInstanceKey
   481  	selfAddr := n.ResourceAddr().Resource.Instance(addrs.NoKey)
   482  
   483  	if n.Config.Count != nil {
   484  		// For a resource that has count, we allow count.index but don't
   485  		// know at this stage what it will return.
   486  		keyData = InstanceKeyEvalData{
   487  			CountIndex: cty.UnknownVal(cty.Number),
   488  		}
   489  
   490  		// "self" can't point to an unknown key, but we'll force it to be
   491  		// key 0 here, which should return an unknown value of the
   492  		// expected type since none of these elements are known at this
   493  		// point anyway.
   494  		selfAddr = n.ResourceAddr().Resource.Instance(addrs.IntKey(0))
   495  	} else if n.Config.ForEach != nil {
   496  		// For a resource that has for_each, we allow each.value and each.key
   497  		// but don't know at this stage what it will return.
   498  		keyData = InstanceKeyEvalData{
   499  			EachKey:   cty.UnknownVal(cty.String),
   500  			EachValue: cty.DynamicVal,
   501  		}
   502  
   503  		// "self" can't point to an unknown key, but we'll force it to be
   504  		// key "" here, which should return an unknown value of the
   505  		// expected type since none of these elements are known at
   506  		// this point anyway.
   507  		selfAddr = n.ResourceAddr().Resource.Instance(addrs.StringKey(""))
   508  	}
   509  
   510  	return keyData, selfAddr
   511  }
   512  
   513  func (n *NodeValidatableResource) validateCheckRules(ctx EvalContext, config *configs.Resource) tfdiags.Diagnostics {
   514  	var diags tfdiags.Diagnostics
   515  
   516  	keyData, selfAddr := n.stubRepetitionData(n.Config.Count != nil, n.Config.ForEach != nil)
   517  
   518  	for _, cr := range config.Preconditions {
   519  		_, conditionDiags := n.evaluateExpr(ctx, cr.Condition, cty.Bool, nil, keyData)
   520  		diags = diags.Append(conditionDiags)
   521  
   522  		_, errorMessageDiags := n.evaluateExpr(ctx, cr.ErrorMessage, cty.Bool, nil, keyData)
   523  		diags = diags.Append(errorMessageDiags)
   524  	}
   525  
   526  	for _, cr := range config.Postconditions {
   527  		_, conditionDiags := n.evaluateExpr(ctx, cr.Condition, cty.Bool, selfAddr, keyData)
   528  		diags = diags.Append(conditionDiags)
   529  
   530  		_, errorMessageDiags := n.evaluateExpr(ctx, cr.ErrorMessage, cty.Bool, selfAddr, keyData)
   531  		diags = diags.Append(errorMessageDiags)
   532  	}
   533  
   534  	return diags
   535  }
   536  
   537  func validateCount(ctx EvalContext, expr hcl.Expression) (diags tfdiags.Diagnostics) {
   538  	val, countDiags := evaluateCountExpressionValue(expr, ctx)
   539  	// If the value isn't known then that's the best we can do for now, but
   540  	// we'll check more thoroughly during the plan walk
   541  	if !val.IsKnown() {
   542  		return diags
   543  	}
   544  
   545  	if countDiags.HasErrors() {
   546  		diags = diags.Append(countDiags)
   547  	}
   548  
   549  	return diags
   550  }
   551  
   552  func validateForEach(ctx EvalContext, expr hcl.Expression) (diags tfdiags.Diagnostics) {
   553  	val, forEachDiags := evaluateForEachExpressionValue(expr, ctx, true)
   554  	// If the value isn't known then that's the best we can do for now, but
   555  	// we'll check more thoroughly during the plan walk
   556  	if !val.IsKnown() {
   557  		return diags
   558  	}
   559  
   560  	if forEachDiags.HasErrors() {
   561  		diags = diags.Append(forEachDiags)
   562  	}
   563  
   564  	return diags
   565  }
   566  
   567  func validateDependsOn(ctx EvalContext, dependsOn []hcl.Traversal) (diags tfdiags.Diagnostics) {
   568  	for _, traversal := range dependsOn {
   569  		ref, refDiags := addrs.ParseRef(traversal)
   570  		diags = diags.Append(refDiags)
   571  		if !refDiags.HasErrors() && len(ref.Remaining) != 0 {
   572  			diags = diags.Append(&hcl.Diagnostic{
   573  				Severity: hcl.DiagError,
   574  				Summary:  "Invalid depends_on reference",
   575  				Detail:   "References in depends_on must be to a whole object (resource, etc), not to an attribute of an object.",
   576  				Subject:  ref.Remaining.SourceRange().Ptr(),
   577  			})
   578  		}
   579  
   580  		// The ref must also refer to something that exists. To test that,
   581  		// we'll just eval it and count on the fact that our evaluator will
   582  		// detect references to non-existent objects.
   583  		if !diags.HasErrors() {
   584  			scope := ctx.EvaluationScope(nil, EvalDataForNoInstanceKey)
   585  			if scope != nil { // sometimes nil in tests, due to incomplete mocks
   586  				_, refDiags = scope.EvalReference(ref, cty.DynamicPseudoType)
   587  				diags = diags.Append(refDiags)
   588  			}
   589  		}
   590  	}
   591  	return diags
   592  }