github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/terraform/evaluate.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  
    10  	"github.com/agext/levenshtein"
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/zclconf/go-cty/cty"
    13  	"github.com/zclconf/go-cty/cty/convert"
    14  
    15  	"github.com/hashicorp/terraform/internal/addrs"
    16  	"github.com/hashicorp/terraform/internal/configs"
    17  	"github.com/hashicorp/terraform/internal/configs/configschema"
    18  	"github.com/hashicorp/terraform/internal/instances"
    19  	"github.com/hashicorp/terraform/internal/lang"
    20  	"github.com/hashicorp/terraform/internal/lang/marks"
    21  	"github.com/hashicorp/terraform/internal/plans"
    22  	"github.com/hashicorp/terraform/internal/states"
    23  	"github.com/hashicorp/terraform/internal/tfdiags"
    24  )
    25  
    26  // Evaluator provides the necessary contextual data for evaluating expressions
    27  // for a particular walk operation.
    28  type Evaluator struct {
    29  	// Operation defines what type of operation this evaluator is being used
    30  	// for.
    31  	Operation walkOperation
    32  
    33  	// Meta is contextual metadata about the current operation.
    34  	Meta *ContextMeta
    35  
    36  	// Config is the root node in the configuration tree.
    37  	Config *configs.Config
    38  
    39  	// VariableValues is a map from variable names to their associated values,
    40  	// within the module indicated by ModulePath. VariableValues is modified
    41  	// concurrently, and so it must be accessed only while holding
    42  	// VariableValuesLock.
    43  	//
    44  	// The first map level is string representations of addr.ModuleInstance
    45  	// values, while the second level is variable names.
    46  	VariableValues     map[string]map[string]cty.Value
    47  	VariableValuesLock *sync.Mutex
    48  
    49  	// Plugins is the library of available plugin components (providers and
    50  	// provisioners) that we have available to help us evaluate expressions
    51  	// that interact with plugin-provided objects.
    52  	//
    53  	// From this we only access the schemas of the plugins, and don't otherwise
    54  	// interact with plugin instances.
    55  	Plugins *contextPlugins
    56  
    57  	// State is the current state, embedded in a wrapper that ensures that
    58  	// it can be safely accessed and modified concurrently.
    59  	State *states.SyncState
    60  
    61  	// Changes is the set of proposed changes, embedded in a wrapper that
    62  	// ensures they can be safely accessed and modified concurrently.
    63  	Changes *plans.ChangesSync
    64  }
    65  
    66  // Scope creates an evaluation scope for the given module path and optional
    67  // resource.
    68  //
    69  // If the "self" argument is nil then the "self" object is not available
    70  // in evaluated expressions. Otherwise, it behaves as an alias for the given
    71  // address.
    72  func (e *Evaluator) Scope(data lang.Data, self addrs.Referenceable) *lang.Scope {
    73  	return &lang.Scope{
    74  		Data:     data,
    75  		SelfAddr: self,
    76  		PureOnly: e.Operation != walkApply && e.Operation != walkDestroy && e.Operation != walkEval,
    77  		BaseDir:  ".", // Always current working directory for now.
    78  	}
    79  }
    80  
    81  // evaluationStateData is an implementation of lang.Data that resolves
    82  // references primarily (but not exclusively) using information from a State.
    83  type evaluationStateData struct {
    84  	Evaluator *Evaluator
    85  
    86  	// ModulePath is the path through the dynamic module tree to the module
    87  	// that references will be resolved relative to.
    88  	ModulePath addrs.ModuleInstance
    89  
    90  	// InstanceKeyData describes the values, if any, that are accessible due
    91  	// to repetition of a containing object using "count" or "for_each"
    92  	// arguments. (It is _not_ used for the for_each inside "dynamic" blocks,
    93  	// since the user specifies in that case which variable name to locally
    94  	// shadow.)
    95  	InstanceKeyData InstanceKeyEvalData
    96  
    97  	// Operation records the type of walk the evaluationStateData is being used
    98  	// for.
    99  	Operation walkOperation
   100  }
   101  
   102  // InstanceKeyEvalData is the old name for instances.RepetitionData, aliased
   103  // here for compatibility. In new code, use instances.RepetitionData instead.
   104  type InstanceKeyEvalData = instances.RepetitionData
   105  
   106  // EvalDataForInstanceKey constructs a suitable InstanceKeyEvalData for
   107  // evaluating in a context that has the given instance key.
   108  //
   109  // The forEachMap argument can be nil when preparing for evaluation
   110  // in a context where each.value is prohibited, such as a destroy-time
   111  // provisioner. In that case, the returned EachValue will always be
   112  // cty.NilVal.
   113  func EvalDataForInstanceKey(key addrs.InstanceKey, forEachMap map[string]cty.Value) InstanceKeyEvalData {
   114  	var evalData InstanceKeyEvalData
   115  	if key == nil {
   116  		return evalData
   117  	}
   118  
   119  	keyValue := key.Value()
   120  	switch keyValue.Type() {
   121  	case cty.String:
   122  		evalData.EachKey = keyValue
   123  		evalData.EachValue = forEachMap[keyValue.AsString()]
   124  	case cty.Number:
   125  		evalData.CountIndex = keyValue
   126  	}
   127  	return evalData
   128  }
   129  
   130  // EvalDataForNoInstanceKey is a value of InstanceKeyData that sets no instance
   131  // key values at all, suitable for use in contexts where no keyed instance
   132  // is relevant.
   133  var EvalDataForNoInstanceKey = InstanceKeyEvalData{}
   134  
   135  // evaluationStateData must implement lang.Data
   136  var _ lang.Data = (*evaluationStateData)(nil)
   137  
   138  func (d *evaluationStateData) GetCountAttr(addr addrs.CountAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   139  	var diags tfdiags.Diagnostics
   140  	switch addr.Name {
   141  
   142  	case "index":
   143  		idxVal := d.InstanceKeyData.CountIndex
   144  		if idxVal == cty.NilVal {
   145  			diags = diags.Append(&hcl.Diagnostic{
   146  				Severity: hcl.DiagError,
   147  				Summary:  `Reference to "count" in non-counted context`,
   148  				Detail:   `The "count" object can only be used in "module", "resource", and "data" blocks, and only when the "count" argument is set.`,
   149  				Subject:  rng.ToHCL().Ptr(),
   150  			})
   151  			return cty.UnknownVal(cty.Number), diags
   152  		}
   153  		return idxVal, diags
   154  
   155  	default:
   156  		diags = diags.Append(&hcl.Diagnostic{
   157  			Severity: hcl.DiagError,
   158  			Summary:  `Invalid "count" attribute`,
   159  			Detail:   fmt.Sprintf(`The "count" object does not have an attribute named %q. The only supported attribute is count.index, which is the index of each instance of a resource block that has the "count" argument set.`, addr.Name),
   160  			Subject:  rng.ToHCL().Ptr(),
   161  		})
   162  		return cty.DynamicVal, diags
   163  	}
   164  }
   165  
   166  func (d *evaluationStateData) GetForEachAttr(addr addrs.ForEachAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   167  	var diags tfdiags.Diagnostics
   168  	var returnVal cty.Value
   169  	switch addr.Name {
   170  
   171  	case "key":
   172  		returnVal = d.InstanceKeyData.EachKey
   173  	case "value":
   174  		returnVal = d.InstanceKeyData.EachValue
   175  
   176  		if returnVal == cty.NilVal {
   177  			diags = diags.Append(&hcl.Diagnostic{
   178  				Severity: hcl.DiagError,
   179  				Summary:  `each.value cannot be used in this context`,
   180  				Detail:   `A reference to "each.value" has been used in a context in which it unavailable, such as when the configuration no longer contains the value in its "for_each" expression. Remove this reference to each.value in your configuration to work around this error.`,
   181  				Subject:  rng.ToHCL().Ptr(),
   182  			})
   183  			return cty.UnknownVal(cty.DynamicPseudoType), diags
   184  		}
   185  	default:
   186  		diags = diags.Append(&hcl.Diagnostic{
   187  			Severity: hcl.DiagError,
   188  			Summary:  `Invalid "each" attribute`,
   189  			Detail:   fmt.Sprintf(`The "each" object does not have an attribute named %q. The supported attributes are each.key and each.value, the current key and value pair of the "for_each" attribute set.`, addr.Name),
   190  			Subject:  rng.ToHCL().Ptr(),
   191  		})
   192  		return cty.DynamicVal, diags
   193  	}
   194  
   195  	if returnVal == cty.NilVal {
   196  		diags = diags.Append(&hcl.Diagnostic{
   197  			Severity: hcl.DiagError,
   198  			Summary:  `Reference to "each" in context without for_each`,
   199  			Detail:   `The "each" object can be used only in "module" or "resource" blocks, and only when the "for_each" argument is set.`,
   200  			Subject:  rng.ToHCL().Ptr(),
   201  		})
   202  		return cty.UnknownVal(cty.DynamicPseudoType), diags
   203  	}
   204  	return returnVal, diags
   205  }
   206  
   207  func (d *evaluationStateData) GetInputVariable(addr addrs.InputVariable, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   208  	var diags tfdiags.Diagnostics
   209  
   210  	// First we'll make sure the requested value is declared in configuration,
   211  	// so we can produce a nice message if not.
   212  	moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   213  	if moduleConfig == nil {
   214  		// should never happen, since we can't be evaluating in a module
   215  		// that wasn't mentioned in configuration.
   216  		panic(fmt.Sprintf("input variable read from %s, which has no configuration", d.ModulePath))
   217  	}
   218  
   219  	config := moduleConfig.Module.Variables[addr.Name]
   220  	if config == nil {
   221  		var suggestions []string
   222  		for k := range moduleConfig.Module.Variables {
   223  			suggestions = append(suggestions, k)
   224  		}
   225  		suggestion := nameSuggestion(addr.Name, suggestions)
   226  		if suggestion != "" {
   227  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   228  		} else {
   229  			suggestion = fmt.Sprintf(" This variable can be declared with a variable %q {} block.", addr.Name)
   230  		}
   231  
   232  		diags = diags.Append(&hcl.Diagnostic{
   233  			Severity: hcl.DiagError,
   234  			Summary:  `Reference to undeclared input variable`,
   235  			Detail:   fmt.Sprintf(`An input variable with the name %q has not been declared.%s`, addr.Name, suggestion),
   236  			Subject:  rng.ToHCL().Ptr(),
   237  		})
   238  		return cty.DynamicVal, diags
   239  	}
   240  
   241  	wantType := cty.DynamicPseudoType
   242  	if config.Type != cty.NilType {
   243  		wantType = config.Type
   244  	}
   245  
   246  	d.Evaluator.VariableValuesLock.Lock()
   247  	defer d.Evaluator.VariableValuesLock.Unlock()
   248  
   249  	// During the validate walk, input variables are always unknown so
   250  	// that we are validating the configuration for all possible input values
   251  	// rather than for a specific set. Checking against a specific set of
   252  	// input values then happens during the plan walk.
   253  	//
   254  	// This is important because otherwise the validation walk will tend to be
   255  	// overly strict, requiring expressions throughout the configuration to
   256  	// be complicated to accommodate all possible inputs, whereas returning
   257  	// known here allows for simpler patterns like using input values as
   258  	// guards to broadly enable/disable resources, avoid processing things
   259  	// that are disabled, etc. Terraform's static validation leans towards
   260  	// being liberal in what it accepts because the subsequent plan walk has
   261  	// more information available and so can be more conservative.
   262  	if d.Operation == walkValidate {
   263  		// Ensure variable sensitivity is captured in the validate walk
   264  		if config.Sensitive {
   265  			return cty.UnknownVal(wantType).Mark(marks.Sensitive), diags
   266  		}
   267  		return cty.UnknownVal(wantType), diags
   268  	}
   269  
   270  	moduleAddrStr := d.ModulePath.String()
   271  	vals := d.Evaluator.VariableValues[moduleAddrStr]
   272  	if vals == nil {
   273  		return cty.UnknownVal(wantType), diags
   274  	}
   275  
   276  	val, isSet := vals[addr.Name]
   277  	if !isSet {
   278  		if config.Default != cty.NilVal {
   279  			return config.Default, diags
   280  		}
   281  		return cty.UnknownVal(wantType), diags
   282  	}
   283  
   284  	var err error
   285  	val, err = convert.Convert(val, wantType)
   286  	if err != nil {
   287  		// We should never get here because this problem should've been caught
   288  		// during earlier validation, but we'll do something reasonable anyway.
   289  		diags = diags.Append(&hcl.Diagnostic{
   290  			Severity: hcl.DiagError,
   291  			Summary:  `Incorrect variable type`,
   292  			Detail:   fmt.Sprintf(`The resolved value of variable %q is not appropriate: %s.`, addr.Name, err),
   293  			Subject:  &config.DeclRange,
   294  		})
   295  		// Stub out our return value so that the semantic checker doesn't
   296  		// produce redundant downstream errors.
   297  		val = cty.UnknownVal(wantType)
   298  	}
   299  
   300  	// Mark if sensitive
   301  	if config.Sensitive {
   302  		val = val.Mark(marks.Sensitive)
   303  	}
   304  
   305  	return val, diags
   306  }
   307  
   308  func (d *evaluationStateData) GetLocalValue(addr addrs.LocalValue, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   309  	var diags tfdiags.Diagnostics
   310  
   311  	// First we'll make sure the requested value is declared in configuration,
   312  	// so we can produce a nice message if not.
   313  	moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   314  	if moduleConfig == nil {
   315  		// should never happen, since we can't be evaluating in a module
   316  		// that wasn't mentioned in configuration.
   317  		panic(fmt.Sprintf("local value read from %s, which has no configuration", d.ModulePath))
   318  	}
   319  
   320  	config := moduleConfig.Module.Locals[addr.Name]
   321  	if config == nil {
   322  		var suggestions []string
   323  		for k := range moduleConfig.Module.Locals {
   324  			suggestions = append(suggestions, k)
   325  		}
   326  		suggestion := nameSuggestion(addr.Name, suggestions)
   327  		if suggestion != "" {
   328  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   329  		}
   330  
   331  		diags = diags.Append(&hcl.Diagnostic{
   332  			Severity: hcl.DiagError,
   333  			Summary:  `Reference to undeclared local value`,
   334  			Detail:   fmt.Sprintf(`A local value with the name %q has not been declared.%s`, addr.Name, suggestion),
   335  			Subject:  rng.ToHCL().Ptr(),
   336  		})
   337  		return cty.DynamicVal, diags
   338  	}
   339  
   340  	val := d.Evaluator.State.LocalValue(addr.Absolute(d.ModulePath))
   341  	if val == cty.NilVal {
   342  		// Not evaluated yet?
   343  		val = cty.DynamicVal
   344  	}
   345  
   346  	return val, diags
   347  }
   348  
   349  func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   350  	var diags tfdiags.Diagnostics
   351  
   352  	// Output results live in the module that declares them, which is one of
   353  	// the child module instances of our current module path.
   354  	moduleAddr := d.ModulePath.Module().Child(addr.Name)
   355  
   356  	parentCfg := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   357  	callConfig, ok := parentCfg.Module.ModuleCalls[addr.Name]
   358  	if !ok {
   359  		diags = diags.Append(&hcl.Diagnostic{
   360  			Severity: hcl.DiagError,
   361  			Summary:  `Reference to undeclared module`,
   362  			Detail:   fmt.Sprintf(`The configuration contains no %s.`, moduleAddr),
   363  			Subject:  rng.ToHCL().Ptr(),
   364  		})
   365  		return cty.DynamicVal, diags
   366  	}
   367  
   368  	// We'll consult the configuration to see what output names we are
   369  	// expecting, so we can ensure the resulting object is of the expected
   370  	// type even if our data is incomplete for some reason.
   371  	moduleConfig := d.Evaluator.Config.Descendent(moduleAddr)
   372  	if moduleConfig == nil {
   373  		// should never happen, since we have a valid module call above, this
   374  		// should be caught during static validation.
   375  		panic(fmt.Sprintf("output value read from %s, which has no configuration", moduleAddr))
   376  	}
   377  	outputConfigs := moduleConfig.Module.Outputs
   378  
   379  	// Collect all the relevant outputs that current exist in the state.
   380  	// We know the instance path up to this point, and the child module name,
   381  	// so we only need to store these by instance key.
   382  	stateMap := map[addrs.InstanceKey]map[string]cty.Value{}
   383  	for _, output := range d.Evaluator.State.ModuleOutputs(d.ModulePath, addr) {
   384  		_, callInstance := output.Addr.Module.CallInstance()
   385  		instance, ok := stateMap[callInstance.Key]
   386  		if !ok {
   387  			instance = map[string]cty.Value{}
   388  			stateMap[callInstance.Key] = instance
   389  		}
   390  
   391  		instance[output.Addr.OutputValue.Name] = output.Value
   392  	}
   393  
   394  	// Get all changes that reside for this module call within our path.
   395  	// The change contains the full addr, so we can key these with strings.
   396  	changesMap := map[addrs.InstanceKey]map[string]*plans.OutputChangeSrc{}
   397  	for _, change := range d.Evaluator.Changes.GetOutputChanges(d.ModulePath, addr) {
   398  		_, callInstance := change.Addr.Module.CallInstance()
   399  		instance, ok := changesMap[callInstance.Key]
   400  		if !ok {
   401  			instance = map[string]*plans.OutputChangeSrc{}
   402  			changesMap[callInstance.Key] = instance
   403  		}
   404  
   405  		instance[change.Addr.OutputValue.Name] = change
   406  	}
   407  
   408  	// Build up all the module objects, creating a map of values for each
   409  	// module instance.
   410  	moduleInstances := map[addrs.InstanceKey]map[string]cty.Value{}
   411  
   412  	// create a dummy object type for validation below
   413  	unknownMap := map[string]cty.Type{}
   414  
   415  	// the structure is based on the configuration, so iterate through all the
   416  	// defined outputs, and add any instance state or changes we find.
   417  	for _, cfg := range outputConfigs {
   418  		// record the output names for validation
   419  		unknownMap[cfg.Name] = cty.DynamicPseudoType
   420  
   421  		// get all instance output for this path from the state
   422  		for key, states := range stateMap {
   423  			outputState, ok := states[cfg.Name]
   424  			if !ok {
   425  				continue
   426  			}
   427  
   428  			instance, ok := moduleInstances[key]
   429  			if !ok {
   430  				instance = map[string]cty.Value{}
   431  				moduleInstances[key] = instance
   432  			}
   433  
   434  			instance[cfg.Name] = outputState
   435  
   436  			if cfg.Sensitive {
   437  				instance[cfg.Name] = outputState.Mark(marks.Sensitive)
   438  			}
   439  		}
   440  
   441  		// any pending changes override the state state values
   442  		for key, changes := range changesMap {
   443  			changeSrc, ok := changes[cfg.Name]
   444  			if !ok {
   445  				continue
   446  			}
   447  
   448  			instance, ok := moduleInstances[key]
   449  			if !ok {
   450  				instance = map[string]cty.Value{}
   451  				moduleInstances[key] = instance
   452  			}
   453  
   454  			change, err := changeSrc.Decode()
   455  			if err != nil {
   456  				// This should happen only if someone has tampered with a plan
   457  				// file, so we won't bother with a pretty error for it.
   458  				diags = diags.Append(fmt.Errorf("planned change for %s could not be decoded: %s", addr, err))
   459  				instance[cfg.Name] = cty.DynamicVal
   460  				continue
   461  			}
   462  
   463  			instance[cfg.Name] = change.After
   464  
   465  			if change.Sensitive {
   466  				instance[cfg.Name] = change.After.Mark(marks.Sensitive)
   467  			}
   468  		}
   469  	}
   470  
   471  	var ret cty.Value
   472  
   473  	// compile the outputs into the correct value type for the each mode
   474  	switch {
   475  	case callConfig.Count != nil:
   476  		// figure out what the last index we have is
   477  		length := -1
   478  		for key := range moduleInstances {
   479  			intKey, ok := key.(addrs.IntKey)
   480  			if !ok {
   481  				// old key from state which is being dropped
   482  				continue
   483  			}
   484  			if int(intKey) >= length {
   485  				length = int(intKey) + 1
   486  			}
   487  		}
   488  
   489  		if length > 0 {
   490  			vals := make([]cty.Value, length)
   491  			for key, instance := range moduleInstances {
   492  				intKey, ok := key.(addrs.IntKey)
   493  				if !ok {
   494  					// old key from state which is being dropped
   495  					continue
   496  				}
   497  
   498  				vals[int(intKey)] = cty.ObjectVal(instance)
   499  			}
   500  
   501  			// Insert unknown values where there are any missing instances
   502  			for i, v := range vals {
   503  				if v.IsNull() {
   504  					vals[i] = cty.DynamicVal
   505  					continue
   506  				}
   507  			}
   508  			ret = cty.TupleVal(vals)
   509  		} else {
   510  			ret = cty.EmptyTupleVal
   511  		}
   512  
   513  	case callConfig.ForEach != nil:
   514  		vals := make(map[string]cty.Value)
   515  		for key, instance := range moduleInstances {
   516  			strKey, ok := key.(addrs.StringKey)
   517  			if !ok {
   518  				continue
   519  			}
   520  
   521  			vals[string(strKey)] = cty.ObjectVal(instance)
   522  		}
   523  
   524  		if len(vals) > 0 {
   525  			ret = cty.ObjectVal(vals)
   526  		} else {
   527  			ret = cty.EmptyObjectVal
   528  		}
   529  
   530  	default:
   531  		val, ok := moduleInstances[addrs.NoKey]
   532  		if !ok {
   533  			// create the object if there wasn't one known
   534  			val = map[string]cty.Value{}
   535  			for k := range outputConfigs {
   536  				val[k] = cty.DynamicVal
   537  			}
   538  		}
   539  
   540  		ret = cty.ObjectVal(val)
   541  	}
   542  
   543  	// The module won't be expanded during validation, so we need to return an
   544  	// unknown value. This will ensure the types looks correct, since we built
   545  	// the objects based on the configuration.
   546  	if d.Operation == walkValidate {
   547  		// While we know the type here and it would be nice to validate whether
   548  		// indexes are valid or not, because tuples and objects have fixed
   549  		// numbers of elements we can't simply return an unknown value of the
   550  		// same type since we have not expanded any instances during
   551  		// validation.
   552  		//
   553  		// In order to validate the expression a little precisely, we'll create
   554  		// an unknown map or list here to get more type information.
   555  		ty := cty.Object(unknownMap)
   556  		switch {
   557  		case callConfig.Count != nil:
   558  			ret = cty.UnknownVal(cty.List(ty))
   559  		case callConfig.ForEach != nil:
   560  			ret = cty.UnknownVal(cty.Map(ty))
   561  		default:
   562  			ret = cty.UnknownVal(ty)
   563  		}
   564  	}
   565  
   566  	return ret, diags
   567  }
   568  
   569  func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   570  	var diags tfdiags.Diagnostics
   571  	switch addr.Name {
   572  
   573  	case "cwd":
   574  		var err error
   575  		var wd string
   576  		if d.Evaluator.Meta != nil {
   577  			// Meta is always non-nil in the normal case, but some test cases
   578  			// are not so realistic.
   579  			wd = d.Evaluator.Meta.OriginalWorkingDir
   580  		}
   581  		if wd == "" {
   582  			wd, err = os.Getwd()
   583  			if err != nil {
   584  				diags = diags.Append(&hcl.Diagnostic{
   585  					Severity: hcl.DiagError,
   586  					Summary:  `Failed to get working directory`,
   587  					Detail:   fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
   588  					Subject:  rng.ToHCL().Ptr(),
   589  				})
   590  				return cty.DynamicVal, diags
   591  			}
   592  		}
   593  		// The current working directory should always be absolute, whether we
   594  		// just looked it up or whether we were relying on ContextMeta's
   595  		// (possibly non-normalized) path.
   596  		wd, err = filepath.Abs(wd)
   597  		if err != nil {
   598  			diags = diags.Append(&hcl.Diagnostic{
   599  				Severity: hcl.DiagError,
   600  				Summary:  `Failed to get working directory`,
   601  				Detail:   fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
   602  				Subject:  rng.ToHCL().Ptr(),
   603  			})
   604  			return cty.DynamicVal, diags
   605  		}
   606  
   607  		return cty.StringVal(filepath.ToSlash(wd)), diags
   608  
   609  	case "module":
   610  		moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   611  		if moduleConfig == nil {
   612  			// should never happen, since we can't be evaluating in a module
   613  			// that wasn't mentioned in configuration.
   614  			panic(fmt.Sprintf("module.path read from module %s, which has no configuration", d.ModulePath))
   615  		}
   616  		sourceDir := moduleConfig.Module.SourceDir
   617  		return cty.StringVal(filepath.ToSlash(sourceDir)), diags
   618  
   619  	case "root":
   620  		sourceDir := d.Evaluator.Config.Module.SourceDir
   621  		return cty.StringVal(filepath.ToSlash(sourceDir)), diags
   622  
   623  	default:
   624  		suggestion := nameSuggestion(addr.Name, []string{"cwd", "module", "root"})
   625  		if suggestion != "" {
   626  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   627  		}
   628  		diags = diags.Append(&hcl.Diagnostic{
   629  			Severity: hcl.DiagError,
   630  			Summary:  `Invalid "path" attribute`,
   631  			Detail:   fmt.Sprintf(`The "path" object does not have an attribute named %q.%s`, addr.Name, suggestion),
   632  			Subject:  rng.ToHCL().Ptr(),
   633  		})
   634  		return cty.DynamicVal, diags
   635  	}
   636  }
   637  
   638  func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   639  	var diags tfdiags.Diagnostics
   640  	// First we'll consult the configuration to see if an resource of this
   641  	// name is declared at all.
   642  	moduleAddr := d.ModulePath
   643  	moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr)
   644  	if moduleConfig == nil {
   645  		// should never happen, since we can't be evaluating in a module
   646  		// that wasn't mentioned in configuration.
   647  		panic(fmt.Sprintf("resource value read from %s, which has no configuration", moduleAddr))
   648  	}
   649  
   650  	config := moduleConfig.Module.ResourceByAddr(addr)
   651  	if config == nil {
   652  		diags = diags.Append(&hcl.Diagnostic{
   653  			Severity: hcl.DiagError,
   654  			Summary:  `Reference to undeclared resource`,
   655  			Detail:   fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Type, addr.Name, moduleDisplayAddr(moduleAddr)),
   656  			Subject:  rng.ToHCL().Ptr(),
   657  		})
   658  		return cty.DynamicVal, diags
   659  	}
   660  
   661  	rs := d.Evaluator.State.Resource(addr.Absolute(d.ModulePath))
   662  
   663  	if rs == nil {
   664  		switch d.Operation {
   665  		case walkPlan, walkApply:
   666  			// During plan and apply as we evaluate each removed instance they
   667  			// are removed from the working state. Since we know there are no
   668  			// instances, return an empty container of the expected type.
   669  			switch {
   670  			case config.Count != nil:
   671  				return cty.EmptyTupleVal, diags
   672  			case config.ForEach != nil:
   673  				return cty.EmptyObjectVal, diags
   674  			default:
   675  				// While we can reference an expanded resource with 0
   676  				// instances, we cannot reference instances that do not exist.
   677  				// Due to the fact that we may have direct references to
   678  				// instances that may end up in a root output during destroy
   679  				// (since a planned destroy cannot yet remove root outputs), we
   680  				// need to return a dynamic value here to allow evaluation to
   681  				// continue.
   682  				log.Printf("[ERROR] unknown instance %q referenced during plan", addr.Absolute(d.ModulePath))
   683  				return cty.DynamicVal, diags
   684  			}
   685  
   686  		default:
   687  			// We should only end up here during the validate walk,
   688  			// since later walks should have at least partial states populated
   689  			// for all resources in the configuration.
   690  			return cty.DynamicVal, diags
   691  		}
   692  	}
   693  
   694  	providerAddr := rs.ProviderConfig
   695  
   696  	schema := d.getResourceSchema(addr, providerAddr)
   697  	if schema == nil {
   698  		// This shouldn't happen, since validation before we get here should've
   699  		// taken care of it, but we'll show a reasonable error message anyway.
   700  		diags = diags.Append(&hcl.Diagnostic{
   701  			Severity: hcl.DiagError,
   702  			Summary:  `Missing resource type schema`,
   703  			Detail:   fmt.Sprintf("No schema is available for %s in %s. This is a bug in Terraform and should be reported.", addr, providerAddr),
   704  			Subject:  rng.ToHCL().Ptr(),
   705  		})
   706  		return cty.DynamicVal, diags
   707  	}
   708  	ty := schema.ImpliedType()
   709  
   710  	// Decode all instances in the current state
   711  	instances := map[addrs.InstanceKey]cty.Value{}
   712  	pendingDestroy := d.Evaluator.Changes.IsFullDestroy()
   713  	for key, is := range rs.Instances {
   714  		if is == nil || is.Current == nil {
   715  			// Assume we're dealing with an instance that hasn't been created yet.
   716  			instances[key] = cty.UnknownVal(ty)
   717  			continue
   718  		}
   719  
   720  		instAddr := addr.Instance(key).Absolute(d.ModulePath)
   721  
   722  		change := d.Evaluator.Changes.GetResourceInstanceChange(instAddr, states.CurrentGen)
   723  		if change != nil {
   724  			// Don't take any resources that are yet to be deleted into account.
   725  			// If the referenced resource is CreateBeforeDestroy, then orphaned
   726  			// instances will be in the state, as they are not destroyed until
   727  			// after their dependants are updated.
   728  			if change.Action == plans.Delete {
   729  				if !pendingDestroy {
   730  					continue
   731  				}
   732  			}
   733  		}
   734  
   735  		// Planned resources are temporarily stored in state with empty values,
   736  		// and need to be replaced by the planned value here.
   737  		if is.Current.Status == states.ObjectPlanned {
   738  			if change == nil {
   739  				// If the object is in planned status then we should not get
   740  				// here, since we should have found a pending value in the plan
   741  				// above instead.
   742  				diags = diags.Append(&hcl.Diagnostic{
   743  					Severity: hcl.DiagError,
   744  					Summary:  "Missing pending object in plan",
   745  					Detail:   fmt.Sprintf("Instance %s is marked as having a change pending but that change is not recorded in the plan. This is a bug in Terraform; please report it.", instAddr),
   746  					Subject:  &config.DeclRange,
   747  				})
   748  				continue
   749  			}
   750  			val, err := change.After.Decode(ty)
   751  			if err != nil {
   752  				diags = diags.Append(&hcl.Diagnostic{
   753  					Severity: hcl.DiagError,
   754  					Summary:  "Invalid resource instance data in plan",
   755  					Detail:   fmt.Sprintf("Instance %s data could not be decoded from the plan: %s.", instAddr, err),
   756  					Subject:  &config.DeclRange,
   757  				})
   758  				continue
   759  			}
   760  
   761  			// If our provider schema contains sensitive values, mark those as sensitive
   762  			afterMarks := change.AfterValMarks
   763  			if schema.ContainsSensitive() {
   764  				afterMarks = append(afterMarks, schema.ValueMarks(val, nil)...)
   765  			}
   766  
   767  			instances[key] = val.MarkWithPaths(afterMarks)
   768  			continue
   769  		}
   770  
   771  		ios, err := is.Current.Decode(ty)
   772  		if err != nil {
   773  			// This shouldn't happen, since by the time we get here we
   774  			// should have upgraded the state data already.
   775  			diags = diags.Append(&hcl.Diagnostic{
   776  				Severity: hcl.DiagError,
   777  				Summary:  "Invalid resource instance data in state",
   778  				Detail:   fmt.Sprintf("Instance %s data could not be decoded from the state: %s.", instAddr, err),
   779  				Subject:  &config.DeclRange,
   780  			})
   781  			continue
   782  		}
   783  
   784  		val := ios.Value
   785  
   786  		// If our schema contains sensitive values, mark those as sensitive.
   787  		// Since decoding the instance object can also apply sensitivity marks,
   788  		// we must remove and combine those before remarking to avoid a double-
   789  		// mark error.
   790  		if schema.ContainsSensitive() {
   791  			var marks []cty.PathValueMarks
   792  			val, marks = val.UnmarkDeepWithPaths()
   793  			marks = append(marks, schema.ValueMarks(val, nil)...)
   794  			val = val.MarkWithPaths(marks)
   795  		}
   796  		instances[key] = val
   797  	}
   798  
   799  	var ret cty.Value
   800  
   801  	switch {
   802  	case config.Count != nil:
   803  		// figure out what the last index we have is
   804  		length := -1
   805  		for key := range instances {
   806  			intKey, ok := key.(addrs.IntKey)
   807  			if !ok {
   808  				continue
   809  			}
   810  			if int(intKey) >= length {
   811  				length = int(intKey) + 1
   812  			}
   813  		}
   814  
   815  		if length > 0 {
   816  			vals := make([]cty.Value, length)
   817  			for key, instance := range instances {
   818  				intKey, ok := key.(addrs.IntKey)
   819  				if !ok {
   820  					// old key from state, which isn't valid for evaluation
   821  					continue
   822  				}
   823  
   824  				vals[int(intKey)] = instance
   825  			}
   826  
   827  			// Insert unknown values where there are any missing instances
   828  			for i, v := range vals {
   829  				if v == cty.NilVal {
   830  					vals[i] = cty.UnknownVal(ty)
   831  				}
   832  			}
   833  			ret = cty.TupleVal(vals)
   834  		} else {
   835  			ret = cty.EmptyTupleVal
   836  		}
   837  
   838  	case config.ForEach != nil:
   839  		vals := make(map[string]cty.Value)
   840  		for key, instance := range instances {
   841  			strKey, ok := key.(addrs.StringKey)
   842  			if !ok {
   843  				// old key that is being dropped and not used for evaluation
   844  				continue
   845  			}
   846  			vals[string(strKey)] = instance
   847  		}
   848  
   849  		if len(vals) > 0 {
   850  			// We use an object rather than a map here because resource schemas
   851  			// may include dynamically-typed attributes, which will then cause
   852  			// each instance to potentially have a different runtime type even
   853  			// though they all conform to the static schema.
   854  			ret = cty.ObjectVal(vals)
   855  		} else {
   856  			ret = cty.EmptyObjectVal
   857  		}
   858  
   859  	default:
   860  		val, ok := instances[addrs.NoKey]
   861  		if !ok {
   862  			// if the instance is missing, insert an unknown value
   863  			val = cty.UnknownVal(ty)
   864  		}
   865  
   866  		ret = val
   867  	}
   868  
   869  	// since the plan was not yet created during validate, the values we
   870  	// collected here may not correspond with configuration, so they must be
   871  	// unknown.
   872  	if d.Operation == walkValidate {
   873  		// While we know the type here and it would be nice to validate whether
   874  		// indexes are valid or not, because tuples and objects have fixed
   875  		// numbers of elements we can't simply return an unknown value of the
   876  		// same type since we have not expanded any instances during
   877  		// validation.
   878  		//
   879  		// In order to validate the expression a little precisely, we'll create
   880  		// an unknown map or list here to get more type information.
   881  		switch {
   882  		case config.Count != nil:
   883  			ret = cty.UnknownVal(cty.List(ty))
   884  		case config.ForEach != nil:
   885  			ret = cty.UnknownVal(cty.Map(ty))
   886  		default:
   887  			ret = cty.UnknownVal(ty)
   888  		}
   889  	}
   890  
   891  	return ret, diags
   892  }
   893  
   894  func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.AbsProviderConfig) *configschema.Block {
   895  	schema, _, err := d.Evaluator.Plugins.ResourceTypeSchema(providerAddr.Provider, addr.Mode, addr.Type)
   896  	if err != nil {
   897  		// We have plently other codepaths that will detect and report
   898  		// schema lookup errors before we'd reach this point, so we'll just
   899  		// treat a failure here the same as having no schema.
   900  		return nil
   901  	}
   902  	return schema
   903  }
   904  
   905  func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   906  	var diags tfdiags.Diagnostics
   907  	switch addr.Name {
   908  
   909  	case "workspace":
   910  		workspaceName := d.Evaluator.Meta.Env
   911  		return cty.StringVal(workspaceName), diags
   912  
   913  	case "env":
   914  		// Prior to Terraform 0.12 there was an attribute "env", which was
   915  		// an alias name for "workspace". This was deprecated and is now
   916  		// removed.
   917  		diags = diags.Append(&hcl.Diagnostic{
   918  			Severity: hcl.DiagError,
   919  			Summary:  `Invalid "terraform" attribute`,
   920  			Detail:   `The terraform.env attribute was deprecated in v0.10 and removed in v0.12. The "state environment" concept was rename to "workspace" in v0.12, and so the workspace name can now be accessed using the terraform.workspace attribute.`,
   921  			Subject:  rng.ToHCL().Ptr(),
   922  		})
   923  		return cty.DynamicVal, diags
   924  
   925  	default:
   926  		diags = diags.Append(&hcl.Diagnostic{
   927  			Severity: hcl.DiagError,
   928  			Summary:  `Invalid "terraform" attribute`,
   929  			Detail:   fmt.Sprintf(`The "terraform" object does not have an attribute named %q. The only supported attribute is terraform.workspace, the name of the currently-selected workspace.`, addr.Name),
   930  			Subject:  rng.ToHCL().Ptr(),
   931  		})
   932  		return cty.DynamicVal, diags
   933  	}
   934  }
   935  
   936  // nameSuggestion tries to find a name from the given slice of suggested names
   937  // that is close to the given name and returns it if found. If no suggestion
   938  // is close enough, returns the empty string.
   939  //
   940  // The suggestions are tried in order, so earlier suggestions take precedence
   941  // if the given string is similar to two or more suggestions.
   942  //
   943  // This function is intended to be used with a relatively-small number of
   944  // suggestions. It's not optimized for hundreds or thousands of them.
   945  func nameSuggestion(given string, suggestions []string) string {
   946  	for _, suggestion := range suggestions {
   947  		dist := levenshtein.Distance(given, suggestion, nil)
   948  		if dist < 3 { // threshold determined experimentally
   949  			return suggestion
   950  		}
   951  	}
   952  	return ""
   953  }
   954  
   955  // moduleDisplayAddr returns a string describing the given module instance
   956  // address that is appropriate for returning to users in situations where the
   957  // root module is possible. Specifically, it returns "the root module" if the
   958  // root module instance is given, or a string representation of the module
   959  // address otherwise.
   960  func moduleDisplayAddr(addr addrs.ModuleInstance) string {
   961  	switch {
   962  	case addr.IsRoot():
   963  		return "the root module"
   964  	default:
   965  		return addr.String()
   966  	}
   967  }