configs

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: MPL-2.0 Imports: 45 Imported by: 0

Documentation

Overview

Package configs contains types that represent OpenTofu configurations and the different elements thereof.

The functionality in this package can be used for some static analyses of OpenTofu configurations, but this package generally exposes representations of the configuration source code rather than the result of evaluating these objects. The sibling package "lang" deals with evaluation of structures and expressions in the configuration.

Due to its close relationship with HCL, this package makes frequent use of types from the HCL API, including raw HCL diagnostic messages. Such diagnostics can be converted into OpenTofu-flavored diagnostics, if needed, using functions in the sibling package tfdiags.

The Parser type is the main entry-point into this package. The LoadConfigDir method can be used to load a single module directory, and then a full configuration (including any descendent modules) can be produced using the top-level BuildConfig method.

Index

Constants

View Source
const (
	// ApplyTestCommand causes the run block to execute a OpenTofu apply
	// operation.
	ApplyTestCommand TestCommand = 0

	// PlanTestCommand causes the run block to execute a OpenTofu plan
	// operation.
	PlanTestCommand TestCommand = 'P'

	// NormalTestMode causes the run block to execute in plans.NormalMode.
	NormalTestMode TestMode = 0

	// RefreshOnlyTestMode causes the run block to execute in
	// plans.RefreshOnlyMode.
	RefreshOnlyTestMode TestMode = 'R'
)
View Source
const (
	DefaultTestDirectory = "tests"
)
View Source
const LiveSidecarFilename = "estate.chdf.hcl"

LiveSidecarFilename is the name of the sidecar configuration file that can carry a module's live configuration instead of a "live" block inside a "terraform" block. The file's whole body is the live block's content - "estate", "policy", "record_store" - with no wrapper block around it.

