github.com/opentofu/opentofu@v1.7.1/internal/tofu/evaluate.go (about)

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