github.com/opentofu/opentofu@v1.7.1/internal/tofu/eval_context_builtin.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  	"context"
    10  	"fmt"
    11  	"log"
    12  	"sync"
    13  
    14  	"github.com/hashicorp/hcl/v2"
    15  	"github.com/hashicorp/hcl/v2/hclsyntax"
    16  	"github.com/zclconf/go-cty/cty"
    17  	"github.com/zclconf/go-cty/cty/function"
    18  
    19  	"github.com/opentofu/opentofu/internal/addrs"
    20  	"github.com/opentofu/opentofu/internal/checks"
    21  	"github.com/opentofu/opentofu/internal/configs/configschema"
    22  	"github.com/opentofu/opentofu/internal/encryption"
    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/providers"
    28  	"github.com/opentofu/opentofu/internal/provisioners"
    29  	"github.com/opentofu/opentofu/internal/refactoring"
    30  	"github.com/opentofu/opentofu/internal/states"
    31  	"github.com/opentofu/opentofu/internal/tfdiags"
    32  	"github.com/opentofu/opentofu/version"
    33  )
    34  
    35  // BuiltinEvalContext is an EvalContext implementation that is used by
    36  // OpenTofu by default.
    37  type BuiltinEvalContext struct {
    38  	// StopContext is the context used to track whether we're complete
    39  	StopContext context.Context
    40  
    41  	// PathValue is the Path that this context is operating within.
    42  	PathValue addrs.ModuleInstance
    43  
    44  	// pathSet indicates that this context was explicitly created for a
    45  	// specific path, and can be safely used for evaluation. This lets us
    46  	// differentiate between PathValue being unset, and the zero value which is
    47  	// equivalent to RootModuleInstance.  Path and Evaluation methods will
    48  	// panic if this is not set.
    49  	pathSet bool
    50  
    51  	// Evaluator is used for evaluating expressions within the scope of this
    52  	// eval context.
    53  	Evaluator *Evaluator
    54  
    55  	// VariableValues contains the variable values across all modules. This
    56  	// structure is shared across the entire containing context, and so it
    57  	// may be accessed only when holding VariableValuesLock.
    58  	// The keys of the first level of VariableValues are the string
    59  	// representations of addrs.ModuleInstance values. The second-level keys
    60  	// are variable names within each module instance.
    61  	VariableValues     map[string]map[string]cty.Value
    62  	VariableValuesLock *sync.Mutex
    63  
    64  	// Plugins is a library of plugin components (providers and provisioners)
    65  	// available for use during a graph walk.
    66  	Plugins *contextPlugins
    67  
    68  	Hooks                 []Hook
    69  	InputValue            UIInput
    70  	ProviderCache         map[string]providers.Interface
    71  	ProviderInputConfig   map[string]map[string]cty.Value
    72  	ProviderLock          *sync.Mutex
    73  	ProvisionerCache      map[string]provisioners.Interface
    74  	ProvisionerLock       *sync.Mutex
    75  	ChangesValue          *plans.ChangesSync
    76  	StateValue            *states.SyncState
    77  	ChecksValue           *checks.State
    78  	RefreshStateValue     *states.SyncState
    79  	PrevRunStateValue     *states.SyncState
    80  	InstanceExpanderValue *instances.Expander
    81  	MoveResultsValue      refactoring.MoveResults
    82  	ImportResolverValue   *ImportResolver
    83  	Encryption            encryption.Encryption
    84  }
    85  
    86  // BuiltinEvalContext implements EvalContext
    87  var _ EvalContext = (*BuiltinEvalContext)(nil)
    88  
    89  func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext {
    90  	newCtx := *ctx
    91  	newCtx.pathSet = true
    92  	newCtx.PathValue = path
    93  	return &newCtx
    94  }
    95  
    96  func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} {
    97  	// This can happen during tests. During tests, we just block forever.
    98  	if ctx.StopContext == nil {
    99  		return nil
   100  	}
   101  
   102  	return ctx.StopContext.Done()
   103  }
   104  
   105  func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
   106  	for _, h := range ctx.Hooks {
   107  		action, err := fn(h)
   108  		if err != nil {
   109  			return err
   110  		}
   111  
   112  		switch action {
   113  		case HookActionContinue:
   114  			continue
   115  		case HookActionHalt:
   116  			// Return an early exit error to trigger an early exit
   117  			log.Printf("[WARN] Early exit triggered by hook: %T", h)
   118  			return nil
   119  		}
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  func (ctx *BuiltinEvalContext) Input() UIInput {
   126  	return ctx.InputValue
   127  }
   128  
   129  func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) {
   130  	ctx.ProviderLock.Lock()
   131  	defer ctx.ProviderLock.Unlock()
   132  
   133  	key := addr.String()
   134  
   135  	// If we have already initialized, it is an error
   136  	if _, ok := ctx.ProviderCache[key]; ok {
   137  		return nil, fmt.Errorf("%s is already initialized", addr)
   138  	}
   139  
   140  	p, err := ctx.Plugins.NewProviderInstance(addr.Provider)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", addr.String(), addr)
   146  	ctx.ProviderCache[key] = p
   147  
   148  	return p, nil
   149  }
   150  
   151  func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface {
   152  	ctx.ProviderLock.Lock()
   153  	defer ctx.ProviderLock.Unlock()
   154  
   155  	return ctx.ProviderCache[addr.String()]
   156  }
   157  
   158  func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (providers.ProviderSchema, error) {
   159  	return ctx.Plugins.ProviderSchema(addr.Provider)
   160  }
   161  
   162  func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error {
   163  	ctx.ProviderLock.Lock()
   164  	defer ctx.ProviderLock.Unlock()
   165  
   166  	key := addr.String()
   167  	provider := ctx.ProviderCache[key]
   168  	if provider != nil {
   169  		delete(ctx.ProviderCache, key)
   170  		return provider.Close()
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, cfg cty.Value) tfdiags.Diagnostics {
   177  	var diags tfdiags.Diagnostics
   178  	if !addr.Module.Equal(ctx.Path().Module()) {
   179  		// This indicates incorrect use of ConfigureProvider: it should be used
   180  		// only from the module that the provider configuration belongs to.
   181  		panic(fmt.Sprintf("%s configured by wrong module %s", addr, ctx.Path()))
   182  	}
   183  
   184  	p := ctx.Provider(addr)
   185  	if p == nil {
   186  		diags = diags.Append(fmt.Errorf("%s not initialized", addr))
   187  		return diags
   188  	}
   189  
   190  	req := providers.ConfigureProviderRequest{
   191  		TerraformVersion: version.String(),
   192  		Config:           cfg,
   193  	}
   194  
   195  	resp := p.ConfigureProvider(req)
   196  	return resp.Diagnostics
   197  }
   198  
   199  func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.AbsProviderConfig) map[string]cty.Value {
   200  	ctx.ProviderLock.Lock()
   201  	defer ctx.ProviderLock.Unlock()
   202  
   203  	if !pc.Module.Equal(ctx.Path().Module()) {
   204  		// This indicates incorrect use of InitProvider: it should be used
   205  		// only from the module that the provider configuration belongs to.
   206  		panic(fmt.Sprintf("%s initialized by wrong module %s", pc, ctx.Path()))
   207  	}
   208  
   209  	if !ctx.Path().IsRoot() {
   210  		// Only root module provider configurations can have input.
   211  		return nil
   212  	}
   213  
   214  	return ctx.ProviderInputConfig[pc.String()]
   215  }
   216  
   217  func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c map[string]cty.Value) {
   218  	absProvider := pc
   219  	if !pc.Module.IsRoot() {
   220  		// Only root module provider configurations can have input.
   221  		log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module")
   222  		return
   223  	}
   224  
   225  	// Save the configuration
   226  	ctx.ProviderLock.Lock()
   227  	ctx.ProviderInputConfig[absProvider.String()] = c
   228  	ctx.ProviderLock.Unlock()
   229  }
   230  
   231  func (ctx *BuiltinEvalContext) Provisioner(n string) (provisioners.Interface, error) {
   232  	ctx.ProvisionerLock.Lock()
   233  	defer ctx.ProvisionerLock.Unlock()
   234  
   235  	p, ok := ctx.ProvisionerCache[n]
   236  	if !ok {
   237  		var err error
   238  		p, err = ctx.Plugins.NewProvisionerInstance(n)
   239  		if err != nil {
   240  			return nil, err
   241  		}
   242  
   243  		ctx.ProvisionerCache[n] = p
   244  	}
   245  
   246  	return p, nil
   247  }
   248  
   249  func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) {
   250  	return ctx.Plugins.ProvisionerSchema(n)
   251  }
   252  
   253  func (ctx *BuiltinEvalContext) CloseProvisioners() error {
   254  	var diags tfdiags.Diagnostics
   255  	ctx.ProvisionerLock.Lock()
   256  	defer ctx.ProvisionerLock.Unlock()
   257  
   258  	for name, prov := range ctx.ProvisionerCache {
   259  		err := prov.Close()
   260  		if err != nil {
   261  			diags = diags.Append(fmt.Errorf("provisioner.Close %s: %w", name, err))
   262  		}
   263  	}
   264  
   265  	return diags.Err()
   266  }
   267  
   268  func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
   269  	var diags tfdiags.Diagnostics
   270  	scope := ctx.EvaluationScope(self, nil, keyData)
   271  	body, evalDiags := scope.ExpandBlock(body, schema)
   272  	diags = diags.Append(evalDiags)
   273  	val, evalDiags := scope.EvalBlock(body, schema)
   274  	diags = diags.Append(evalDiags)
   275  	return val, body, diags
   276  }
   277  
   278  func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) {
   279  	scope := ctx.EvaluationScope(self, nil, EvalDataForNoInstanceKey)
   280  	return scope.EvalExpr(expr, wantType)
   281  }
   282  
   283  func (ctx *BuiltinEvalContext) EvaluateReplaceTriggeredBy(expr hcl.Expression, repData instances.RepetitionData) (*addrs.Reference, bool, tfdiags.Diagnostics) {
   284  
   285  	// get the reference to lookup changes in the plan
   286  	ref, diags := evalReplaceTriggeredByExpr(expr, repData)
   287  	if diags.HasErrors() {
   288  		return nil, false, diags
   289  	}
   290  
   291  	var changes []*plans.ResourceInstanceChangeSrc
   292  	// store the address once we get it for validation
   293  	var resourceAddr addrs.Resource
   294  
   295  	// The reference is either a resource or resource instance
   296  	switch sub := ref.Subject.(type) {
   297  	case addrs.Resource:
   298  		resourceAddr = sub
   299  		rc := sub.Absolute(ctx.Path())
   300  		changes = ctx.Changes().GetChangesForAbsResource(rc)
   301  	case addrs.ResourceInstance:
   302  		resourceAddr = sub.ContainingResource()
   303  		rc := sub.Absolute(ctx.Path())
   304  		change := ctx.Changes().GetResourceInstanceChange(rc, states.CurrentGen)
   305  		if change != nil {
   306  			// we'll generate an error below if there was no change
   307  			changes = append(changes, change)
   308  		}
   309  	}
   310  
   311  	// Do some validation to make sure we are expecting a change at all
   312  	cfg := ctx.Evaluator.Config.Descendent(ctx.Path().Module())
   313  	resCfg := cfg.Module.ResourceByAddr(resourceAddr)
   314  	if resCfg == nil {
   315  		diags = diags.Append(&hcl.Diagnostic{
   316  			Severity: hcl.DiagError,
   317  			Summary:  `Reference to undeclared resource`,
   318  			Detail:   fmt.Sprintf(`A resource %s has not been declared in %s`, ref.Subject, moduleDisplayAddr(ctx.Path())),
   319  			Subject:  expr.Range().Ptr(),
   320  		})
   321  		return nil, false, diags
   322  	}
   323  
   324  	if len(changes) == 0 {
   325  		// If the resource is valid there should always be at least one change.
   326  		diags = diags.Append(fmt.Errorf("no change found for %s in %s", ref.Subject, moduleDisplayAddr(ctx.Path())))
   327  		return nil, false, diags
   328  	}
   329  
   330  	// If we don't have a traversal beyond the resource, then we can just look
   331  	// for any change.
   332  	if len(ref.Remaining) == 0 {
   333  		for _, c := range changes {
   334  			switch c.ChangeSrc.Action {
   335  			// Only immediate changes to the resource will trigger replacement.
   336  			case plans.Update, plans.DeleteThenCreate, plans.CreateThenDelete:
   337  				return ref, true, diags
   338  			}
   339  		}
   340  
   341  		// no change triggered
   342  		return nil, false, diags
   343  	}
   344  
   345  	// This must be an instances to have a remaining traversal, which means a
   346  	// single change.
   347  	change := changes[0]
   348  
   349  	// Make sure the change is actionable. A create or delete action will have
   350  	// a change in value, but are not valid for our purposes here.
   351  	switch change.ChangeSrc.Action {
   352  	case plans.Update, plans.DeleteThenCreate, plans.CreateThenDelete:
   353  		// OK
   354  	default:
   355  		return nil, false, diags
   356  	}
   357  
   358  	// Since we have a traversal after the resource reference, we will need to
   359  	// decode the changes, which means we need a schema.
   360  	providerAddr := change.ProviderAddr
   361  	schema, err := ctx.ProviderSchema(providerAddr)
   362  	if err != nil {
   363  		diags = diags.Append(err)
   364  		return nil, false, diags
   365  	}
   366  
   367  	resAddr := change.Addr.ContainingResource().Resource
   368  	resSchema, _ := schema.SchemaForResourceType(resAddr.Mode, resAddr.Type)
   369  	ty := resSchema.ImpliedType()
   370  
   371  	before, err := change.ChangeSrc.Before.Decode(ty)
   372  	if err != nil {
   373  		diags = diags.Append(err)
   374  		return nil, false, diags
   375  	}
   376  
   377  	after, err := change.ChangeSrc.After.Decode(ty)
   378  	if err != nil {
   379  		diags = diags.Append(err)
   380  		return nil, false, diags
   381  	}
   382  
   383  	path := traversalToPath(ref.Remaining)
   384  	attrBefore, _ := path.Apply(before)
   385  	attrAfter, _ := path.Apply(after)
   386  
   387  	if attrBefore == cty.NilVal || attrAfter == cty.NilVal {
   388  		replace := attrBefore != attrAfter
   389  		return ref, replace, diags
   390  	}
   391  
   392  	replace := !attrBefore.RawEquals(attrAfter)
   393  
   394  	return ref, replace, diags
   395  }
   396  
   397  // EvaluateImportAddress takes the raw reference expression of the import address
   398  // from the config, and returns the evaluated address addrs.AbsResourceInstance
   399  //
   400  // The implementation is inspired by config.AbsTraversalForImportToExpr, but this time we can evaluate the expression
   401  // in the indexes of expressions. If we encounter a hclsyntax.IndexExpr, we can evaluate the Key expression and create
   402  // an Index Traversal, adding it to the Traverser
   403  // TODO move this function into eval_import.go
   404  func (ctx *BuiltinEvalContext) EvaluateImportAddress(expr hcl.Expression, keyData instances.RepetitionData) (addrs.AbsResourceInstance, tfdiags.Diagnostics) {
   405  	traversal, diags := ctx.traversalForImportExpr(expr, keyData)
   406  	if diags.HasErrors() {
   407  		return addrs.AbsResourceInstance{}, diags
   408  	}
   409  
   410  	return addrs.ParseAbsResourceInstance(traversal)
   411  }
   412  
   413  func (ctx *BuiltinEvalContext) traversalForImportExpr(expr hcl.Expression, keyData instances.RepetitionData) (traversal hcl.Traversal, diags tfdiags.Diagnostics) {
   414  	switch e := expr.(type) {
   415  	case *hclsyntax.IndexExpr:
   416  		t, d := ctx.traversalForImportExpr(e.Collection, keyData)
   417  		diags = diags.Append(d)
   418  		traversal = append(traversal, t...)
   419  
   420  		tIndex, dIndex := ctx.parseImportIndexKeyExpr(e.Key, keyData)
   421  		diags = diags.Append(dIndex)
   422  		traversal = append(traversal, tIndex)
   423  	case *hclsyntax.RelativeTraversalExpr:
   424  		t, d := ctx.traversalForImportExpr(e.Source, keyData)
   425  		diags = diags.Append(d)
   426  		traversal = append(traversal, t...)
   427  		traversal = append(traversal, e.Traversal...)
   428  	case *hclsyntax.ScopeTraversalExpr:
   429  		traversal = append(traversal, e.Traversal...)
   430  	default:
   431  		// This should not happen, as it should have failed validation earlier, in config.AbsTraversalForImportToExpr
   432  		diags = diags.Append(&hcl.Diagnostic{
   433  			Severity: hcl.DiagError,
   434  			Summary:  "Invalid import address expression",
   435  			Detail:   "Import address must be a reference to a resource's address, and only allows for indexing with dynamic keys. For example: module.my_module[expression1].aws_s3_bucket.my_buckets[expression2] for resources inside of modules, or simply aws_s3_bucket.my_bucket for a resource in the root module",
   436  			Subject:  expr.Range().Ptr(),
   437  		})
   438  	}
   439  	return
   440  }
   441  
   442  // parseImportIndexKeyExpr parses an expression that is used as a key in an index, of an HCL expression representing an
   443  // import target address, into a traversal of type hcl.TraverseIndex.
   444  // After evaluation, the expression must be known, not null, not sensitive, and must be a string (for_each) or a number
   445  // (count)
   446  func (ctx *BuiltinEvalContext) parseImportIndexKeyExpr(expr hcl.Expression, keyData instances.RepetitionData) (hcl.TraverseIndex, tfdiags.Diagnostics) {
   447  	idx := hcl.TraverseIndex{
   448  		SrcRange: expr.Range(),
   449  	}
   450  
   451  	// evaluate and take into consideration the for_each key (if exists)
   452  	val, diags := evaluateExprWithRepetitionData(ctx, expr, cty.DynamicPseudoType, keyData)
   453  	if diags.HasErrors() {
   454  		return idx, diags
   455  	}
   456  
   457  	if !val.IsKnown() {
   458  		diags = diags.Append(&hcl.Diagnostic{
   459  			Severity: hcl.DiagError,
   460  			Summary:  "Import block 'to' address contains an invalid key",
   461  			Detail:   "Import block contained a resource address using an index that will only be known after apply. Please ensure to use expressions that are known at plan time for the index of an import target address",
   462  			Subject:  expr.Range().Ptr(),
   463  		})
   464  		return idx, diags
   465  	}
   466  
   467  	if val.IsNull() {
   468  		diags = diags.Append(&hcl.Diagnostic{
   469  			Severity: hcl.DiagError,
   470  			Summary:  "Import block 'to' address contains an invalid key",
   471  			Detail:   "Import block contained a resource address using an index which is null. Please ensure the expression for the index is not null",
   472  			Subject:  expr.Range().Ptr(),
   473  		})
   474  		return idx, diags
   475  	}
   476  
   477  	if val.Type() != cty.String && val.Type() != cty.Number {
   478  		diags = diags.Append(&hcl.Diagnostic{
   479  			Severity: hcl.DiagError,
   480  			Summary:  "Import block 'to' address contains an invalid key",
   481  			Detail:   "Import block contained a resource address using an index which is not valid for a resource instance (not a string or a number). Please ensure the expression for the index is correct, and returns either a string or a number",
   482  			Subject:  expr.Range().Ptr(),
   483  		})
   484  		return idx, diags
   485  	}
   486  
   487  	unmarkedVal, valMarks := val.Unmark()
   488  	if _, sensitive := valMarks[marks.Sensitive]; sensitive {
   489  		diags = diags.Append(&hcl.Diagnostic{
   490  			Severity: hcl.DiagError,
   491  			Summary:  "Import block 'to' address contains an invalid key",
   492  			Detail:   "Import block contained a resource address using an index which is sensitive. Please ensure indexes used in the resource address of an import target are not sensitive",
   493  			Subject:  expr.Range().Ptr(),
   494  		})
   495  	}
   496  
   497  	idx.Key = unmarkedVal
   498  	return idx, diags
   499  }
   500  
   501  func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope {
   502  	if !ctx.pathSet {
   503  		panic("context path not set")
   504  	}
   505  	data := &evaluationStateData{
   506  		Evaluator:       ctx.Evaluator,
   507  		ModulePath:      ctx.PathValue,
   508  		InstanceKeyData: keyData,
   509  		Operation:       ctx.Evaluator.Operation,
   510  	}
   511  
   512  	// ctx.PathValue is the path of the module that contains whatever
   513  	// expression the caller will be trying to evaluate, so this will
   514  	// activate only the experiments from that particular module, to
   515  	// be consistent with how experiment checking in the "configs"
   516  	// package itself works. The nil check here is for robustness in
   517  	// incompletely-mocked testing situations; mc should never be nil in
   518  	// real situations.
   519  	mc := ctx.Evaluator.Config.DescendentForInstance(ctx.PathValue)
   520  
   521  	if mc == nil || mc.Module.ProviderRequirements == nil {
   522  		return ctx.Evaluator.Scope(data, self, source, nil)
   523  	}
   524  
   525  	scope := ctx.Evaluator.Scope(data, self, source, func(pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
   526  		return evalContextProviderFunction(ctx.Provider, mc, ctx.Evaluator.Operation, pf, rng)
   527  	})
   528  	scope.SetActiveExperiments(mc.Module.ActiveExperiments)
   529  
   530  	return scope
   531  }
   532  
   533  func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance {
   534  	if !ctx.pathSet {
   535  		panic("context path not set")
   536  	}
   537  	return ctx.PathValue
   538  }
   539  
   540  func (ctx *BuiltinEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) {
   541  	ctx.VariableValuesLock.Lock()
   542  	defer ctx.VariableValuesLock.Unlock()
   543  
   544  	log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", addr.Absolute(addrs.RootModuleInstance))
   545  	key := addrs.RootModuleInstance.String()
   546  	args := ctx.VariableValues[key]
   547  	if args == nil {
   548  		args = make(map[string]cty.Value)
   549  		ctx.VariableValues[key] = args
   550  	}
   551  	args[addr.Name] = v
   552  }
   553  
   554  func (ctx *BuiltinEvalContext) SetModuleCallArgument(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) {
   555  	ctx.VariableValuesLock.Lock()
   556  	defer ctx.VariableValuesLock.Unlock()
   557  
   558  	if !ctx.pathSet {
   559  		panic("context path not set")
   560  	}
   561  
   562  	childPath := callAddr.ModuleInstance(ctx.PathValue)
   563  	log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", varAddr.Absolute(childPath))
   564  	key := childPath.String()
   565  	args := ctx.VariableValues[key]
   566  	if args == nil {
   567  		args = make(map[string]cty.Value)
   568  		ctx.VariableValues[key] = args
   569  	}
   570  	args[varAddr.Name] = v
   571  }
   572  
   573  func (ctx *BuiltinEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value {
   574  	ctx.VariableValuesLock.Lock()
   575  	defer ctx.VariableValuesLock.Unlock()
   576  
   577  	modKey := addr.Module.String()
   578  	modVars := ctx.VariableValues[modKey]
   579  	val, ok := modVars[addr.Variable.Name]
   580  	if !ok {
   581  		return cty.DynamicVal
   582  	}
   583  	return val
   584  }
   585  
   586  func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync {
   587  	return ctx.ChangesValue
   588  }
   589  
   590  func (ctx *BuiltinEvalContext) State() *states.SyncState {
   591  	return ctx.StateValue
   592  }
   593  
   594  func (ctx *BuiltinEvalContext) Checks() *checks.State {
   595  	return ctx.ChecksValue
   596  }
   597  
   598  func (ctx *BuiltinEvalContext) RefreshState() *states.SyncState {
   599  	return ctx.RefreshStateValue
   600  }
   601  
   602  func (ctx *BuiltinEvalContext) PrevRunState() *states.SyncState {
   603  	return ctx.PrevRunStateValue
   604  }
   605  
   606  func (ctx *BuiltinEvalContext) InstanceExpander() *instances.Expander {
   607  	return ctx.InstanceExpanderValue
   608  }
   609  
   610  func (ctx *BuiltinEvalContext) MoveResults() refactoring.MoveResults {
   611  	return ctx.MoveResultsValue
   612  }
   613  
   614  func (ctx *BuiltinEvalContext) ImportResolver() *ImportResolver {
   615  	return ctx.ImportResolverValue
   616  }
   617  
   618  func (ctx *BuiltinEvalContext) GetEncryption() encryption.Encryption {
   619  	return ctx.Encryption
   620  }