The sidecar exists for ecosystem compatibility (GitHub issue #72): strict HCL parsers - stock tofu and terraform validate, tflint, editors - are right to reject an unknown block inside terraform{}, so the in-.tf form taxes any repository whose CI runs stock tooling over the same files. The sidecar is a file those tools never read (the extension is deliberately not .tf or .tofu), so adopting live markers adds one file and changes zero existing lines. It is still a checked-in, reviewed file and not a flag, which is the property Live's doc comment explains the mode depends on.

A module may use either form; both at once is refused in Module.appendFile, because a live configuration must have one source of truth.

Variables

View Source
var ResourceBlockSchema = &hcl.BodySchema{
	Attributes: commonResourceAttributes,
	Blocks: []hcl.BlockHeaderSchema{
		{Type: "locals"},
		{Type: "lifecycle"},
		{Type: "connection"},
		{Type: "provisioner", LabelNames: []string{"type"}},
		{Type: "_"},
	},
}

ResourceBlockSchema is the schema for a resource or data resource type within OpenTofu.

This schema is public as it is required elsewhere in order to validate and use generated config.

Functions

func IsCrossStackDataSource added in v0.3.0

func IsCrossStackDataSource(typeName string) bool

IsCrossStackDataSource reports whether typeName is one of the data source types that read another stack's recorded outputs rather than a live cloud resource - the union of IsTfeOutputs and IsRemoteState. Exported because the data-read phase draws its coverage line exactly here: a same-stack data source is an ordering problem the phase solves, a cross-stack one carries its own auth surface and failure modes and stays refused until its own stage ships (issue #179).

func IsEmptyDir

func IsEmptyDir(path string) (bool, error)

IsEmptyDir returns true if the given filesystem path contains no OpenTofu configuration files.

Unlike the methods of the Parser type, this function always consults the real filesystem, and thus it isn't appropriate to use when working with configuration loaded from a plan file.

func IsIgnoredFile

func IsIgnoredFile(name string) bool

IsIgnoredFile returns true if the given filename (which must not have a directory path ahead of it) should be ignored as e.g. an editor swap file.

func IsRemoteState added in v0.3.0

func IsRemoteState(typeName string) bool

IsRemoteState reports whether typeName is the builtin terraform_remote_state data source. It stays refused byte-for-byte until #179's stage 3 gives it its own read pipeline.

func IsTfeOutputs added in v0.3.0

func IsTfeOutputs(typeName string) bool

IsTfeOutputs reports whether typeName is the tfe provider's cross-stack output reader. Stage 2 of #179 gives it its own read pipeline; see IsRemoteState for the flavor that stays refused until stage 3.

func MergeBodies

func MergeBodies(base, override hcl.Body) hcl.Body

These were all moved to the hcl2shim package, but still have uses referenced from this package TODO Call sites through opentofu to these functions should be migrated to hcl2shim eventually and this file removed

func ModulesFromStringsForTesting

func ModulesFromStringsForTesting(t testing.TB, srcs map[string]string) map[addrs.ModuleSourceLocal]*Module

ModulesFromStringsForTesting calls ModuleFromStringForTesting for each element of the given map and then treats the map keys as local module source addresses to construct a map from source address to module.

As with ModuleFromStringForTesting, if any of the given configuration strings are invalid then this halts testing by calling testing.TB.FailNow, and experiments are always allowed. The map keys must also be valid local module source addresses.

func ParseProviderConfigCompact

func ParseProviderConfigCompact(traversal hcl.Traversal) (addrs.LocalProviderConfig, tfdiags.Diagnostics)

ParseProviderConfigCompact parses the given absolute traversal as a relative provider address in compact form. The following are examples of traversals that can be successfully parsed as compact relative provider configuration addresses:

  • aws
  • aws.foo

This function will panic if given a relative traversal.

If the returned diagnostics contains errors then the result value is invalid and must not be used.

func ParseProviderConfigCompactStr

func ParseProviderConfigCompactStr(str string) (addrs.LocalProviderConfig, tfdiags.Diagnostics)

ParseProviderConfigCompactStr is a helper wrapper around ParseProviderConfigCompact that takes a string and parses it with the HCL native syntax traversal parser before interpreting it.

This should be used only in specialized situations since it will cause the created references to not have any meaningful source location information. If a reference string is coming from a source that should be identified in error messages then the caller should instead parse it directly using a suitable function from the HCL API and pass the traversal itself to ParseProviderConfigCompact.

Error diagnostics are returned if either the parsing fails or the analysis of the traversal fails. There is no way for the caller to distinguish the two kinds of diagnostics programmatically. If error diagnostics are returned then the returned address is invalid.

func SynthBody

func SynthBody(filename string, values map[string]cty.Value) hcl.Body

Types

type Backend

type Backend struct {
	Type   string
	Config hcl.Body
	Eval   *StaticEvaluator

	TypeRange hcl.Range
	DeclRange hcl.Range
}

Backend represents a "backend" block inside a "terraform" block in a module or file.

func (*Backend) Decode

func (b *Backend) Decode(ctx context.Context, schema *configschema.Block) (cty.Value, hcl.Diagnostics)

func (*Backend) Hash

func (b *Backend) Hash(ctx context.Context, schema *configschema.Block) (int, hcl.Diagnostics)

Hash produces a hash value for the receiver that covers the type and the portions of the config that conform to the given schema.

If the config does not conform to the schema then the result is not meaningful for comparison since it will be based on an incomplete result.

As an exception, required attributes in the schema are treated as optional for the purpose of hashing, so that an incomplete configuration can still be hashed. Other errors, such as extraneous attributes, have no such special case.

type Check

type Check struct {
	Name string

	DataResource *Resource
	Asserts      []*CheckRule

	DeclRange hcl.Range
}

Check represents a configuration defined check block.

A check block contains 0-1 data blocks, and 0-n assert blocks. The check block will load the data block, and execute the assert blocks as check rules during the plan and apply OpenTofu operations.

func (Check) Accessible

func (c Check) Accessible(addr addrs.Referenceable) bool

func (Check) Addr

func (c Check) Addr() addrs.Check

type CheckRule

type CheckRule struct {
	// Condition is an expression that must evaluate to true if the condition
	// holds or false if it does not. If the expression produces an error then
	// that's considered to be a bug in the module defining the check.
	//
	// The available variables in a condition expression vary depending on what
	// a check is attached to. For example, validation rules attached to
	// input variables can only refer to the variable that is being validated.
	Condition hcl.Expression

	// ErrorMessage should be one or more full sentences, which should be in
	// English for consistency with the rest of the error message output but
	// can in practice be in any language. The message should describe what is
	// required for the condition to return true in a way that would make sense
	// to a caller of the module.
	//
	// The error message expression has the same variables available for
	// interpolation as the corresponding condition.
	ErrorMessage hcl.Expression

	DeclRange hcl.Range
}

CheckRule represents a configuration-defined validation rule, precondition, or postcondition. Blocks of this sort can appear in a few different places in configuration, including "validation" blocks for variables, and "precondition" and "postcondition" blocks for resources.

type CloudConfig

type CloudConfig struct {
	Config hcl.Body

	DeclRange hcl.Range
	// contains filtered or unexported fields
}

Cloud represents a "cloud" block inside a "terraform" block in a module or file.

func (*CloudConfig) ToBackendConfig

func (c *CloudConfig) ToBackendConfig() Backend

type Config

type Config struct {
	// RootModule points to the Config for the root module within the same
	// module tree as this module. If this module _is_ the root module then
	// this is self-referential.
	Root *Config

	// ParentModule points to the Config for the module that directly calls
	// this module. If this is the root module then this field is nil.
	Parent *Config

	// Path is a sequence of module logical names that traverse from the root
	// module to this config. Path is empty for the root module.
	//
	// This should only be used to display paths to the end-user in rare cases
	// where we are talking about the static module tree, before module calls
	// have been resolved. In most cases, an addrs.ModuleInstance describing
	// a node in the dynamic module tree is better, since it will then include
	// any keys resulting from evaluating "count" and "for_each" arguments.
	Path addrs.Module

	// ChildModules points to the Config for each of the direct child modules
	// called from this module. The keys in this map match the keys in
	// Module.ModuleCalls.
	Children map[string]*Config

	// Module points to the object describing the configuration for the
	// various elements (variables, resources, etc) defined by this module.
	Module *Module

	// CallRange is the source range for the header of the module block that
	// requested this module.
	//
	// This field is meaningless for the root module, where its contents are undefined.
	CallRange hcl.Range

	// SourceAddr is the source address that the referenced module was requested
	// from, as specified in configuration. SourceAddrRaw is the same
	// information, but as the raw string the user originally entered.
	//
	// These fields are meaningless for the root module, where their contents are undefined.
	SourceAddr    addrs.ModuleSource
	SourceAddrRaw string

	// SourceAddrRange is the location in the configuration source where the
	// SourceAddr value was set, for use in diagnostic messages.
	//
	// This field is meaningless for the root module, where its contents are undefined.
	SourceAddrRange hcl.Range

	// Version is the specific version that was selected for this module,
	// based on version constraints given in configuration.
	//
	// This field is nil if the module was loaded from a non-registry source,
	// since versions are not supported for other sources.
	//
	// This field is meaningless for the root module, where it will always
	// be nil.
	Version *version.Version
}

A Config is a node in the tree of modules within a configuration.

The module tree is constructed by following ModuleCall instances recursively through the root module transitively into descendent modules.

A module tree described in *this* package represents the static tree represented by configuration. During evaluation a static ModuleNode may expand into zero or more module instances depending on the use of count and for_each configuration attributes within each call.

func BuildConfig

func BuildConfig(ctx context.Context, root *Module, walker ModuleWalker) (*Config, hcl.Diagnostics)

BuildConfig constructs a Config from a root module by loading all of its descendent modules via the given ModuleWalker.

The result is a module tree that has so far only had basic module- and file-level invariants validated. If the returned diagnostics contains errors, the returned module tree may be incomplete but can still be used carefully for static analysis.

func NewEmptyConfig

func NewEmptyConfig() *Config

NewEmptyConfig constructs a single-node configuration tree with an empty root module. This is generally a pretty useless thing to do, so most callers should instead use BuildConfig.

func (*Config) AllModules

func (c *Config) AllModules() []*Config

AllModules returns a slice of all the receiver and all of its descendent nodes in the module tree, in the same order they would be visited by DeepEach.

func (*Config) DeepEach

func (c *Config) DeepEach(cb func(c *Config))

DeepEach calls the given function once for each module in the tree, starting with the receiver.

A parent is always called before its children and children of a particular node are visited in lexicographic order by their names.

func (*Config) Depth

func (c *Config) Depth() int

Depth returns the number of "hops" the receiver is from the root of its module tree, with the root module having a depth of zero.

func (*Config) Descendent

func (c *Config) Descendent(path addrs.Module) *Config

Descendent returns the descendent config that has the given path beneath the receiver, or nil if there is no such module.

The path traverses the static module tree, prior to any expansion to handle count and for_each arguments.

An empty path will just return the receiver, and is therefore pointless.

func (*Config) DescendentForInstance

func (c *Config) DescendentForInstance(path addrs.ModuleInstance) *Config

DescendentForInstance is like Descendent except that it accepts a path to a particular module instance in the dynamic module graph, returning the node from the static module graph that corresponds to it.

All instances created by a particular module call share the same configuration, so the keys within the given path are disregarded.

func (*Config) EntersNewPackage

func (c *Config) EntersNewPackage() bool

EntersNewPackage returns true if this call is to an external module, either directly via a remote source address or indirectly via a registry source address.

Other behaviors in OpenTofu may treat package crossings as a special situation, because that indicates that the caller and callee can change independently of one another and thus we should disallow using any features where the caller assumes anything about the callee other than its input variables, required provider configurations, and output values.

It's not meaningful to ask if the Config representing the root module enters a new package because the root module is always outside of all module packages, and so this function will arbitrarily return false in that case.

func (*Config) ProviderForConfigAddr

func (c *Config) ProviderForConfigAddr(addr addrs.LocalProviderConfig) addrs.Provider

ProviderForConfigAddr returns the FQN for a given addrs.ProviderConfig, first by checking for the provider in module.ProviderRequirements and falling back to addrs.NewDefaultProvider if it is not found.

func (*Config) ProviderRequirements

ProviderRequirements searches the full tree of modules under the receiver for both explicit and implicit dependencies on providers.

The result is a full manifest of all of the providers that must be available in order to work with the receiving configuration.

If the returned diagnostics includes errors then the resulting Requirements may be incomplete.

func (*Config) ProviderRequirementsByModule

func (c *Config) ProviderRequirementsByModule() (*ModuleRequirements, hcl.Diagnostics)

ProviderRequirementsByModule searches the full tree of modules under the receiver for both explicit and implicit dependencies on providers, constructing a tree where the requirements are broken out by module.

If the returned diagnostics includes errors then the resulting Requirements may be incomplete.

func (*Config) ProviderRequirementsShallow

func (c *Config) ProviderRequirementsShallow() (getproviders.Requirements, hcl.Diagnostics)

ProviderRequirementsShallow searches only the direct receiver for explicit and implicit dependencies on providers. Descendant modules are ignored.

If the returned diagnostics includes errors then the resulting Requirements may be incomplete.

func (*Config) ProviderTypes

func (c *Config) ProviderTypes() []addrs.Provider

ProviderTypes returns the FQNs of each distinct provider type referenced in the receiving configuration.

This is a helper for easily determining which provider types are required to fully interpret the configuration, though it does not include version information and so callers are expected to have already dealt with provider version selection in an earlier step and have identified suitable versions for each provider.

func (*Config) ResolveAbsProviderAddr

func (c *Config) ResolveAbsProviderAddr(addr addrs.ProviderConfig, inModule addrs.Module) addrs.AbsProviderConfig

ResolveAbsProviderAddr returns the AbsProviderConfig represented by the given ProviderConfig address, which must not be nil or this method will panic.

If the given address is already an AbsProviderConfig then this method returns it verbatim, and will always succeed. If it's a LocalProviderConfig then it will consult the local-to-FQN mapping table for the given module to find the absolute address corresponding to the given local one.

The module address to resolve local addresses in must be given in the second argument, and must refer to a module that exists under the receiver or else this method will panic.

func (*Config) TransformForTest

func (c *Config) TransformForTest(run *TestRun, file *TestFile, evalCtx *hcl.EvalContext) (func(), hcl.Diagnostics)

TransformForTest prepares the config to execute the given test.

This function directly edits the config that is to be tested, and returns a function that will reset the config back to its original state.

Tests will call this before they execute, and then call the deferred function to reset the config before the next test.

func (*Config) VerifyDependencySelections

func (c *Config) VerifyDependencySelections(depLocks *depsfile.Locks) []error

VerifyDependencySelections checks whether the given locked dependencies are acceptable for all of the version constraints reported in the configuration tree represented by the receiver.

This function will errors only if any of the locked dependencies are out of range for corresponding constraints in the configuration. If there are multiple inconsistencies then it will attempt to describe as many of them as possible, rather than stopping at the first problem.

It's typically the responsibility of "tofu init" to change the locked dependencies to conform with the configuration, and so VerifyDependencySelections is intended for other commands to check whether it did so correctly and to catch if anything has changed in configuration since the last "tofu init" which requires re-initialization. However, it's up to the caller to decide how to advise users recover from these errors, because the advise can vary depending on what operation the user is attempting.

type Connection

type Connection struct {
	Config hcl.Body

	DeclRange hcl.Range
}

Connection represents a "connection" block when used within either a "resource" or "provisioner" block in a module or file.

type Container

type Container interface {
	// Accessible should return true if the resource specified by addr can
	// reference other items within this Container.
	//
	// Typically, that means that addr will either be the container itself or
	// something within the container.
	Accessible(addr addrs.Referenceable) bool
}

Container provides an interface for scoped resources.

Any resources contained within a Container should not be accessible from outside the container.

type File

type File struct {
	Backends          []*Backend
	CloudConfigs      []*CloudConfig
	Lives             []*Live
	ProviderConfigs   []*Provider
	ProviderMetas     []*ProviderMeta
	RequiredProviders []*RequiredProviders
	Encryptions       []*config.EncryptionConfig

	Variables []*Variable
	Locals    []*Local
	Outputs   []*Output

	ModuleCalls []*ModuleCall

	ManagedResources   []*Resource
	DataResources      []*Resource
	EphemeralResources []*Resource

	Moved   []*Moved
	Import  []*Import
	Removed []*Removed

	Checks []*Check
}

File describes the contents of a single configuration file.

Individual files are not usually used alone, but rather combined together with other files (conventionally, those in the same directory) to produce a *Module, using NewModule.

At the level of an individual file we represent directly the structural elements present in the file, without any attempt to detect conflicting declarations. A File object can therefore be used for some basic static analysis of individual elements, but must be built into a Module to detect duplicate declarations.

type Import

type Import struct {
	ID hcl.Expression

	// Identity is an alternative to ID, which is used to identify the resource instance
	// by its 'Resource Identity'.
	Identity hcl.Expression

	// To is the address HCL expression given in the `import` block configuration.
	// It supports the following address formats:
	// - aws_s3_bucket.my_bucket
	// - module.my_module.aws_s3_bucket.my_bucket
	// - aws_s3_bucket.my_bucket["static_key"]
	// - module.my_module[0].aws_s3_bucket.my_buckets["static_key"]
	// - aws_s3_bucket.my_bucket[expression]
	// - module.my_module[expression].aws_s3_bucket.my_buckets[expression]
	// A dynamic instance key supports a dynamic expression like - a variable, a local, a condition (for example,
	//  ternary), a resource block attribute, a data block attribute, etc.
	To hcl.Expression
	// StaticTo is the corresponding resource and module that the address is referring to. When decoding, as long
	// as the `to` field is in the accepted format, we could determine the actual modules and resource that the
	// address represents. However, we do not yet know for certain what module instance and resource instance this
	// address refers to. So, Static import is mainly used to figure out the Module and Resource, and Provider of the
	// import target resource
	// If we could not determine the StaticTo when decoding the block, then the address is in an unacceptable format
	StaticTo addrs.ConfigResource
	// ResolvedTo will be a reference to the resource instance of the import target, if it can be resolved when decoding
	// the `import` block. If the `to` field does not represent a static address
	// (for example: module.my_module[var.var1].aws_s3_bucket.bucket), then this will be nil.
	// However, if the address is static and can be fully resolved at decode time
	// (for example: module.my_module[2].aws_s3_bucket.bucket), then this will be a reference to the resource instance's
	// address
	// Mainly used for early validations on the import block address, for example making sure there are no duplicate
	// import blocks targeting the same resource
	ResolvedTo *addrs.AbsResourceInstance

	ForEach hcl.Expression

	ProviderConfigRef *ProviderConfigRef
	Provider          addrs.Provider

	DeclRange         hcl.Range
	ProviderDeclRange hcl.Range
}

type Live

type Live struct {
	// Estate is the name of the estate this configuration owns, as it appears
	// in the tofu-estate marker described by live/MARKERS.md. It is
	// optional: when it is not set, the estate name is derived from the
	// tofu-estate tags the configuration already stamps, and a configuration
	// that stamps none (or several) is told to name one here.
	//
	// The value must be a literal string. An estate name assembled from a
	// variable would mean the identity of the thing that owns live resources
	// depends on how a run was invoked, and ownership records that move with
	// the invocation are not ownership records.
	Estate string

	// EstateSet distinguishes an absent estate argument from one set to the
	// empty string, so that the second can be an error rather than silently
	// meaning "derive it".
	EstateSet bool

	// EstateRange is where the "estate" argument was written, the zero value
	// when the block does not set it. It is recorded so that a diagnostic
	// about the estate can point at the argument that named it rather than at
	// the whole block, which is what DeclRange gives.
	EstateRange hcl.Range

	// DeclRange is the "live" block's own header, which is what a diagnostic
	// about the block as a whole points at - a backend beside it, or a
	// stateless refusal that has no more specific argument to name. For a
	// live configuration read from the sidecar file it is the start of that
	// file, so the same diagnostics point at the file instead.
	DeclRange hcl.Range

	// Sidecar records that this live configuration was read from the
	// [LiveSidecarFilename] sidecar file rather than from a live block inside
	// a terraform block. The two sources are equivalent everywhere else; the
	// field exists so that a module carrying both can be told exactly which
	// two places disagree, rather than getting the duplicate-block error
	// meant for two live blocks in .tf files.
	Sidecar bool

	// Policy is the optional nested "policy" block: one verb per ownership
	// quadrant (declared-in-source x carries-the-tag), plus the tag those
	// quadrants read and the delete quadrant's safety rails. Nil when the
	// live block sets no policy block at all, which must mean today's fixed
	// behavior (issue #67's "existing estates change nothing").
	//
	// This struct is the raw decode only - the literal string an author
	// wrote for each attribute, or its zero value with the matching *Set
	// flag false when they left it out. It deliberately knows nothing about
	// which verbs are valid for which quadrant: that is
	// internal/live/policy's ValidVerbs matrix, enforced at lint time by
	// internal/live/lint, the same layering every other semantic rule about
	// this fork's configuration follows (compare Estate above, whose own
	// grammar is checked by internal/live/discovery.ValidEstateName rather
	// than here).
	Policy *LivePolicy

	// RecordStore is the nested "record_store" block: where GitHub issue
	// #73's record-backed logical types (null_resource, terraform_data,
	// time_*, non-sensitive random_*) persist their micro-state, and where
	// issue #364's per-instance record will live.
	//
	// It is NEVER nil for a decoded live configuration. A live block that
	// declares no record_store block gets the implied local one
	// ([impliedRecordStore]): HANDOFF.md's "compatible out of the box" says
	// "a local record store is implied when none is declared, the way stock
	// implies local state", so declaring an estate is the whole setup step
	// and the record rung is available to every estate from its first run.
	// [LiveRecordStore.Implied] tells the two apart for a diagnostic that
	// needs to point at something the author actually wrote.
	//
	// A configuration with no live block at all still has no record store,
	// because a nil *Live has no fields: there is no estate, so there is
	// nothing to imply a store for. That is the case every reader must keep
	// treating as "no record store", and it is the only one left.
	//
	// See [LiveRecordStore].
	RecordStore *LiveRecordStore

	// Strict is the optional nested "strict" block: GitHub issue #365's
	// profile toggles, each one of the principles this fork exists for
	// turned into a setting whose default is today's behavior. Nil when the
	// live block sets no strict block at all, which must mean exactly what
	// a configuration written before the block existed got - the same
	// "absent means absent" contract Policy and RecordStore already follow,
	// and the thing that makes HANDOFF.md's "compatible out of the box"
	// true by construction rather than by review.
	//
	// Like [Live.Policy], this is the raw decode only: the literal string
	// an author wrote, with no opinion on whether it means anything. That
	// judgement needs internal/live/strict's vocabulary and belongs to
	// internal/live/lint.
	Strict *LiveStrict
}

Live represents a module's live configuration: a "live" block inside a "terraform" block, or the LiveSidecarFilename sidecar file, whose whole body is the same content the block would carry. Its presence is what puts a run into stateless mode: no state file, no backend, no lock, with prior state rebuilt from the live system on every operation.

It is deliberately a configuration block and not a command-line flag. Whether a team's infrastructure has an authoritative state file is a property of the configuration, checked in and reviewed with it; a flag would mean a run that forgot it silently fell back to writing a state file, which is exactly the failure this mode exists to remove.

The block is also why a stateless module cannot have a backend: a "backend" or "cloud" block alongside it is refused here, in the configuration decoder, rather than only by the stateless subset lint. The decoder is the earlier wall, and it is the one every command passes through - including the ones that would otherwise reach for a state manager before any stateless code ran.

type LivePolicy added in v0.3.0

type LivePolicy struct {
	// DeclaredTagged, DeclaredUntagged, UndeclaredTagged and
	// UndeclaredUntagged are the four quadrant verbs, named for their
	// policy-block attribute: "declared_tagged" and so on. Each is the
	// literal string an author wrote - "converge", "delete", or whatever
	// they typed, valid or not; a typo is caught by
	// internal/live/lint.checkLivePolicy, not here, because deciding
	// validity needs the per-quadrant matrix and this package does not
	// depend on it. The matching *Set field distinguishes an omitted
	// attribute (which resolves to today's fixed behavior for that
	// quadrant) from one written out.
	DeclaredTagged      string
	DeclaredTaggedSet   bool
	DeclaredTaggedRange hcl.Range

	DeclaredUntagged      string
	DeclaredUntaggedSet   bool
	DeclaredUntaggedRange hcl.Range

	UndeclaredTagged      string
	UndeclaredTaggedSet   bool
	UndeclaredTaggedRange hcl.Range

	UndeclaredUntagged      string
	UndeclaredUntaggedSet   bool
	UndeclaredUntaggedRange hcl.Range

	// TagKey and TagValue name the tag every quadrant's "tagged" half is
	// read against. Both are optional; omitted, they default to the estate
	// marker (internal/live/policy.Build fills in markers.TagEstate and
	// this estate's name), which is the reading every quadrant had before
	// this block existed. A configuration sets them to use a preservation
	// tag distinct from the estate marker instead - issue #67's "one
	// semantic question for the maintainer to confirm".
	TagKey      string
	TagKeySet   bool
	TagKeyRange hcl.Range

	TagValue      string
	TagValueSet   bool
	TagValueRange hcl.Range

	// Scope narrows a delete-quadrant verb's reach. Nil when the policy
	// block sets no scope block. internal/live/lint refuses a quadrant
	// explicitly assigned "delete" with no scope block at all - "an
	// unscoped account-wide purge is a lint refusal, not a default" per
	// issue #67 - so a Policy that later carries a delete verb and a nil
	// Scope can only happen if that check was skipped.
	Scope *LivePolicyScope

	// Threshold is the delete quadrant's first-run guard: a policy whose
	// delete quadrant would touch more resources than this refuses to
	// apply. Parsed and validated as a non-negative literal integer here;
	// enforcing it against an actual roster is the behavioral half's job
	// (issue #67, "First-run protection"), not this package's.
	Threshold      int
	ThresholdSet   bool
	ThresholdRange hcl.Range

	// DeclRange is the "policy" block's own header.
	DeclRange hcl.Range
}

LivePolicy is the "policy" block nested inside a live block. See Live.Policy.

type LivePolicyScope added in v0.3.0

type LivePolicyScope struct {
	// Services, Types and Regions each narrow what a delete-quadrant verb
	// may reach: provider service namespaces, resource type names, and
	// regions, respectively. Every one is optional and any combination may
	// be set; an empty list is indistinguishable from an absent attribute,
	// because in both cases nothing was named to narrow by, so
	// internal/live/lint requires at least one of the three to be
	// non-empty in a scope block that accompanies a delete quadrant.
	Services []string
	Types    []string
	Regions  []string

	// DeclRange is the "scope" block's own header.
	DeclRange hcl.Range
}

LivePolicyScope is the "scope" block nested inside a policy block. See LivePolicy.Scope.

type LiveRecordStore added in v0.3.0

type LiveRecordStore struct {
	// Type is the block's label: "local", "ssm", or "s3". Validated against
	// exactly those three spellings in decodeRecordStoreBlock; nothing else
	// reaches this field.
	Type      string
	TypeRange hcl.Range

	// Path is the "local" backend's directory, relative to the module
	// directory. Optional; empty means the caller's own default (a
	// ".tofu-records" directory beside the module, mirroring plain local
	// state's default filename). Literal string, relative, and confined to
	// the module directory: see validateRecordStorePath.
	Path      string
	PathSet   bool
	PathRange hcl.Range

	// Bucket is the "s3" backend's bucket name. Required for that backend;
	// unused by the other two.
	Bucket      string
	BucketSet   bool
	BucketRange hcl.Range

	// KeyPrefix overrides the "ssm" and "s3" backends' default key
	// namespace, which the caller derives from the estate name. Optional.
	// When set, it must stay disjoint from the receipts namespace
	// (live/RECEIPTS.md's "/tofu-receipts/<estate>/<effect>"): a key_prefix
	// whose first "/"-delimited segment is literally "tofu-receipts" is a
	// decode error, so a record can never be written where a receipt's own
	// namespace lives. See validateRecordStoreKeyPrefix.
	KeyPrefix      string
	KeyPrefixSet   bool
	KeyPrefixRange hcl.Range

	// Region overrides the "ssm" and "s3" backends' AWS region. Optional;
	// empty defers to the ordinary AWS SDK default-config chain (environment,
	// shared config, IMDS), the same as every other AWS client this fork
	// builds when a caller names no region.
	Region      string
	RegionSet   bool
	RegionRange hcl.Range

	// DeclRange is the "record_store" block's own header, or - for the
	// implied store - the live block's own header, since that is the
	// nearest thing the author wrote.
	DeclRange hcl.Range

	// Implied is true for the store [impliedRecordStore] fills in when the
	// live block declares no record_store block of its own.
	//
	// Nothing about where a record GOES may branch on this. An implied
	// local store and one written out as `record_store "local" {}` are the
	// same store, holding the same records under the same keys in the same
	// directory, and internal/live/lint's implied_record_store_test.go
	// asserts that by value. That is the whole point of implying it: a
	// reader deciding differently would make the default a third behavior
	// rather than the default one.
	//
	// What it is for is the question "did the author ASK for a store", which
	// is a different question and has exactly two readers today:
	//
	//   - internal/live/lint's strict-markers check, which refuses
	//     `markers "record"` without a declared store. That block is an
	//     author giving UP an available marker for a record, and naming
	//     where the record goes stays part of turning it on.
	//   - internal/command's statelessApplyGuidedDiscovery, which leaves
	//     guided discovery - an opt-in cost optimization that was reached
	//     by declaring a store - opt-in.
	//
	// It is also what a diagnostic reads to say "your live block has the
	// implied local record store" rather than pointing at a block the author
	// never wrote, and what a test reads to assert which of the two it got.
	Implied bool
}

LiveRecordStore is the "record_store" block nested inside a live block. Its label picks the backend ("local", "ssm", or "s3"), the same labeled-block-names-the-implementation shape a stock "backend" block uses, per issue #73's "phrased in familiar backend-like terms" ruling. See Live.RecordStore.

type LiveStrict added in v0.3.0

type LiveStrict struct {
	// MarkerRepair is the literal string an author wrote for the
	// "marker_repair" argument - "repair", "never", or whatever they typed,
	// valid or not. Validity is internal/live/strict's vocabulary, checked
	// at lint time by internal/live/lint.checkLiveStrict, for the same
	// reason [LivePolicy]'s quadrant verbs are checked there: this package
	// does not depend on that one.
	//
	// MarkerRepairSet distinguishes an omitted argument, which resolves to
	// internal/live/strict.DefaultMarkerRepair and therefore to today's
	// behavior, from one written out.
	MarkerRepair      string
	MarkerRepairSet   bool
	MarkerRepairRange hcl.Range

	// Secrets is the literal string an author wrote for the "secrets"
	// argument - "store", "refuse", or whatever they typed, valid or not.
	// Read the same way [LiveStrict.MarkerRepair] is, by the same decoder,
	// and judged the same place: internal/live/strict says what the
	// spellings mean and internal/live/lint refuses the ones that mean
	// nothing.
	//
	// SecretsSet distinguishes an omitted argument, which resolves to
	// internal/live/strict.DefaultSecrets, from one written out. The two
	// must behave identically for the default spelling - GitHub issue #101's
	// standing lesson - and the flag exists so that a reader can tell the
	// difference for a diagnostic's Subject range without changing the
	// verdict.
	Secrets      string
	SecretsSet   bool
	SecretsRange hcl.Range

	// NoSourceCreate is the literal string an author wrote for the
	// "no_source_create" argument - "refuse", "create", or whatever they
	// typed, valid or not. GitHub issue #365's ruling-4 toggle
	// (rfc/20260823-foundation-order-ruling.md): a no-source instance (no
	// record, no marker, and an identity nothing - neither the static
	// evaluator nor #388's plan-node seam - can derive) refuses by default;
	// this selects stock's own behavior of planning a create instead. Read
	// the same way [LiveStrict.Secrets] is, by the same decoder;
	// internal/live/strict says what the spellings mean.
	//
	// NoSourceCreateSet distinguishes an omitted argument, which resolves
	// to internal/live/strict.DefaultNoSourceCreate, from one written out,
	// the same reason [LiveStrict.SecretsSet] exists.
	NoSourceCreate      string
	NoSourceCreateSet   bool
	NoSourceCreateRange hcl.Range

	// MarkersRecord is the optional nested `markers "record"` block: which
	// resources hold their identity in the estate's record store instead of
	// in an ownership marker tag, HANDOFF.md's "per-type or per-address
	// markers = record, for tag budgets and tag policies, trading IAM
	// governability for a record-held identity". Nil when the strict block
	// declares no such block, which must mean today's behavior - every
	// taggable resource is marked.
	//
	// It is a LABELED block rather than an attribute because HANDOFF's
	// phrasing is shorthand for what is really a selection with two lists,
	// and because the label leaves room for the inverse selection
	// (`markers "tag"`) without a grammar change.
	//
	// Like every other field here this is the raw decode: two literal lists
	// of strings, with no opinion on whether the type names exist or the
	// addresses parse. That judgement needs internal/addrs' target grammar
	// and the provider's schemas, and belongs to internal/live/lint - the
	// same layering [Live.Estate] already has, whose grammar is checked by
	// internal/live/discovery.ValidEstateName rather than here.
	MarkersRecord *LiveStrictMarkers

	// DeclRange is the "strict" block's own header.
	DeclRange hcl.Range
}

LiveStrict is the "strict" block nested inside a live block. See Live.Strict.

type LiveStrictMarkers added in v0.3.0

type LiveStrictMarkers struct {
	// Kind is the block's label. "record" is the only one this fork knows
	// today; decodeStrictBlock refuses anything else, so nothing else
	// reaches this field.
	Kind      string
	KindRange hcl.Range

	// Types names resource types whose every instance this selection
	// covers, in the same literal-list-of-strings shape
	// [LivePolicyScope.Types] uses and read by the same decode helper.
	Types      []string
	TypesSet   bool
	TypesRange hcl.Range

	// Addresses names individual resources this selection covers, in the
	// `-target` grammar (internal/addrs' ParseTargetStr): module-qualified
	// or not, whole-resource, with no wildcards. Parsed by
	// internal/live/lint rather than here.
	Addresses      []string
	AddressesSet   bool
	AddressesRange hcl.Range

	// DeclRange is the "markers" block's own header.
	DeclRange hcl.Range
}

LiveStrictMarkers is a `markers "<kind>"` block nested inside a strict block. See LiveStrict.MarkersRecord.

type Local

type Local struct {
	Name string
	Expr hcl.Expression

	DeclRange hcl.Range
}

Local represents a single entry from a "locals" block in a module or file. The "locals" block itself is not represented, because it serves only to provide context for us to interpret its contents.

func (*Local) Addr

func (l *Local) Addr() addrs.LocalValue

Addr returns the address of the local value declared by the receiver, relative to its containing module.

type ManagedResource

type ManagedResource struct {
	Connection   *Connection
	Provisioners []*Provisioner

	CreateBeforeDestroy bool
	PreventDestroy      hcl.Expression
	// Destroy attribute indicates if the resource should be destroy once it is planned for destruction. This attribute corresponds to the `lifecycle.destroy` attribute.
	// The default behavior is to destroy the resource when it is planned for destruction, so the value of false will skip destroying the resource.
	// Note that the resource will still be removed from the state file even if Destroy is set to false but won't call the underlying provider for destruction.
	// This field will accept only constant boolean expressions. This is of type hcl.Expression to make future extensions of dynamic evaluation easier.
	Destroy          hcl.Expression
	IgnoreChanges    []hcl.Traversal
	IgnoreAllChanges bool

	CreateBeforeDestroySet bool
}

ManagedResource represents a "resource" block in a module or file.

type MockProvider

type MockProvider struct {
	Name       string
	NameRange  hcl.Range
	Alias      string
	AliasRange *hcl.Range // nil if no alias set

	DeclRange hcl.Range

	ForEach   hcl.Expression
	Instances map[addrs.InstanceKey]instances.RepetitionData

	MockResources     []*MockResource
	OverrideResources []*OverrideResource
}

MockProvider represents mocked provider block. It partially matches the Provider configuration block (name, alias) and includes additional mocking data (mock resources).

type MockResource

type MockResource struct {
	Mode     addrs.ResourceMode
	Type     string
	Defaults map[string]cty.Value
}

MockResource represents mocked resource. It is similar to OverrideResource, except all the resources with the same type should be overridden (mocked).

type Module

type Module struct {

	// Any other caller that constructs a module directly with NewModule may
	// assign a suitable value to this attribute before using it for other
	// purposes. It should be treated as immutable by all consumers of Module
	// values.
	SourceDir string

	Backend              *Backend
	CloudConfig          *CloudConfig
	Live                 *Live
	ProviderConfigs      map[string]*Provider
	ProviderRequirements *RequiredProviders
	ProviderLocalNames   map[addrs.Provider]string
	ProviderMetas        map[addrs.Provider]*ProviderMeta
	Encryption           *config.EncryptionConfig

	Variables map[string]*Variable
	Locals    map[string]*Local
	Outputs   map[string]*Output

	ModuleCalls map[string]*ModuleCall

	ManagedResources   map[string]*Resource
	DataResources      map[string]*Resource
	EphemeralResources map[string]*Resource

	Moved   []*Moved
	Import  []*Import
	Removed []*Removed

	Checks map[string]*Check

	Tests map[string]*TestFile

	// IsOverridden indicates if the module is being overridden. It's used in
	// testing framework to not call the underlying module.
	IsOverridden bool

	// StaticEvaluator is used to evaluate static expressions in the scope of the Module.
	StaticEvaluator *StaticEvaluator

	// ActiveExperiments is not currently used and so is always nil, but is
	// reserved to be a place to capture a module's active experiments if we
	// begin using language experiments in a later release.
	ActiveExperiments experiments.Set
}

Module is a container for a set of configuration constructs that are evaluated within a common namespace.

func ModuleFromStringForTesting

func ModuleFromStringForTesting(t testing.TB, src string) *Module

ModuleFromStringForTesting interprets the given string as if it were the content of a ".tofu" file in a module directory, parsing and decoding it as a single-file module.

Note that THIS FUNCTION DOES NOT PERFORM EARLY EVALUATION. This is intended mainly for an experimental new config evaluation strategy where early evaluation and config tree assembly are handled outside of this package.

If the configuration is not valid then this halts testing by calling testing.TB.FailNow.

Language experiments are always allowed in the "modules" loaded by this function.

func NewModule

func NewModule(primaryFiles, overrideFiles []*File, call StaticModuleCall, sourceDir string, load SelectiveLoader) (*Module, hcl.Diagnostics)

NewModule takes a list of primary files and a list of override files and produces a *Module by combining the files together.

If there are any conflicting declarations in the given files -- for example, if the same variable name is defined twice -- then the resulting module will be incomplete and error diagnostics will be returned. Careful static analysis of the returned Module is still possible in this case, but the module will probably not be semantically valid.

func NewModuleUneval

func NewModuleUneval(primaryFiles, overrideFiles []*File, sourceDir string, load SelectiveLoader) (*Module, hcl.Diagnostics)

NewModuleUneval is a variation of NewModule which performs only the static decoding steps and stops before performing any of the "early eval" steps, instead just returning with the results of early eval unpopulated.

This is currently here only in support of the experiment in internal/lang/eval, which wants to handle the situations where we currently rely on early eval in a different way. Outside of that experiment we should keep using NewModule in its entirety for now.

func NewModuleWithTests

func NewModuleWithTests(primaryFiles, overrideFiles []*File, testFiles map[string]*TestFile, call StaticModuleCall, sourceDir string) (*Module, hcl.Diagnostics)

NewModuleWithTests matches NewModule except it will also load in the provided test files.

func (*Module) EphemeralVariablesHints

func (m *Module) EphemeralVariablesHints() map[string]bool

EphemeralVariablesHints builds a map that indicates what variable name of the module is ephemeral and which isn't. This is used by the plan to know what variables are meant to be stored and which ones should be skipped.

func (*Module) GetProviderConfig

func (m *Module) GetProviderConfig(name, alias string) (*Provider, bool)

GetProviderConfig uses name and alias to find the respective Provider configuration.

func (*Module) ImpliedProviderForUnqualifiedType

func (m *Module) ImpliedProviderForUnqualifiedType(pType string) addrs.Provider

ImpliedProviderForUnqualifiedType returns the provider FQN for a given type, first by looking up the type in the provider requirements map, and falling back to an implied default provider.

The intended behaviour is that configuring a provider with local name "foo" in a required_providers block will result in resources with type "foo" using that provider.

func (*Module) LocalNameForProvider

func (m *Module) LocalNameForProvider(p addrs.Provider) string

LocalNameForProvider returns the module-specific user-supplied local name for a given provider FQN, or the default local name if none was supplied.

func (*Module) ProviderForLocalConfig

func (m *Module) ProviderForLocalConfig(pc addrs.LocalProviderConfig) addrs.Provider

ProviderForLocalConfig returns the provider FQN for a given LocalProviderConfig, based on its local name.

func (*Module) ResourceByAddr

func (m *Module) ResourceByAddr(addr addrs.Resource) *Resource

ResourceByAddr returns the configuration for the resource with the given address, or nil if there is no such resource.

type ModuleCall

type ModuleCall struct {
	Name string

	Source        hcl.Expression
	SourceAddrRaw string
	SourceAddr    addrs.ModuleSource
	SourceSet     bool

	// Used when building the corresponding StaticModuleCall
	Variables StaticModuleVariables
	Workspace string

	Config hcl.Body

	VersionAttr *hcl.Attribute
	Version     VersionConstraint

	Count   hcl.Expression
	ForEach hcl.Expression
	Enabled hcl.Expression

	Providers []PassedProviderConfig

	DependsOn []hcl.Traversal

	DeclRange hcl.Range
}

ModuleCall represents a "module" block in a module or file.

func (*ModuleCall) EntersNewPackage

func (mc *ModuleCall) EntersNewPackage() bool

EntersNewPackage returns true if this call is to an external module, either directly via a remote source address or indirectly via a registry source address.

Other behaviors in OpenTofu may treat package crossings as a special situation, because that indicates that the caller and callee can change independently of one another and thus we should disallow using any features where the caller assumes anything about the callee other than its input variables, required provider configurations, and output values.

func (*ModuleCall) VariablesUsing added in v0.3.0

func (mc *ModuleCall) VariablesUsing(ctx context.Context, eval *StaticEvaluator) StaticModuleVariables

VariablesUsing returns the StaticModuleVariables closure that evaluates this module call's own variable-assignment expressions - the `X = <expr>` arguments inside `module "name" { ... }` - through eval, the SAME construction [ModuleCall.decodeStaticVariables] uses to freeze mc.Variables once, when the module tree is first built.

Exposed so a caller resolving a reference that reaches back into this call's own module can rebuild the closure against an evaluator that actually carries that module's own per-request coverage - a data lookup, most concretely (issue #212) - rather than the frozen evaluator decodeStaticVariables captured at load time, before any caller had a chance to attach one. See StaticEvaluator.WithVariables, the seam that installs the result this method returns.

type ModuleRequest

type ModuleRequest struct {
	// Name is the "logical name" of the module call within configuration.
	// This is provided in case the name is used as part of a storage key
	// for the module, but implementations must otherwise treat it as an
	// opaque string. It is guaranteed to have already been validated as an
	// HCL identifier and UTF-8 encoded.
	Name string

	// Path is a list of logical names that traverse from the root module to
	// this module. This can be used, for example, to form a lookup key for
	// each distinct module call in a configuration, allowing for multiple
	// calls with the same name at different points in the tree.
	Path addrs.Module

	// SourceAddr is the source address string provided by the user in
	// configuration.
	SourceAddr addrs.ModuleSource

	// SourceAddrRange is the source range for the SourceAddr value as it
	// was provided in configuration. This can and should be used to generate
	// diagnostics about the source address having invalid syntax, referring
	// to a non-existent object, etc.
	SourceAddrRange hcl.Range

	// VersionConstraint is the version constraint applied to the module in
	// configuration. This data structure includes the source range for
	// the constraint, which can and should be used to generate diagnostics
	// about constraint-related issues, such as constraints that eliminate all
	// available versions of a module whose source is otherwise valid.
	VersionConstraint VersionConstraint

	// Parent is the partially-constructed module tree node that the loaded
	// module will be added to. Callers may refer to any field of this
	// structure except Children, which is still under construction when
	// ModuleRequest objects are created and thus has undefined content.
	// The main reason this is provided is so that full module paths can
	// be constructed for uniqueness.
	Parent *Config

	// CallRange is the source range for the header of the "module" block
	// in configuration that prompted this request. This can be used as the
	// subject of an error diagnostic that relates to the module call itself,
	// rather than to either its source address or its version number.
	CallRange hcl.Range

	// This is where variables and other information from the calling module
	// are propagated to the child module for use in the static evaluator
	Call StaticModuleCall
}

ModuleRequest is used with the ModuleWalker interface to describe a child module that must be loaded.

type ModuleRequirements

type ModuleRequirements struct {
	Name         string
	SourceAddr   addrs.ModuleSource
	SourceDir    string
	Requirements getproviders.Requirements
	Children     map[string]*ModuleRequirements
	Tests        map[string]*TestFileModuleRequirements
}

ModuleRequirements represents the provider requirements for an individual module, along with references to any child modules. This is used to determine which modules require which providers.

type ModuleWalker

type ModuleWalker interface {
	// LoadModule finds and loads a requested child module.
	//
	// If errors are detected during loading, implementations should return them
	// in the diagnostics object. If the diagnostics object contains any errors
	// then the caller will tolerate the returned module being nil or incomplete.
	// If no errors are returned, it should be non-nil and complete.
	//
	// Full validation need not have been performed but an implementation should
	// ensure that the basic file- and module-validations performed by the
	// LoadConfigDir function (valid syntax, no namespace collisions, etc) have
	// been performed before returning a module.
	LoadModule(ctx context.Context, req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics)
}

A ModuleWalker knows how to find and load a child module given details about the module to be loaded and a reference to its partially-loaded parent Config.

var DisabledModuleWalker ModuleWalker

DisabledModuleWalker is a ModuleWalker that doesn't support child modules at all, and so will return an error if asked to load one.

This is provided primarily for testing. There is no good reason to use this in the main application.

type ModuleWalkerFunc

type ModuleWalkerFunc func(ctx context.Context, req *ModuleRequest) (*Module, *version.Version, hcl.Diagnostics)

ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps a callback function, for more convenient use of that interface.

func (ModuleWalkerFunc) LoadModule

LoadModule implements ModuleWalker.

type Moved

type Moved struct {
	From *addrs.MoveEndpoint
	To   *addrs.MoveEndpoint

	DeclRange hcl.Range
}

type Output

type Output struct {
	Name        string
	Description string
	Expr        hcl.Expression
	DependsOn   []hcl.Traversal
	Sensitive   bool
	Deprecated  string
	Ephemeral   bool

	Preconditions []*CheckRule

	DescriptionSet bool
	SensitiveSet   bool
	EphemeralSet   bool

	DeclRange hcl.Range

	// IsOverridden indicates if the output is being overridden. It's used in
	// testing framework to not evaluate expression and use OverrideValue instead.
	IsOverridden bool
	// OverrideValue is only valid if IsOverridden is set to true. The value
	// should be used instead of evaluated expression. It's possible to have no
	// OverrideValue even with IsOverridden is set to true.
	OverrideValue *cty.Value
}

Output represents an "output" block in a module or file.

func (*Output) Addr

func (o *Output) Addr() addrs.OutputValue

func (*Output) UsageRange

func (o *Output) UsageRange() hcl.Range

UsageRange returns the location where the output value is configured, but if the expression is not configured then it returns the output definition location. Useful for generating diagnostics.

type OverrideModule

type OverrideModule struct {
	// Target references module call to override.
	Target       hcl.Traversal
	TargetParsed addrs.Module

	// Outputs represents fields to use instead
	// of the real module call output.
	Outputs map[string]cty.Value
}

OverrideModule contains information about a module to be overridden.

type OverrideResource

type OverrideResource struct {
	// Target references resource or data block to override.
	Target       hcl.Traversal
	TargetParsed *addrs.ConfigResource

	// Mode indicates if the Target is resource or data block.
	Mode addrs.ResourceMode

	// Values represents fields to use as defaults
	// if they are not present in configuration.
	Values map[string]cty.Value
}

OverrideResource contains information about a resource or data block to be overridden.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser is the main interface to read configuration files and other related files from disk.

It retains a cache of all files that are loaded so that they can be used to create source code snippets in diagnostics, etc.

func NewParser

func NewParser(fs afero.Fs) *Parser

NewParser creates and returns a new Parser that reads files from the given filesystem. If a nil filesystem is passed then the system's "real" filesystem will be used, via afero.OsFs.

func (Parser) ConfigDirFiles

func (p Parser) ConfigDirFiles(dir string) (primary, override []string, diags hcl.Diagnostics)

ConfigDirFiles returns lists of the primary and override files configuration files in the given directory.

If the given directory does not exist or cannot be read, error diagnostics are returned. If errors are returned, the resulting lists may be incomplete.

func (Parser) ConfigDirFilesWithTests

func (p Parser) ConfigDirFilesWithTests(dir string, testDirectory string) (primary, override, tests []string, diags hcl.Diagnostics)

ConfigDirFilesWithTests matches ConfigDirFiles except it also returns the paths to any test files within the module.

func (*Parser) ConfigFiles added in v0.3.0

func (p *Parser) ConfigFiles(dir string) (primary, override []string, diags hcl.Diagnostics)

ConfigFiles returns the configuration files Parser.LoadConfigDir would read in dir: the primary set and the override set, in the same order, with the same extension rules, the same ignored-file rules and the same .tf-shadowed-by-.tofu filtering.

It exists so that a caller which must REWRITE a module's source text - internal/live/onboard, computing the edit that turns a state-backed module into a live one - reads exactly the file set the loader will read back. Duplicating that selection has failed in this repository before: an ownership check filtered on ".tf" while the loader also accepted ".tf.json" and ".tofu", so the guard was narrower than the thing it guarded and said nothing about the files it missed. A rewriter with that bug is worse than a narrow guard: it would delete a backend block from one spelling of a file and leave the module carrying the backend that spelling of the loader still finds, then report the module as edited.

Test files are deliberately not returned. They are not part of the module and cannot carry a terraform block's backend.

func (*Parser) ForceFileSource

func (p *Parser) ForceFileSource(filename string, src []byte)

ForceFileSource artificially adds source code to the cache of file sources, as if it had been loaded from the given filename.

This should be used only in special situations where configuration is loaded some other way. Most callers should load configuration via methods of Parser, which will update the sources cache automatically.

func (*Parser) IsConfigDir

func (p *Parser) IsConfigDir(path string) bool

IsConfigDir determines whether the given path refers to a directory that exists and contains at least one OpenTofu config file (with a .tf or .tf.json extension.). Note, we explicitly exclude checking for tests here as tests must live alongside actual .tf config files.

func (*Parser) LoadConfigDir

func (p *Parser) LoadConfigDir(path string, call StaticModuleCall) (*Module, hcl.Diagnostics)

LoadConfigDir reads the .tf and .tf.json files in the given directory as config files (using LoadConfigFile) and then combines these files into a single Module.

If this method returns nil, that indicates that the given directory does not exist at all or could not be opened for some reason. Callers may wish to detect this case and ignore the returned diagnostics so that they can produce a more context-aware error message in that case.

If this method returns a non-nil module while error diagnostics are returned then the module may be incomplete but can be used carefully for static analysis.

This file does not consider a directory with no files to be an error, and will simply return an empty module in that case. Callers should first call Parser.IsConfigDir if they wish to recognize that situation.

.tf files are parsed using the HCL native syntax while .tf.json files are parsed using the HCL JSON syntax.

func (*Parser) LoadConfigDirSelective

func (p *Parser) LoadConfigDirSelective(path string, call StaticModuleCall, load SelectiveLoader) (*Module, hcl.Diagnostics)

func (*Parser) LoadConfigDirUneval

func (p *Parser) LoadConfigDirUneval(path string, load SelectiveLoader) (*Module, hcl.Diagnostics)

LoadConfigDirUneval is a variant of Parser.LoadConfigDir that only performs the static decoding step and does not perform early evaluation.

This is currently intended only for the experiment in internal/lang/eval, which wants to use a different strategy to meet the "early evaluation" use-cases. We should continue to use Parser.LoadConfigDir for all other callers for now.

func (*Parser) LoadConfigDirWithTests

func (p *Parser) LoadConfigDirWithTests(path string, testDirectory string, call StaticModuleCall) (*Module, hcl.Diagnostics)

LoadConfigDirWithTests matches LoadConfigDir, but the return Module also contains any relevant .tftest.hcl files.

func (*Parser) LoadConfigFile

func (p *Parser) LoadConfigFile(path string) (*File, hcl.Diagnostics)

LoadConfigFile reads the file at the given path and parses it as a config file.

If the file cannot be read -- for example, if it does not exist -- then a nil *File will be returned along with error diagnostics. Callers may wish to disregard the returned diagnostics in this case and instead generate their own error message(s) with additional context.

If the returned diagnostics has errors when a non-nil map is returned then the map may be incomplete but should be valid enough for careful static analysis.

This method wraps LoadHCLFile, and so it inherits the syntax selection behaviors documented for that method.

func (*Parser) LoadConfigFileOverride

func (p *Parser) LoadConfigFileOverride(path string) (*File, hcl.Diagnostics)

LoadConfigFileOverride is the same as LoadConfigFile except that it relaxes certain required attribute constraints in order to interpret the given file as an overrides file.

func (*Parser) LoadHCLFile

func (p *Parser) LoadHCLFile(path string) (hcl.Body, hcl.Diagnostics)

LoadHCLFile is a low-level method that reads the file at the given path, parses it, and returns the hcl.Body representing its root. In many cases it is better to use one of the other Load*File methods on this type, which additionally decode the root body in some way and return a higher-level construct.

If the file cannot be read at all -- e.g. because it does not exist -- then this method will return a nil body and error diagnostics. In this case callers may wish to ignore the provided error diagnostics and produce a more context-sensitive error instead.

The file will be parsed using the HCL native syntax unless the filename ends with ".json", in which case the HCL JSON syntax will be used.

func (*Parser) LoadTestFile

func (p *Parser) LoadTestFile(path string) (*TestFile, hcl.Diagnostics)

LoadTestFile reads the file at the given path and parses it as a OpenTofu test file.

It references the same LoadHCLFile as LoadConfigFile, so inherits the same syntax selection behaviours.

func (*Parser) Sources

func (p *Parser) Sources() map[string]*hcl.File

Sources returns a map of the cached source buffers for all files that have been loaded through this parser, with source filenames (as requested when each file was opened) as the keys.

type PassedProviderConfig

type PassedProviderConfig struct {
	InChild  *ProviderConfigRef
	InParent *ProviderConfigRef
}

PassedProviderConfig represents a provider config explicitly passed down to a child module, possibly giving it a new local address in the process.

type Provider

type Provider struct {
	Name       string
	NameRange  hcl.Range
	Alias      string
	AliasRange *hcl.Range // nil if no alias set

	Version VersionConstraint

	Config hcl.Body

	DeclRange hcl.Range

	// IsMocked indicates if this provider has been mocked. It is used in
	// testing framework to instantiate test provider wrapper.
	IsMocked          bool
	MockResources     []*MockResource
	OverrideResources []*OverrideResource

	ForEach   hcl.Expression
	Instances map[addrs.InstanceKey]instances.RepetitionData
	// contains filtered or unexported fields
}

Provider represents a "provider" block in a module or file. A provider block is a provider configuration, and there can be zero or more configurations for each actual provider.

func (*Provider) Addr

Addr returns the address of the receiving provider configuration, relative to its containing module.

type ProviderConfigRef

type ProviderConfigRef struct {
	Name       string
	NameRange  hcl.Range
	Alias      string
	AliasRange *hcl.Range // nil if alias not set

	KeyExpression hcl.Expression
	// contains filtered or unexported fields
}

func (*ProviderConfigRef) Addr

Addr returns the provider config address corresponding to the receiving config reference.

This is a trivial conversion, essentially just discarding the source location information and keeping just the addressing information.

func (*ProviderConfigRef) InstanceValidation

func (r *ProviderConfigRef) InstanceValidation(blockType string, isInstanced bool, hasConfig bool) hcl.Diagnostics

func (*ProviderConfigRef) String

func (r *ProviderConfigRef) String() string

type ProviderMeta

type ProviderMeta struct {
	Provider string
	Config   hcl.Body

	ProviderRange hcl.Range
	DeclRange     hcl.Range
}

ProviderMeta represents a "provider_meta" block inside a "terraform" block in a module or file.

type Provisioner

type Provisioner struct {
	Type       string
	Config     hcl.Body
	Connection *Connection
	When       ProvisionerWhen
	OnFailure  ProvisionerOnFailure

	DeclRange hcl.Range
	TypeRange hcl.Range
}

Provisioner represents a "provisioner" block when used within a "resource" block in a module or file.

type ProvisionerOnFailure

type ProvisionerOnFailure int

ProvisionerOnFailure is an enum for valid values for on_failure options for provisioners.

const (
	ProvisionerOnFailureInvalid ProvisionerOnFailure = iota
	ProvisionerOnFailureContinue
	ProvisionerOnFailureFail
)

func (ProvisionerOnFailure) String

func (i ProvisionerOnFailure) String() string

type ProvisionerWhen

type ProvisionerWhen int

ProvisionerWhen is an enum for valid values for when to run provisioners.

const (
	ProvisionerWhenInvalid ProvisionerWhen = iota
	ProvisionerWhenCreate
	ProvisionerWhenDestroy
)

func (ProvisionerWhen) String

func (i ProvisionerWhen) String() string

type ReferenceCategory added in v0.3.0

type ReferenceCategory string

ReferenceCategory classifies the kind of object a reference site named, derived structurally from ref.Subject's Go type at the point StaticValidateReferences refused it - never by parsing a diagnostic's rendered text. It rides in the diagnostic's Extra field (the same mechanism tfdiags.ExtraInfo already exposes for other diagnostic metadata), so a caller can recover it with tfdiags.ExtraInfo[ReferenceCategory](diag) instead of re-deriving it.

#178's scoping pass found the split between a same-stack data source and a cross-stack one load-bearing for its design question: a same-stack data source is an ordering problem this repo could plausibly solve with a pre-resolution read phase against the live provider, while a cross-stack one (terraform_remote_state, tfe_outputs) depends on a second state backend this fork does not open, and needs its own design call regardless of what the first gets.

const (
	CategoryManagedResource  ReferenceCategory = "managed_resource"
	CategoryDataSource       ReferenceCategory = "data_source"
	CategoryTfeOutputs       ReferenceCategory = "tfe_outputs"
	CategoryRemoteState      ReferenceCategory = "remote_state"
	CategoryModuleOutput     ReferenceCategory = "module_output"
	CategoryProviderFunction ReferenceCategory = "provider_function"
	CategoryOther            ReferenceCategory = "other"
)

type RefusedReference added in v0.3.0

type RefusedReference struct {
	// Category classifies the referenced object. See [ReferenceCategory].
	Category ReferenceCategory

	// Subject is the referenced object itself, module-relative, exactly as
	// the reference parser produced it.
	Subject addrs.Referenceable

	// Module is the module the referencing expression belongs to: the
	// evaluating module's call path, with no instance keys, because a static
	// evaluator is shared by every instance of its module.
	Module addrs.Module

	// NeededBy names the static identifier whose evaluation needed the
	// refused reference - the top of the evaluation stack, rendered the way
	// the diagnostic's own text renders it.
	NeededBy string
}

RefusedReference is the structured account of one reference [staticScopeData.StaticValidateReferences] refused, carried in the diagnostic's Extra field. It wraps the ReferenceCategory that has ridden there since #178 - tfdiags.ExtraInfo[ReferenceCategory] still finds it, through the unwrap chain - and adds the two facts a consumer needs to act on the refusal rather than merely count it: which object was referenced and in which module. The pre-resolution data-read phase (internal/live/dataread) is the consumer: it derives which data sources identity resolution demands from exactly these refusals.

func (RefusedReference) UnwrapDiagnosticExtra added in v0.3.0

func (r RefusedReference) UnwrapDiagnosticExtra() interface{}

UnwrapDiagnosticExtra keeps tfdiags.ExtraInfo[ReferenceCategory] working: the category used to BE the Extra value, and every consumer of it reads through the standard unwrap chain.

type Removed

type Removed struct {
	From *addrs.RemoveEndpoint

	Destroy    bool
	DestroySet bool

	Provisioners []*Provisioner

	DeclRange hcl.Range
}

Removed represents a removed block in the configuration.

type RequiredProvider

type RequiredProvider struct {
	Name        string
	Source      string
	Type        addrs.Provider
	Requirement VersionConstraint
	DeclRange   hcl.Range
	Aliases     []addrs.LocalProviderConfig
}

RequiredProvider represents a declaration of a dependency on a particular provider version or source without actually configuring that provider. This is used in child modules that expect a provider to be passed in from their parent.

type RequiredProviders

type RequiredProviders struct {
	RequiredProviders map[string]*RequiredProvider
	DeclRange         hcl.Range
}

type Resource

type Resource struct {
	Mode    addrs.ResourceMode
	Name    string
	Type    string
	Config  hcl.Body
	Count   hcl.Expression
	Enabled hcl.Expression
	ForEach hcl.Expression

	ProviderConfigRef *ProviderConfigRef
	Provider          addrs.Provider

	Preconditions  []*CheckRule
	Postconditions []*CheckRule

	DependsOn []hcl.Traversal

	TriggersReplacement []hcl.Expression

	// Managed is populated only for Mode = addrs.ManagedResourceMode,
	// containing the additional fields that apply to managed resources.
	// For all other resource modes, this field is nil.
	Managed *ManagedResource

	// Container links a scoped resource back up to the resources that contains
	// it. This field is referenced during static analysis to check whether any
	// references are also made from within the same container.
	//
	// If this is nil, then this resource is essentially public.
	Container Container

	// IsOverridden indicates if the resource is being overridden. It's used in
	// testing framework to not call the underlying provider.
	IsOverridden bool
	// OverrideValues are only valid if IsOverridden is set to true. The values
	// should be used to compose mock provider response. It is possible to have
	// zero-length OverrideValues even if IsOverridden is set to true.
	OverrideValues map[string]cty.Value

	DeclRange hcl.Range
	TypeRange hcl.Range
}

Resource represents a "resource" or "data" block in a module or file.

func (*Resource) Addr

func (r *Resource) Addr() addrs.Resource

Addr returns a resource address for the receiver that is relative to the resource's containing module.

func (*Resource) HasCustomConditions

func (r *Resource) HasCustomConditions() bool

HasCustomConditions returns true if and only if the resource has at least one author-specified custom condition.

func (*Resource) ProviderConfigAddr

func (r *Resource) ProviderConfigAddr() addrs.LocalProviderConfig

ProviderConfigAddr returns the address for the provider configuration that should be used for this resource. This function returns a default provider config addr if an explicit "provider" argument was not provided.

type SelectiveLoader

type SelectiveLoader int

SelectiveLoader allows the consumer to only load and validate the portions of files needed for the given operations/contexts

const (
	SelectiveLoadAll        SelectiveLoader = 0
	SelectiveLoadBackend    SelectiveLoader = 1
	SelectiveLoadEncryption SelectiveLoader = 2
)

type StaticDataLookup added in v0.3.0

type StaticDataLookup func(addr addrs.Resource) (cty.Value, bool)

StaticDataLookup answers a data-resource reference with a value read ahead of static evaluation, or reports that it has none. The address is the module-relative resource (addrs.Resource with addrs.DataResourceMode); the value is the whole resource's: the single instance's object for an unexpanded block, a tuple for count, an object keyed by string for for_each, matching how the plan-time evaluator shapes a resource reference so that instance indexing works unchanged.

type StaticEvaluator

type StaticEvaluator struct {
	// contains filtered or unexported fields
}

A static evaluator contains the information required to build a EvalContext which only understands "static" (non-state) data. Internally, it relies on staticData

func NewStaticEvaluator

func NewStaticEvaluator(mod *Module, call StaticModuleCall) *StaticEvaluator

Creates a static evaluator based from the given module and module call

func (StaticEvaluator) DecodeBlock

func (s StaticEvaluator) DecodeBlock(ctx context.Context, body hcl.Body, spec hcldec.Spec, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)

DecodeBlock decodes body against spec, expanding any "dynamic" block it contains first - the same two-phase split stock OpenTofu's own plan-time evaluator uses (internal/lang/eval.go's Scope.ExpandBlock, then Scope.EvalBlock): phase one resolves whatever a "dynamic" block's own for_each/iterator/labels arguments reference and expands the block; phase two resolves the expanded body's own references and decodes it.

Before this, DecodeBlock ran phase two alone, directly against the raw body. hcldec rejects a "dynamic" block outright ("Blocks of type \"dynamic\" are not expected here") because it is not a block type any schema ever declares, so a data source's arguments using one - an IAM policy document's `dynamic "statement" { ... }`, ordinary and common in real configurations - refused every time, never silently: the caller always got an error diagnostic, not a wrong or partial value. See internal/live/dataread's #212 fix, which is the concrete case that first exercised this.

func (StaticEvaluator) DecodeExpression

func (s StaticEvaluator) DecodeExpression(ctx context.Context, expr hcl.Expression, ident StaticIdentifier, val any) hcl.Diagnostics

func (StaticEvaluator) EvalContext

func (StaticEvaluator) EvalContextWithParent

func (s StaticEvaluator) EvalContextWithParent(ctx context.Context, parent *hcl.EvalContext, ident StaticIdentifier, refs []*addrs.Reference) (*hcl.EvalContext, hcl.Diagnostics)

func (StaticEvaluator) Evaluate

func (StaticEvaluator) EvaluateStructural added in v0.3.0

func (s StaticEvaluator) EvaluateStructural(ctx context.Context, expr hcl.Expression, ident StaticIdentifier) (cty.Value, hcl.Diagnostics)

EvaluateStructural evaluates expr the same way StaticEvaluator.Evaluate does, except a reference this evaluator refuses (a managed resource, a data source, a module output - see [staticScopeData.StaticValidateReferences]) does not automatically veto expr's own rendered VALUE the way Evaluate's EvalExpr does. Ordinary evaluation treats "some reference this expression transitively depends on was refused" and "the value this expression renders is unusable" as the same fact; they are not. A tuple literal's LENGTH is a property of its type, independent of any one element's contents, so length(var.x) for a var.x whose declared value is [{a=1,b=SOMETHING_DYNAMIC}] still has a real answer even though b does not - GetInputVariable (static_scope.go) already keeps that shape's TYPE on a refused reference rather than collapsing it to cty.DynamicVal's type-erased unknown, so this method's only job is to stop discarding the value the instant ANY refusal fired anywhere in the reference graph, and ask instead whether THIS expression's own rendered result came back usable regardless.

The gate is deliberately narrower than "ignore every error": every diagnostic has to carry a RefusedReference extra (attached only by StaticValidateReferences, meaning exactly "a reference named here is outside static scope" - never a parse error, a type mismatch, or a wrong argument count, none of which carry that extra), AND the rendered value has to come back non-null, wholly known and unmarked. Either condition failing returns the value and diagnostics exactly as computed, unfiltered - the same conservative default every refusal in this codebase falls back to.

Nothing else calls this yet: it is additive, so every existing caller of Evaluate/EvalExpr keeps its current behavior unchanged.

func (*StaticEvaluator) Pure

func (s *StaticEvaluator) Pure() *StaticEvaluator

Pure returns a copy of the evaluator whose scopes evaluate uuid(), timestamp() and bcrypt() to unknown values instead of calling them, the same way the plan walk does outside of apply (see lang.Scope.PureOnly and internal/tofu/evaluate.go).

The default is impure because the original callers of the static evaluator - module source, backend configuration, module call variables - all want whatever the function returns and consume it once, immediately.

A caller that is deriving a stable identity from configuration wants the opposite. An impure function evaluated here produces a value that is real, known, and different on the next run: for stateless mode's identity resolution that means a fabricated import ID, a plan that proposes to create something that already exists, and a leaked resource per run, with no diagnostic anywhere because nothing about the value looks wrong. Such a caller asks for a pure evaluator, and gets an unknown value it can refuse on rather than a plausible one it cannot tell from a real answer.

func (*StaticEvaluator) WithDataResults added in v0.3.0

func (s *StaticEvaluator) WithDataResults(lookup StaticDataLookup) *StaticEvaluator

WithDataResults returns a copy of the evaluator whose scopes answer data-resource references through the given lookup instead of refusing them as dynamic values.

The base evaluator refuses every resource reference, because static evaluation runs before anything has been read and inventing a value here would be a guess a later phase treats as fact. A caller that HAS performed reads - the pre-resolution data-read phase - hands the results in through this seam, and only references the lookup actually covers are permitted; everything else keeps refusing exactly as before.

A managed-mode reference is answerable only from the resource block's own configuration - never from a pre-plan read, because there is nothing such a read could honestly say about an object the plan may be about to change. internal/live/dataread's lookup answers one when the block's own body sets an argument of that name and the expression evaluates statically; the answer is partial by construction, so [lookupCoversTraversal] refuses a reference that does not take it down to one of those arguments.

func (*StaticEvaluator) WithFunctionOverrides added in v0.3.0

func (s *StaticEvaluator) WithFunctionOverrides(overrides map[string]function.Function) *StaticEvaluator

WithFunctionOverrides returns a copy of the evaluator whose scopes use overrides in place of the named entries of the base function table (lang.Scope.FuncOverrides), for every reference this evaluator resolves at any depth - the same "carries through nested scopes" property StaticEvaluator.WithRepetitionData documents, because [newStaticScope] builds every nested scope from this same *StaticEvaluator.

internal/live/dataread is the first and, as of this writing, only caller: issue #193's length()/keys() guard needs to see whether the OBJECT a data source's argument passes to either function is an unexpanded managed resource's own projection, a fact [configs.lookupCoversTraversal] and plain attribute access cannot be made to draw without over-refusing a legitimate for_each-expanded reference (see managedproj.go's doc). A function table override is the one seam that can see the argument VALUE at the exact place length()/keys() are invoked without changing what an ordinary `.subnet_id`-style reference resolves to, because it replaces only those two function-table entries and never touches attribute access at all.

func (*StaticEvaluator) WithModuleInstance added in v0.3.0

func (s *StaticEvaluator) WithModuleInstance(modInst addrs.ModuleInstance) *StaticEvaluator

WithModuleInstance returns a copy of the evaluator that knows WHICH instance of its module it is evaluating for, and therefore can answer this fork's one added evaluator symbol, markers.ModulePrefixAttr - see [staticScopeData.GetTerraformAttr].

It carries through nested scopes exactly as StaticEvaluator.WithRepetitionData does, and for the same mechanical reason: [newStaticScope] builds every nested scope from this same *StaticEvaluator, so a local value or module-call variable reached from the expression a caller hands to Evaluate sees the same instance.

The base evaluator does NOT know its module instance, and must not guess one. A StaticEvaluator belongs to a *configs.Module, and a module with a for_each'd or count'd call above it has one static evaluator shared by every instance of it - the exact fact issue #378 is about. Answering the symbol from an evaluator nobody threaded an instance into would answer it for whichever instance happened to be nearest, or for none, and a marker built on that answer is a WRONG marker on a real object rather than a missing one. So the refusal in GetTerraformAttr is the load-bearing half of this seam, not an edge case of it: this method exists to turn that refusal off for exactly the caller that has established the instance, and for nobody else.

A caller that genuinely evaluates for the root module passes addrs.RootModuleInstance, which is answered as a refusal of its own (the root has no module prefix; see markers.ModulePrefix) rather than as "not threaded" - the distinction the moduleInstanceSet flag exists for.

func (*StaticEvaluator) WithModuleOutputResults added in v0.3.0

func (s *StaticEvaluator) WithModuleOutputResults(lookup StaticModuleOutputLookup) *StaticEvaluator

WithModuleOutputResults returns a copy of the evaluator whose scopes answer a module-call reference through the given lookup instead of refusing it as unsupported in a static context, when the lookup covers that call.

This is [WithDataResults]'s exact counterpart for the one referenceable kind that seam does not cover: a data source's own argument, or a provider block's, can equally well read a child module's output (`name = module.eks.cluster_id`), and that reference is refused by a dedicated case in [staticScopeData.StaticValidateReferences], never reaching [staticScopeData.GetResource] or [StaticEvaluator.dataLookup] at all. A caller that can evaluate the child module's own output expression - internal/live/dataread, through the same live evaluator it already builds for that module's data sources and managed-resource projections - hands the result in through this seam.

Deliberately independent of [WithUnknownForRefusedReferences]: that seam turns EVERY uncovered managed, data or module-output reference into an unknown, which would make an uncovered managed reference stop raising the RefusedReference-tagged diagnostic [identity.DemandedManagedReads] reads - silently discarding the one signal a caller needs to know a live read would settle it. This seam keeps that discipline: only a call the lookup actually covers passes, in the same "covered or still refused" shape [dataLookup] already uses, and it changes nothing about how any other reference kind is handled.

func (*StaticEvaluator) WithRepetitionData added in v0.3.0

func (s *StaticEvaluator) WithRepetitionData(rd instances.RepetitionData) *StaticEvaluator

WithRepetitionData returns a copy of the evaluator whose scopes answer each.key, each.value and count.index from rd, for every reference this evaluator resolves at any depth - not only the expression a caller hands straight to StaticEvaluator.Evaluate, but also any local value or module-call variable that expression reaches through StaticEvaluator's own reference resolution (a local's own defining expression evaluates through a freshly built child scope, and that scope carries the same *StaticEvaluator, so it sees the same rd).

