github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/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/cycloidio/terraform/addrs"
    16  	"github.com/cycloidio/terraform/configs"
    17  	"github.com/cycloidio/terraform/configs/configschema"
    18  	"github.com/cycloidio/terraform/instances"
    19  	"github.com/cycloidio/terraform/lang"
    20  	"github.com/cycloidio/terraform/lang/marks"
    21  	"github.com/cycloidio/terraform/plans"
    22  	"github.com/cycloidio/terraform/states"
    23  	"github.com/cycloidio/terraform/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  	d.Evaluator.VariableValuesLock.Lock()
   241  	defer d.Evaluator.VariableValuesLock.Unlock()
   242  
   243  	// During the validate walk, input variables are always unknown so
   244  	// that we are validating the configuration for all possible input values
   245  	// rather than for a specific set. Checking against a specific set of
   246  	// input values then happens during the plan walk.
   247  	//
   248  	// This is important because otherwise the validation walk will tend to be
   249  	// overly strict, requiring expressions throughout the configuration to
   250  	// be complicated to accommodate all possible inputs, whereas returning
   251  	// known here allows for simpler patterns like using input values as
   252  	// guards to broadly enable/disable resources, avoid processing things
   253  	// that are disabled, etc. Terraform's static validation leans towards
   254  	// being liberal in what it accepts because the subsequent plan walk has
   255  	// more information available and so can be more conservative.
   256  	if d.Operation == walkValidate {
   257  		// Ensure variable sensitivity is captured in the validate walk
   258  		if config.Sensitive {
   259  			return cty.UnknownVal(config.Type).Mark(marks.Sensitive), diags
   260  		}
   261  		return cty.UnknownVal(config.Type), diags
   262  	}
   263  
   264  	moduleAddrStr := d.ModulePath.String()
   265  	vals := d.Evaluator.VariableValues[moduleAddrStr]
   266  	if vals == nil {
   267  		return cty.UnknownVal(config.Type), diags
   268  	}
   269  
   270  	val, isSet := vals[addr.Name]
   271  	switch {
   272  	case !isSet:
   273  		// The config loader will ensure there is a default if the value is not
   274  		// set at all.
   275  		val = config.Default
   276  
   277  	case val.IsNull() && !config.Nullable && config.Default != cty.NilVal:
   278  		// If nullable=false a null value will use the configured default.
   279  		val = config.Default
   280  	}
   281  
   282  	var err error
   283  	val, err = convert.Convert(val, config.ConstraintType)
   284  	if err != nil {
   285  		// We should never get here because this problem should've been caught
   286  		// during earlier validation, but we'll do something reasonable anyway.
   287  		diags = diags.Append(&hcl.Diagnostic{
   288  			Severity: hcl.DiagError,
   289  			Summary:  `Incorrect variable type`,
   290  			Detail:   fmt.Sprintf(`The resolved value of variable %q is not appropriate: %s.`, addr.Name, err),
   291  			Subject:  &config.DeclRange,
   292  		})
   293  		val = cty.UnknownVal(config.Type)
   294  	}
   295  
   296  	// Mark if sensitive
   297  	if config.Sensitive {
   298  		val = val.Mark(marks.Sensitive)
   299  	}
   300  
   301  	return val, diags
   302  }
   303  
   304  func (d *evaluationStateData) GetLocalValue(addr addrs.LocalValue, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   305  	var diags tfdiags.Diagnostics
   306  
   307  	// First we'll make sure the requested value is declared in configuration,
   308  	// so we can produce a nice message if not.
   309  	moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   310  	if moduleConfig == nil {
   311  		// should never happen, since we can't be evaluating in a module
   312  		// that wasn't mentioned in configuration.
   313  		panic(fmt.Sprintf("local value read from %s, which has no configuration", d.ModulePath))
   314  	}
   315  
   316  	config := moduleConfig.Module.Locals[addr.Name]
   317  	if config == nil {
   318  		var suggestions []string
   319  		for k := range moduleConfig.Module.Locals {
   320  			suggestions = append(suggestions, k)
   321  		}
   322  		suggestion := nameSuggestion(addr.Name, suggestions)
   323  		if suggestion != "" {
   324  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   325  		}
   326  
   327  		diags = diags.Append(&hcl.Diagnostic{
   328  			Severity: hcl.DiagError,
   329  			Summary:  `Reference to undeclared local value`,
   330  			Detail:   fmt.Sprintf(`A local value with the name %q has not been declared.%s`, addr.Name, suggestion),
   331  			Subject:  rng.ToHCL().Ptr(),
   332  		})
   333  		return cty.DynamicVal, diags
   334  	}
   335  
   336  	val := d.Evaluator.State.LocalValue(addr.Absolute(d.ModulePath))
   337  	if val == cty.NilVal {
   338  		// Not evaluated yet?
   339  		val = cty.DynamicVal
   340  	}
   341  
   342  	return val, diags
   343  }
   344  
   345  func (d *evaluationStateData) GetModule(addr addrs.ModuleCall, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   346  	var diags tfdiags.Diagnostics
   347  
   348  	// Output results live in the module that declares them, which is one of
   349  	// the child module instances of our current module path.
   350  	moduleAddr := d.ModulePath.Module().Child(addr.Name)
   351  
   352  	parentCfg := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   353  	callConfig, ok := parentCfg.Module.ModuleCalls[addr.Name]
   354  	if !ok {
   355  		diags = diags.Append(&hcl.Diagnostic{
   356  			Severity: hcl.DiagError,
   357  			Summary:  `Reference to undeclared module`,
   358  			Detail:   fmt.Sprintf(`The configuration contains no %s.`, moduleAddr),
   359  			Subject:  rng.ToHCL().Ptr(),
   360  		})
   361  		return cty.DynamicVal, diags
   362  	}
   363  
   364  	// We'll consult the configuration to see what output names we are
   365  	// expecting, so we can ensure the resulting object is of the expected
   366  	// type even if our data is incomplete for some reason.
   367  	moduleConfig := d.Evaluator.Config.Descendent(moduleAddr)
   368  	if moduleConfig == nil {
   369  		// should never happen, since we have a valid module call above, this
   370  		// should be caught during static validation.
   371  		panic(fmt.Sprintf("output value read from %s, which has no configuration", moduleAddr))
   372  	}
   373  	outputConfigs := moduleConfig.Module.Outputs
   374  
   375  	// Collect all the relevant outputs that current exist in the state.
   376  	// We know the instance path up to this point, and the child module name,
   377  	// so we only need to store these by instance key.
   378  	stateMap := map[addrs.InstanceKey]map[string]cty.Value{}
   379  	for _, output := range d.Evaluator.State.ModuleOutputs(d.ModulePath, addr) {
   380  		_, callInstance := output.Addr.Module.CallInstance()
   381  		instance, ok := stateMap[callInstance.Key]
   382  		if !ok {
   383  			instance = map[string]cty.Value{}
   384  			stateMap[callInstance.Key] = instance
   385  		}
   386  
   387  		instance[output.Addr.OutputValue.Name] = output.Value
   388  	}
   389  
   390  	// Get all changes that reside for this module call within our path.
   391  	// The change contains the full addr, so we can key these with strings.
   392  	changesMap := map[addrs.InstanceKey]map[string]*plans.OutputChangeSrc{}
   393  	for _, change := range d.Evaluator.Changes.GetOutputChanges(d.ModulePath, addr) {
   394  		_, callInstance := change.Addr.Module.CallInstance()
   395  		instance, ok := changesMap[callInstance.Key]
   396  		if !ok {
   397  			instance = map[string]*plans.OutputChangeSrc{}
   398  			changesMap[callInstance.Key] = instance
   399  		}
   400  
   401  		instance[change.Addr.OutputValue.Name] = change
   402  	}
   403  
   404  	// Build up all the module objects, creating a map of values for each
   405  	// module instance.
   406  	moduleInstances := map[addrs.InstanceKey]map[string]cty.Value{}
   407  
   408  	// create a dummy object type for validation below
   409  	unknownMap := map[string]cty.Type{}
   410  
   411  	// the structure is based on the configuration, so iterate through all the
   412  	// defined outputs, and add any instance state or changes we find.
   413  	for _, cfg := range outputConfigs {
   414  		// record the output names for validation
   415  		unknownMap[cfg.Name] = cty.DynamicPseudoType
   416  
   417  		// get all instance output for this path from the state
   418  		for key, states := range stateMap {
   419  			outputState, ok := states[cfg.Name]
   420  			if !ok {
   421  				continue
   422  			}
   423  
   424  			instance, ok := moduleInstances[key]
   425  			if !ok {
   426  				instance = map[string]cty.Value{}
   427  				moduleInstances[key] = instance
   428  			}
   429  
   430  			instance[cfg.Name] = outputState
   431  
   432  			if cfg.Sensitive {
   433  				instance[cfg.Name] = outputState.Mark(marks.Sensitive)
   434  			}
   435  		}
   436  
   437  		// any pending changes override the state state values
   438  		for key, changes := range changesMap {
   439  			changeSrc, ok := changes[cfg.Name]
   440  			if !ok {
   441  				continue
   442  			}
   443  
   444  			instance, ok := moduleInstances[key]
   445  			if !ok {
   446  				instance = map[string]cty.Value{}
   447  				moduleInstances[key] = instance
   448  			}
   449  
   450  			change, err := changeSrc.Decode()
   451  			if err != nil {
   452  				// This should happen only if someone has tampered with a plan
   453  				// file, so we won't bother with a pretty error for it.
   454  				diags = diags.Append(fmt.Errorf("planned change for %s could not be decoded: %s", addr, err))
   455  				instance[cfg.Name] = cty.DynamicVal
   456  				continue
   457  			}
   458  
   459  			instance[cfg.Name] = change.After
   460  
   461  			if change.Sensitive {
   462  				instance[cfg.Name] = change.After.Mark(marks.Sensitive)
   463  			}
   464  		}
   465  	}
   466  
   467  	var ret cty.Value
   468  
   469  	// compile the outputs into the correct value type for the each mode
   470  	switch {
   471  	case callConfig.Count != nil:
   472  		// figure out what the last index we have is
   473  		length := -1
   474  		for key := range moduleInstances {
   475  			intKey, ok := key.(addrs.IntKey)
   476  			if !ok {
   477  				// old key from state which is being dropped
   478  				continue
   479  			}
   480  			if int(intKey) >= length {
   481  				length = int(intKey) + 1
   482  			}
   483  		}
   484  
   485  		if length > 0 {
   486  			vals := make([]cty.Value, length)
   487  			for key, instance := range moduleInstances {
   488  				intKey, ok := key.(addrs.IntKey)
   489  				if !ok {
   490  					// old key from state which is being dropped
   491  					continue
   492  				}
   493  
   494  				vals[int(intKey)] = cty.ObjectVal(instance)
   495  			}
   496  
   497  			// Insert unknown values where there are any missing instances
   498  			for i, v := range vals {
   499  				if v.IsNull() {
   500  					vals[i] = cty.DynamicVal
   501  					continue
   502  				}
   503  			}
   504  			ret = cty.TupleVal(vals)
   505  		} else {
   506  			ret = cty.EmptyTupleVal
   507  		}
   508  
   509  	case callConfig.ForEach != nil:
   510  		vals := make(map[string]cty.Value)
   511  		for key, instance := range moduleInstances {
   512  			strKey, ok := key.(addrs.StringKey)
   513  			if !ok {
   514  				continue
   515  			}
   516  
   517  			vals[string(strKey)] = cty.ObjectVal(instance)
   518  		}
   519  
   520  		if len(vals) > 0 {
   521  			ret = cty.ObjectVal(vals)
   522  		} else {
   523  			ret = cty.EmptyObjectVal
   524  		}
   525  
   526  	default:
   527  		val, ok := moduleInstances[addrs.NoKey]
   528  		if !ok {
   529  			// create the object if there wasn't one known
   530  			val = map[string]cty.Value{}
   531  			for k := range outputConfigs {
   532  				val[k] = cty.DynamicVal
   533  			}
   534  		}
   535  
   536  		ret = cty.ObjectVal(val)
   537  	}
   538  
   539  	// The module won't be expanded during validation, so we need to return an
   540  	// unknown value. This will ensure the types looks correct, since we built
   541  	// the objects based on the configuration.
   542  	if d.Operation == walkValidate {
   543  		// While we know the type here and it would be nice to validate whether
   544  		// indexes are valid or not, because tuples and objects have fixed
   545  		// numbers of elements we can't simply return an unknown value of the
   546  		// same type since we have not expanded any instances during
   547  		// validation.
   548  		//
   549  		// In order to validate the expression a little precisely, we'll create
   550  		// an unknown map or list here to get more type information.
   551  		ty := cty.Object(unknownMap)
   552  		switch {
   553  		case callConfig.Count != nil:
   554  			ret = cty.UnknownVal(cty.List(ty))
   555  		case callConfig.ForEach != nil:
   556  			ret = cty.UnknownVal(cty.Map(ty))
   557  		default:
   558  			ret = cty.UnknownVal(ty)
   559  		}
   560  	}
   561  
   562  	return ret, diags
   563  }
   564  
   565  func (d *evaluationStateData) GetPathAttr(addr addrs.PathAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   566  	var diags tfdiags.Diagnostics
   567  	switch addr.Name {
   568  
   569  	case "cwd":
   570  		var err error
   571  		var wd string
   572  		if d.Evaluator.Meta != nil {
   573  			// Meta is always non-nil in the normal case, but some test cases
   574  			// are not so realistic.
   575  			wd = d.Evaluator.Meta.OriginalWorkingDir
   576  		}
   577  		if wd == "" {
   578  			wd, err = os.Getwd()
   579  			if err != nil {
   580  				diags = diags.Append(&hcl.Diagnostic{
   581  					Severity: hcl.DiagError,
   582  					Summary:  `Failed to get working directory`,
   583  					Detail:   fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
   584  					Subject:  rng.ToHCL().Ptr(),
   585  				})
   586  				return cty.DynamicVal, diags
   587  			}
   588  		}
   589  		// The current working directory should always be absolute, whether we
   590  		// just looked it up or whether we were relying on ContextMeta's
   591  		// (possibly non-normalized) path.
   592  		wd, err = filepath.Abs(wd)
   593  		if err != nil {
   594  			diags = diags.Append(&hcl.Diagnostic{
   595  				Severity: hcl.DiagError,
   596  				Summary:  `Failed to get working directory`,
   597  				Detail:   fmt.Sprintf(`The value for path.cwd cannot be determined due to a system error: %s`, err),
   598  				Subject:  rng.ToHCL().Ptr(),
   599  			})
   600  			return cty.DynamicVal, diags
   601  		}
   602  
   603  		return cty.StringVal(filepath.ToSlash(wd)), diags
   604  
   605  	case "module":
   606  		moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath)
   607  		if moduleConfig == nil {
   608  			// should never happen, since we can't be evaluating in a module
   609  			// that wasn't mentioned in configuration.
   610  			panic(fmt.Sprintf("module.path read from module %s, which has no configuration", d.ModulePath))
   611  		}
   612  		sourceDir := moduleConfig.Module.SourceDir
   613  		return cty.StringVal(filepath.ToSlash(sourceDir)), diags
   614  
   615  	case "root":
   616  		sourceDir := d.Evaluator.Config.Module.SourceDir
   617  		return cty.StringVal(filepath.ToSlash(sourceDir)), diags
   618  
   619  	default:
   620  		suggestion := nameSuggestion(addr.Name, []string{"cwd", "module", "root"})
   621  		if suggestion != "" {
   622  			suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
   623  		}
   624  		diags = diags.Append(&hcl.Diagnostic{
   625  			Severity: hcl.DiagError,
   626  			Summary:  `Invalid "path" attribute`,
   627  			Detail:   fmt.Sprintf(`The "path" object does not have an attribute named %q.%s`, addr.Name, suggestion),
   628  			Subject:  rng.ToHCL().Ptr(),
   629  		})
   630  		return cty.DynamicVal, diags
   631  	}
   632  }
   633  
   634  func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   635  	var diags tfdiags.Diagnostics
   636  	// First we'll consult the configuration to see if an resource of this
   637  	// name is declared at all.
   638  	moduleAddr := d.ModulePath
   639  	moduleConfig := d.Evaluator.Config.DescendentForInstance(moduleAddr)
   640  	if moduleConfig == nil {
   641  		// should never happen, since we can't be evaluating in a module
   642  		// that wasn't mentioned in configuration.
   643  		panic(fmt.Sprintf("resource value read from %s, which has no configuration", moduleAddr))
   644  	}
   645  
   646  	config := moduleConfig.Module.ResourceByAddr(addr)
   647  	if config == nil {
   648  		diags = diags.Append(&hcl.Diagnostic{
   649  			Severity: hcl.DiagError,
   650  			Summary:  `Reference to undeclared resource`,
   651  			Detail:   fmt.Sprintf(`A resource %q %q has not been declared in %s`, addr.Type, addr.Name, moduleDisplayAddr(moduleAddr)),
   652  			Subject:  rng.ToHCL().Ptr(),
   653  		})
   654  		return cty.DynamicVal, diags
   655  	}
   656  
   657  	// Build the provider address from configuration, since we may not have
   658  	// state available in all cases.
   659  	// We need to build an abs provider address, but we can use a default
   660  	// instance since we're only interested in the schema.
   661  	schema := d.getResourceSchema(addr, config.Provider)
   662  	if schema == nil {
   663  		// This shouldn't happen, since validation before we get here should've
   664  		// taken care of it, but we'll show a reasonable error message anyway.
   665  		diags = diags.Append(&hcl.Diagnostic{
   666  			Severity: hcl.DiagError,
   667  			Summary:  `Missing resource type schema`,
   668  			Detail:   fmt.Sprintf("No schema is available for %s in %s. This is a bug in Terraform and should be reported.", addr, config.Provider),
   669  			Subject:  rng.ToHCL().Ptr(),
   670  		})
   671  		return cty.DynamicVal, diags
   672  	}
   673  	ty := schema.ImpliedType()
   674  
   675  	rs := d.Evaluator.State.Resource(addr.Absolute(d.ModulePath))
   676  
   677  	if rs == nil {
   678  		switch d.Operation {
   679  		case walkPlan, walkApply:
   680  			// During plan and apply as we evaluate each removed instance they
   681  			// are removed from the working state. Since we know there are no
   682  			// instances, return an empty container of the expected type.
   683  			switch {
   684  			case config.Count != nil:
   685  				return cty.EmptyTupleVal, diags
   686  			case config.ForEach != nil:
   687  				return cty.EmptyObjectVal, diags
   688  			default:
   689  				// While we can reference an expanded resource with 0
   690  				// instances, we cannot reference instances that do not exist.
   691  				// Due to the fact that we may have direct references to
   692  				// instances that may end up in a root output during destroy
   693  				// (since a planned destroy cannot yet remove root outputs), we
   694  				// need to return a dynamic value here to allow evaluation to
   695  				// continue.
   696  				log.Printf("[ERROR] unknown instance %q referenced during %s", addr.Absolute(d.ModulePath), d.Operation)
   697  				return cty.DynamicVal, diags
   698  			}
   699  
   700  		default:
   701  			// We should only end up here during the validate walk,
   702  			// since later walks should have at least partial states populated
   703  			// for all resources in the configuration.
   704  			return cty.DynamicVal, diags
   705  		}
   706  	}
   707  
   708  	// Decode all instances in the current state
   709  	instances := map[addrs.InstanceKey]cty.Value{}
   710  	pendingDestroy := d.Evaluator.Changes.IsFullDestroy()
   711  	for key, is := range rs.Instances {
   712  		if is == nil || is.Current == nil {
   713  			// Assume we're dealing with an instance that hasn't been created yet.
   714  			instances[key] = cty.UnknownVal(ty)
   715  			continue
   716  		}
   717  
   718  		instAddr := addr.Instance(key).Absolute(d.ModulePath)
   719  
   720  		change := d.Evaluator.Changes.GetResourceInstanceChange(instAddr, states.CurrentGen)
   721  		if change != nil {
   722  			// Don't take any resources that are yet to be deleted into account.
   723  			// If the referenced resource is CreateBeforeDestroy, then orphaned
   724  			// instances will be in the state, as they are not destroyed until
   725  			// after their dependants are updated.
   726  			if change.Action == plans.Delete {
   727  				if !pendingDestroy {
   728  					continue
   729  				}
   730  			}
   731  		}
   732  
   733  		// Planned resources are temporarily stored in state with empty values,
   734  		// and need to be replaced by the planned value here.
   735  		if is.Current.Status == states.ObjectPlanned {
   736  			if change == nil {
   737  				// If the object is in planned status then we should not get
   738  				// here, since we should have found a pending value in the plan
   739  				// above instead.
   740  				diags = diags.Append(&hcl.Diagnostic{
   741  					Severity: hcl.DiagError,
   742  					Summary:  "Missing pending object in plan",
   743  					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),
   744  					Subject:  &config.DeclRange,
   745  				})
   746  				continue
   747  			}
   748  			val, err := change.After.Decode(ty)
   749  			if err != nil {
   750  				diags = diags.Append(&hcl.Diagnostic{
   751  					Severity: hcl.DiagError,
   752  					Summary:  "Invalid resource instance data in plan",
   753  					Detail:   fmt.Sprintf("Instance %s data could not be decoded from the plan: %s.", instAddr, err),
   754  					Subject:  &config.DeclRange,
   755  				})
   756  				continue
   757  			}
   758  
   759  			// If our provider schema contains sensitive values, mark those as sensitive
   760  			afterMarks := change.AfterValMarks
   761  			if schema.ContainsSensitive() {
   762  				afterMarks = append(afterMarks, schema.ValueMarks(val, nil)...)
   763  			}
   764  
   765  			instances[key] = val.MarkWithPaths(afterMarks)
   766  			continue
   767  		}
   768  
   769  		ios, err := is.Current.Decode(ty)
   770  		if err != nil {
   771  			// This shouldn't happen, since by the time we get here we
   772  			// should have upgraded the state data already.
   773  			diags = diags.Append(&hcl.Diagnostic{
   774  				Severity: hcl.DiagError,
   775  				Summary:  "Invalid resource instance data in state",
   776  				Detail:   fmt.Sprintf("Instance %s data could not be decoded from the state: %s.", instAddr, err),
   777  				Subject:  &config.DeclRange,
   778  			})
   779  			continue
   780  		}
   781  
   782  		val := ios.Value
   783  
   784  		// If our schema contains sensitive values, mark those as sensitive.
   785  		// Since decoding the instance object can also apply sensitivity marks,
   786  		// we must remove and combine those before remarking to avoid a double-
   787  		// mark error.
   788  		if schema.ContainsSensitive() {
   789  			var marks []cty.PathValueMarks
   790  			val, marks = val.UnmarkDeepWithPaths()
   791  			marks = append(marks, schema.ValueMarks(val, nil)...)
   792  			val = val.MarkWithPaths(marks)
   793  		}
   794  		instances[key] = val
   795  	}
   796  
   797  	// ret should be populated with a valid value in all cases below
   798  	var ret cty.Value
   799  
   800  	switch {
   801  	case config.Count != nil:
   802  		// figure out what the last index we have is
   803  		length := -1
   804  		for key := range instances {
   805  			intKey, ok := key.(addrs.IntKey)
   806  			if !ok {
   807  				continue
   808  			}
   809  			if int(intKey) >= length {
   810  				length = int(intKey) + 1
   811  			}
   812  		}
   813  
   814  		if length > 0 {
   815  			vals := make([]cty.Value, length)
   816  			for key, instance := range instances {
   817  				intKey, ok := key.(addrs.IntKey)
   818  				if !ok {
   819  					// old key from state, which isn't valid for evaluation
   820  					continue
   821  				}
   822  
   823  				vals[int(intKey)] = instance
   824  			}
   825  
   826  			// Insert unknown values where there are any missing instances
   827  			for i, v := range vals {
   828  				if v == cty.NilVal {
   829  					vals[i] = cty.UnknownVal(ty)
   830  				}
   831  			}
   832  			ret = cty.TupleVal(vals)
   833  		} else {
   834  			ret = cty.EmptyTupleVal
   835  		}
   836  
   837  	case config.ForEach != nil:
   838  		vals := make(map[string]cty.Value)
   839  		for key, instance := range instances {
   840  			strKey, ok := key.(addrs.StringKey)
   841  			if !ok {
   842  				// old key that is being dropped and not used for evaluation
   843  				continue
   844  			}
   845  			vals[string(strKey)] = instance
   846  		}
   847  
   848  		if len(vals) > 0 {
   849  			// We use an object rather than a map here because resource schemas
   850  			// may include dynamically-typed attributes, which will then cause
   851  			// each instance to potentially have a different runtime type even
   852  			// though they all conform to the static schema.
   853  			ret = cty.ObjectVal(vals)
   854  		} else {
   855  			ret = cty.EmptyObjectVal
   856  		}
   857  
   858  	default:
   859  		val, ok := instances[addrs.NoKey]
   860  		if !ok {
   861  			// if the instance is missing, insert an unknown value
   862  			val = cty.UnknownVal(ty)
   863  		}
   864  
   865  		ret = val
   866  	}
   867  
   868  	return ret, diags
   869  }
   870  
   871  func (d *evaluationStateData) getResourceSchema(addr addrs.Resource, providerAddr addrs.Provider) *configschema.Block {
   872  	schema, _, err := d.Evaluator.Plugins.ResourceTypeSchema(providerAddr, addr.Mode, addr.Type)
   873  	if err != nil {
   874  		// We have plently other codepaths that will detect and report
   875  		// schema lookup errors before we'd reach this point, so we'll just
   876  		// treat a failure here the same as having no schema.
   877  		return nil
   878  	}
   879  	return schema
   880  }
   881  
   882  func (d *evaluationStateData) GetTerraformAttr(addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
   883  	var diags tfdiags.Diagnostics
   884  	switch addr.Name {
   885  
   886  	case "workspace":
   887  		workspaceName := d.Evaluator.Meta.Env
   888  		return cty.StringVal(workspaceName), diags
   889  
   890  	case "env":
   891  		// Prior to Terraform 0.12 there was an attribute "env", which was
   892  		// an alias name for "workspace". This was deprecated and is now
   893  		// removed.
   894  		diags = diags.Append(&hcl.Diagnostic{
   895  			Severity: hcl.DiagError,
   896  			Summary:  `Invalid "terraform" attribute`,
   897  			Detail:   `The terraform.env attribute was deprecated in v0.10 and removed in v0.12. The "state environment" concept was renamed to "workspace" in v0.12, and so the workspace name can now be accessed using the terraform.workspace attribute.`,
   898  			Subject:  rng.ToHCL().Ptr(),
   899  		})
   900  		return cty.DynamicVal, diags
   901  
   902  	default:
   903  		diags = diags.Append(&hcl.Diagnostic{
   904  			Severity: hcl.DiagError,
   905  			Summary:  `Invalid "terraform" attribute`,
   906  			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),
   907  			Subject:  rng.ToHCL().Ptr(),
   908  		})
   909  		return cty.DynamicVal, diags
   910  	}
   911  }
   912  
   913  // nameSuggestion tries to find a name from the given slice of suggested names
   914  // that is close to the given name and returns it if found. If no suggestion
   915  // is close enough, returns the empty string.
   916  //
   917  // The suggestions are tried in order, so earlier suggestions take precedence
   918  // if the given string is similar to two or more suggestions.
   919  //
   920  // This function is intended to be used with a relatively-small number of
   921  // suggestions. It's not optimized for hundreds or thousands of them.
   922  func nameSuggestion(given string, suggestions []string) string {
   923  	for _, suggestion := range suggestions {
   924  		dist := levenshtein.Distance(given, suggestion, nil)
   925  		if dist < 3 { // threshold determined experimentally
   926  			return suggestion
   927  		}
   928  	}
   929  	return ""
   930  }
   931  
   932  // moduleDisplayAddr returns a string describing the given module instance
   933  // address that is appropriate for returning to users in situations where the
   934  // root module is possible. Specifically, it returns "the root module" if the
   935  // root module instance is given, or a string representation of the module
   936  // address otherwise.
   937  func moduleDisplayAddr(addr addrs.ModuleInstance) string {
   938  	switch {
   939  	case addr.IsRoot():
   940  		return "the root module"
   941  	default:
   942  		return addr.String()
   943  	}
   944  }