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