rd is a value bag, not a source of truth: whatever the caller passes is what a reference gets back, with no independent check that it matches the resource instance actually being evaluated. The caller - identity resolution's own per-instance expansion - is the party responsible for that, because only it knows which instance's arguments it is asking this evaluator to answer. A field of rd left at cty.NilVal (the zero value) means "not known for this instance", which behaves exactly as an evaluator built with no repetition data at all: an unset each.value under a for_each whose values are not statically known must still refuse a reference to it, not answer with something invented.

The base evaluator refuses every each/count reference (see [staticScopeData.GetCountAttr] and [staticScopeData.GetForEachAttr]), because plain static evaluation - module source, backend configuration, module call variables - has no notion of a resource instance at all. A caller resolving one instance's identity is the first caller of this package that does.

func (*StaticEvaluator) WithUnknownForRefusedReferences added in v0.3.0

func (s *StaticEvaluator) WithUnknownForRefusedReferences(outputs StaticModuleOutputLookup) *StaticEvaluator

WithUnknownForRefusedReferences returns a copy of the evaluator whose scopes substitute an UNKNOWN value for a managed-resource, data-source or module-output reference instead of refusing it, and which answer a module call by value through outputs where outputs can.

What this is for

Stock OpenTofu's plan-time evaluator has no strict/loose distinction to make. A reference it cannot answer yet becomes an unknown and evaluation continues, so an object with one apply-time leaf is a KNOWN object with an unknown attribute, and everything the configuration derives from the parts it did write down - a map's key set, a literal sibling, a conditional on a literal flag - still has an answer. This fork's static evaluator refuses the whole enclosing value instead, and that difference is choudoufu refusing where stock proceeds rather than a fact about the configuration.

[identity.resolver] already brought the two together for one shape, a module-call ARGUMENT whose skeleton is literal and one of whose leaves is not (internal/live/identity/partialargs.go). That rebuild works one constructor element at a time and therefore reaches only what the caller wrote out AT the call. The same poisoning happens one layer in - inside a local value, and inside a module output's own expression - where there is no constructor for a caller to rebuild, and this is the seam for it.

Why an unknown, and why that cannot widen what becomes a marker

The substitution is an unknown, never a guess, so any value that comes back KNOWN is independent of every reference that was substituted: it is the configuration's own literals, run through the configuration's own functions, exactly as stock would run them. A value that DID depend on a substituted leaf comes back unknown, and every path in this fork that turns a value into an identity demands a known one ([identity.staticSubValue] requires IsWhollyKnown, [identity.collectionKeyNames] rejects an unknown key, the resolver refuses an unknown identity argument), so a substituted leaf cannot become a marker. What can newly succeed is a count, a for_each key set, or an identity component that reads only what the configuration states outright.

What it deliberately does not tolerate

count.index, each.key, each.value and provider functions keep refusing exactly as they did. Repetition is answered by StaticEvaluator.WithRepetitionData from a caller that has established WHICH instance it is asking about; substituting an unknown for it instead would make an expansion that reads count.index appear answerable when nothing has established the instance at all. A provider function's answer comes from a provider that has not been started.

This is opt-in and additive: an evaluator nobody calls this on refuses every one of these references exactly as it always has.

func (*StaticEvaluator) WithVariables added in v0.3.0

func (s *StaticEvaluator) WithVariables(vars StaticModuleVariables) *StaticEvaluator

WithVariables returns a copy of the evaluator whose module call answers var.* references (see [staticScopeData.GetInputVariable]) through vars instead of the one captured when the module tree was first built.

The base evaluator's own vars closure (ModuleCall.Variables, built by [ModuleCall.decodeStaticVariables]) is frozen once, at load time, against this module's OWN *StaticEvaluator as it existed then - before any caller has ever had a chance to call StaticEvaluator.WithDataResults or StaticEvaluator.WithRepetitionData on anything, because no read or instance resolution has happened yet. That closure is a real *StaticEvaluator pointer captured by a Go closure, not re-derived per call, so every module-call variable it ever answers is evaluated with NO data-source coverage and NO repetition data, forever, for the lifetime of that *Config tree - regardless of what coverage a later caller attaches to ITS OWN evaluator for a descendant module.

That staleness is invisible for an ordinary var.X - source, count, for_each, module version - because those rarely depend on a data source or a resource instance's repetition. It stops being invisible the moment a descendant module's data source references var.X, and var.X's value, in the ANCESTOR, itself reads that ancestor's OWN data source (issue #212): the ancestor's frozen evaluator has never seen a data-source lookup at all, so the reference refuses as unreadable even when #179 stage 3 says this exact shape should be readable.

A caller that has built a "live" evaluator for the ancestor module - carrying that module's own StaticEvaluator.WithDataResults coverage, scoped to that module and no other (see internal/live/dataread's per-module lookup, never a lookup shared across two different modules) - uses WithVariables to re-point THIS module's own var.* resolution at that live ancestor evaluator instead of the frozen one, via ModuleCall.VariablesUsing. Every other field this evaluator carries (its own module, its own data lookup, its own repetition) is untouched: only how ITS OWN var.* references reach their ancestor's expression changes.

type StaticIdentifier

type StaticIdentifier struct {
	Module    addrs.Module
	Subject   string
	DeclRange hcl.Range
}

StaticIdentifier holds a Referenceable item and where it was declared

func (StaticIdentifier) String

func (ref StaticIdentifier) String() string

type StaticModuleCall

type StaticModuleCall struct {
	// contains filtered or unexported fields
}

StaticModuleCall contains the information required to call a given module

func NewStaticModuleCall

func NewStaticModuleCall(addr addrs.Module, declRange hcl.Range, vars StaticModuleVariables, rootPath string, workspace string) StaticModuleCall

func RootModuleCallForTesting

func RootModuleCallForTesting() StaticModuleCall

only used in testing

func (StaticModuleCall) Variables

func (StaticModuleCall) WithVariables

type StaticModuleOutputLookup added in v0.3.0

type StaticModuleOutputLookup func(call addrs.ModuleCall) (cty.Value, bool)

StaticModuleOutputLookup answers a module-call reference with the object of that call's own output values, or reports that it has none. The address is the call as written in the module doing the referring (`module.network` is addrs.ModuleCall{Name: "network"}); the value is the whole call's, an object with one attribute per output, matching how the plan-time evaluator shapes a module reference so that `module.network.configuration` resolves by ordinary attribute access.

It is consulted only by an evaluator built through StaticEvaluator.WithUnknownForRefusedReferences, and a false there is not a refusal: the reference becomes unknown like any other the tolerant scope cannot answer.

type StaticModuleVariables

type StaticModuleVariables func(v *Variable) (cty.Value, hcl.Diagnostics)

type TestCommand

type TestCommand rune

TestCommand represents the OpenTofu a given run block will execute, plan or apply. Defaults to apply.

type TestFile

type TestFile struct {
	// Variables defines a set of global variable definitions that should be set
	// for every run block within the test file.
	Variables map[string]hcl.Expression

	// Providers defines a set of providers that are available to run blocks
	// within this test file.
	//
	// If empty, tests should use the default providers for the module under
	// test.
	Providers map[string]*Provider

	// Runs defines the sequential list of run blocks that should be executed in
	// order.
	Runs []*TestRun

	// OverrideResources is a list of resources to be overridden with static values.
	// Underlying providers shouldn't be called for overridden resources.
	OverrideResources []*OverrideResource

	// OverrideModules is a list of modules to be overridden with static values.
	// Underlying modules shouldn't be called.
	OverrideModules []*OverrideModule

	// MockProviders is a map of providers that should be mocked. It is merged
	// with Providers map to use later when instantiating provider instance.
	MockProviders map[string]*MockProvider

	VariablesDeclRange hcl.Range
}

TestFile represents a single test file within a `tofu test` execution.

A test file is made up of a sequential list of run blocks, each designating a command to execute and a series of validations to check after the command.

func (*TestFile) Validate

func (file *TestFile) Validate() tfdiags.Diagnostics

Validate does a very simple and cursory check across the file blocks to look for simple issues we can highlight early on. It doesn't validate nested run blocks.

type TestFileModuleRequirements

type TestFileModuleRequirements struct {
	Requirements getproviders.Requirements
	Runs         map[string]*ModuleRequirements
}

TestFileModuleRequirements maps the runs for a given test file to the module requirements for that run block.

type TestMode

type TestMode rune

TestMode represents the plan mode that OpenTofu will use for a given run block, normal or refresh-only. Defaults to normal.

type TestRun

type TestRun struct {
	Name string

	// Command is the OpenTofu command to execute.
	//
	// One of ['apply', 'plan'].
	Command TestCommand

	// Options contains the embedded plan options that will affect the given
	// Command. These should map to the options documented here:
	//   - https://opentofu.org/docs/cli/commands/plan/#planning-options
	//
	// Note, that the Variables are a top level concept and not embedded within
	// the options despite being listed as plan options in the documentation.
	Options *TestRunOptions

	// Variables defines a set of variable definitions for this command.
	//
	// Any variables specified locally that clash with the global variables will
	// take precedence over the global definition.
	Variables map[string]hcl.Expression

	// Providers specifies the set of providers that should be loaded into the
	// module for this run block.
	//
	// Providers specified here must be configured in one of the provider blocks
	// for this file. If empty, the run block will load the default providers
	// for the module under test.
	Providers []PassedProviderConfig

	// CheckRules defines the list of assertions/validations that should be
	// checked by this run block.
	CheckRules []*CheckRule

	// Module defines an address of another module that should be loaded and
	// executed as part of this run block instead of the module under test.
	//
	// We support loading from all the module sources, like local directories, registry,
	// generic git repos, github, bitbucket, s3 and gcs repositories.
	Module *TestRunModuleCall

	// ConfigUnderTest describes the configuration this run block should execute
	// against.
	//
	// In typical cases, this will be null and the config under test is the
	// configuration within the directory the tofu test command is
	// executing within. However, when Module is set the config under test is
	// whichever config is defined by Module. This field is then set during the
	// configuration load process and should be used when the test is executed.
	ConfigUnderTest *Config

	// ExpectFailures should be a list of checkable objects that are expected
	// to report a failure from their custom conditions as part of this test
	// run.
	ExpectFailures []hcl.Traversal

	// OverrideResources is a list of resources to be overridden with static values.
	// Underlying providers shouldn't be called for overridden resources.
	OverrideResources []*OverrideResource

	// OverrideModules is a list of modules to be overridden with static values.
	// Underlying modules shouldn't be called.
	OverrideModules []*OverrideModule

	NameDeclRange      hcl.Range
	VariablesDeclRange hcl.Range
	DeclRange          hcl.Range
}

TestRun represents a single run block within a test file.

Each run block represents a single OpenTofu command to be executed and a set of validations to run after the command.

func (*TestRun) Validate

func (run *TestRun) Validate() tfdiags.Diagnostics

Validate does a very simple and cursory check across the run block to look for simple issues we can highlight early on.

type TestRunModuleCall

type TestRunModuleCall struct {
	// Source is the source of the module to test.
	Source addrs.ModuleSource

	// Version is the version of the module to load from the registry.
	Version VersionConstraint

	DeclRange       hcl.Range
	SourceDeclRange hcl.Range
}

TestRunModuleCall specifies which module should be executed by a given run block.

type TestRunOptions

type TestRunOptions struct {
	// Mode is the planning mode to run in. One of ['normal', 'refresh-only'].
	Mode TestMode

	// Refresh is analogous to the -refresh=false OpenTofu plan option.
	Refresh bool

	// Replace is analogous to the -refresh=ADDRESS OpenTofu plan option.
	Replace []hcl.Traversal

	// Target is analogous to the -target=ADDRESS OpenTofu plan option.
	Target []hcl.Traversal

	DeclRange hcl.Range
}

TestRunOptions contains the plan options for a given run block.

type Variable

type Variable struct {
	Name        string
	Description string
	Default     cty.Value

	// Only used inside modules that have *some* variable with ConstSet.
	// This allows us to match terraform's validation in their imitation
	// of our static eval concept.
	Const bool

	// Type is the concrete type of the variable value.
	Type cty.Type
	// ConstraintType is used for decoding and type conversions, and may
	// contain nested ObjectWithOptionalAttr types.
	ConstraintType cty.Type
	TypeDefaults   *typeexpr.Defaults

	ParsingMode VariableParsingMode
	Validations []*CheckRule
	Sensitive   bool
	Deprecated  string
	Ephemeral   bool

	DescriptionSet bool
	SensitiveSet   bool
	EphemeralSet   bool
	ConstSet       bool

	// Nullable indicates that null is a valid value for this variable. Setting
	// Nullable to false means that the module can expect this variable to
	// never be null.
	Nullable    bool
	NullableSet bool

	DeclRange hcl.Range
}

Variable represents a "variable" block in a module or file.

func (*Variable) Addr

func (v *Variable) Addr() addrs.InputVariable

func (*Variable) InputPrompt

func (v *Variable) InputPrompt() string

InputPrompt returns the text that will be shown during prompting for the variable input when required but no value given. This method is meant to return also the deprecated message together with the description when both exists or only the deprecated info when the description is missing. Other than these 2 cases, the method is keeping the default behavior in case of both, deprecated and description, are missing by returning the description. This will keep the previous behavior where during prompting will be shown only the variable name.

func (*Variable) Required

func (v *Variable) Required() bool

Required returns true if this variable is required to be set by the caller, or false if there is a default value that will be used when it isn't set.

type VariableParsingMode

type VariableParsingMode rune

VariableParsingMode defines how values of a particular variable given by text-only mechanisms (command line arguments and environment variables) should be parsed to produce the final value.

const VariableParseHCL VariableParsingMode = 'H'

VariableParseHCL is a variable parsing mode that attempts to parse the given string as an HCL expression and returns the result.

const VariableParseLiteral VariableParsingMode = 'L'

VariableParseLiteral is a variable parsing mode that just takes the given string directly as a cty.String value.

func (VariableParsingMode) Parse

func (m VariableParsingMode) Parse(name, value string) (cty.Value, hcl.Diagnostics)

Parse uses the receiving parsing mode to process the given variable value string, returning the result along with any diagnostics.

A VariableParsingMode does not know the expected type of the corresponding variable, so it's the caller's responsibility to attempt to convert the result to the appropriate type and return to the user any diagnostics that conversion may produce.

The given name is used to create a synthetic filename in case any diagnostics must be generated about the given string value. This should be the name of the root module variable whose value will be populated from the given string.

If the returned diagnostics has errors, the returned value may not be valid.

type VariableTypeHint

type VariableTypeHint rune

VariableTypeHint is an enumeration used for the Variable.TypeHint field, which is an incompletely-specified type for the variable which is used as a hint for whether a value provided in an ambiguous context (on the command line or in an environment variable) should be taken literally as a string or parsed as an HCL expression to produce a data structure.

The type hint is applied to runtime values as well, but since it does not accurately describe a precise type it is not fully-sufficient to infer the dynamic type of a value passed through a variable.

These hints use inaccurate terminology for historical reasons. Full details are in the documentation for each constant in this enumeration, but in summary:

  • TypeHintString requires a primitive type
  • TypeHintList requires a type that could be converted to a tuple
  • TypeHintMap requires a type that could be converted to an object
const TypeHintList VariableTypeHint = 'L'

TypeHintList indicates that a value provided in an ambiguous context should be treated as an HCL expression, and additionally requires that the runtime value for the variable is of an tuple, list, or set type.

const TypeHintMap VariableTypeHint = 'M'

TypeHintMap indicates that a value provided in an ambiguous context should be treated as an HCL expression, and additionally requires that the runtime value for the variable is of an object or map type.

const TypeHintNone VariableTypeHint = 0

TypeHintNone indicates the absence of a type hint. Values specified in ambiguous contexts will be treated as literal strings, as if TypeHintString were selected, but no runtime value checks will be applied. This is reasonable type hint for a module that is never intended to be used at the top-level of a configuration, since descendent modules never receive values from ambiguous contexts.

const TypeHintString VariableTypeHint = 'S'

TypeHintString spec indicates that a value provided in an ambiguous context should be treated as a literal string, and additionally requires that the runtime value for the variable is of a primitive type (string, number, bool).

func (VariableTypeHint) String

func (i VariableTypeHint) String() string

type VersionConstraint

type VersionConstraint struct {
	Required    version.Constraints
	RequiredSet versions.Set // New style
	DeclRange   hcl.Range
}

VersionConstraint represents a version constraint on some resource (e.g. OpenTofu Core, a provider, a module, ...) that carries with it a source range so that a helpful diagnostic can be printed in the event that a particular constraint does not match.

func (VersionConstraint) Check

func (v VersionConstraint) Check(ver *version.Version) bool

func (VersionConstraint) HasRequirements

func (v VersionConstraint) HasRequirements() bool

func (VersionConstraint) String

func (v VersionConstraint) String() string

Directories

Path Synopsis
Package configload knows how to install modules into the .terraform/modules directory and to load modules from those installed locations.
Package configload knows how to install modules into the .terraform/modules directory and to load modules from those installed locations.
Package configschema contains types for describing the expected structure of a configuration block whose shape is not known until runtime.
Package configschema contains types for describing the expected structure of a configuration block whose shape is not known until runtime.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL