References to Named Values
OpenTF makes several kinds of named values available. Each of these names is an expression that references the associated value. You can use them as standalone expressions, or combine them with other expressions to compute new values.
Types of Named Values
The main kinds of named values available in OpenTF are:
- Resources
- Input variables
- Local values
- Child module outputs
- Data sources
- Filesystem and workspace info
- Block-local values
The sections below explain each kind of named value in detail.
Although many of these names use dot-separated paths that resemble
attribute notation for elements of object values, they are not
implemented as real objects. This means you must use them exactly as written:
you cannot use square-bracket notation to replace the dot-separated paths, and
you cannot iterate over the "parent object" of a named entity; for example, you
cannot use aws_instance
in a for
expression to iterate over every AWS
instance resource.
Resources
<RESOURCE TYPE>.<NAME>
represents a managed resource of
the given type and name.
The value of a resource reference can vary, depending on whether the resource
uses count
or for_each
:
- If the resource doesn't use
count
orfor_each
, the reference's value is an object. The resource's attributes are elements of the object, and you can access them using dot or square bracket notation. - If the resource has the
count
argument set, the reference's value is a list of objects representing its instances. - If the resource has the
for_each
argument set, the reference's value is a map of objects representing its instances.
Any named value that does not match another pattern listed below will be interpreted by OpenTF as a reference to a managed resource.
For more information about how to use resource references, see references to resource attributes below.
Input Variables
var.<NAME>
is the value of the input variable of the given name.
If the variable has a type constraint (type
argument) as part of its
declaration, OpenTF will automatically convert the caller's given value
to conform to the type constraint.
For that reason, you can safely assume that a reference using var.
will
always produce a value that conforms to the type constraint, even if the caller
provided a value of a different type that was automatically converted.
In particular, note that if you define a variable as being of an object type with particular attributes then only those specific attributes will be available in expressions elsewhere in the module, even if the caller actually passed in a value with additional attributes. You must define in the type constraint all of the attributes you intend to use elsewhere in your module.
Local Values
local.<NAME>
is the value of the local value of the given name.
Local values can refer to other local values, even within the same locals
block, as long as you don't introduce circular dependencies.
Child Module Outputs
module.<MODULE NAME>
is an value representing the results of
a module
block.
If the corresponding module
block does not have either count
nor for_each
set then the value will be an object with one attribute for each output value
defined in the child module. To access one of the module's
output values, use module.<MODULE NAME>.<OUTPUT NAME>
.
If the corresponding module
uses for_each
then the value will be a map
of objects whose keys correspond with the keys in the for_each
expression,
and whose values are each objects with one attribute for each output value
defined in the child module, each representing one module instance.
If the corresponding module uses count
then the result is similar to for
for_each
except that the value is a list with the requested number of
elements, each one representing one module instance.
Data Sources
data.<DATA TYPE>.<NAME>
is an object representing a
data resource of the given data
source type and name. If the resource has the count
argument set, the value
is a list of objects representing its instances. If the resource has the for_each
argument set, the value is a map of objects representing its instances.
For more information, see
References to Resource Attributes, which
also applies to data resources aside from the addition of the data.
prefix
to mark the reference as for a data resource.
Filesystem and Workspace Info
The following values are available:
path.module
is the filesystem path of the module where the expression is placed. We do not recommend usingpath.module
in write operations because it can produce different behavior depending on whether you use remote or local module sources. Multiple invocations of local modules use the same source directory, overwriting the data inpath.module
during each call. This can lead to race conditions and unexpected results.path.root
is the filesystem path of the root module of the configuration.path.cwd
is the filesystem path of the original working directory from where you ran OpenTF before applying any-chdir
argument. This path is an absolute path that includes details about the filesystem structure. It is also useful in some advanced cases where OpenTF is run from a directory other than the root module directory. We recommend usingpath.root
orpath.module
overpath.cwd
where possible.terraform.workspace
is the name of the currently selected workspace.
Use the values in this section carefully, because they include information about the context in which a configuration is being applied and so may inadvertently hurt the portability or composability of a module.
For example, if you use path.cwd
directly to populate a path into a resource
argument then later applying the same configuration from a different directory
or on a different computer with a different directory structure will cause
the provider to consider the change of path to be a change to be applied, even
if the path still refers to the same file.
Similarly, if you use any of these values as a form of namespacing in a shared
module, such as using terraform.workspace
as a prefix for globally-unique
object names, it may not be possible to call your module more than once in
the same configuration.
Aside from path.module
, we recommend using the values in this section only
in the root module of your configuration. If you are writing a shared module
which needs a prefix to help create unique names, define an input variable
for your module and allow the calling module to define the prefix. The
calling module can then use terraform.workspace
to define it if appropriate,
or some other value if not:
module "example" {
# ...
name_prefix = "app-${terraform.workspace}"
}
Block-Local Values
Within the bodies of certain blocks, or in some other specific contexts, there are other named values available beyond the global values listed above. These local names are described in the documentation for the specific contexts where they appear. Some of most common local names are:
count.index
, in resources that use thecount
meta-argument.each.key
/each.value
, in resources that use thefor_each
meta-argument.self
, in provisioner and connection blocks.
-> Note: Local names are often referred to as variables or temporary variables in their documentation. These are not input variables; they are just arbitrary names that temporarily represent a value.
The names in this section relate to top-level configuration blocks only.
If you use dynamic
blocks to dynamically generate
resource-type-specific nested blocks within resource
and data
blocks then
you'll refer to the key and value of each element differently. See the
dynamic
blocks documentation for details.
Named Values and Dependencies
Constructs like resources and module calls often use references to named values in their block bodies, and OpenTF analyzes these expressions to automatically infer dependencies between objects. For example, an expression in a resource argument that refers to another managed resource creates an implicit dependency between the two resources.
References to Resource Attributes
The most common reference type is a reference to an attribute of a resource
which has been declared either with a resource
or data
block. Because
the contents of such blocks can be quite complicated themselves, expressions
referring to these contents can also be complicated.
Consider the following example resource block:
resource "aws_instance" "example" {
ami = "ami-abc123"
instance_type = "t2.micro"
ebs_block_device {
device_name = "sda2"
volume_size = 16
}
ebs_block_device {
device_name = "sda3"
volume_size = 20
}
}
The documentation for aws_instance
lists all of the arguments and nested blocks supported for this resource type,
and also lists a number of attributes that are exported by this resource
type. All of these different resource type schema constructs are available
for use in references, as follows:
The
ami
argument set in the configuration can be used elsewhere with the reference expressionaws_instance.example.ami
.The
id
attribute exported by this resource type can be read using the same syntax, givingaws_instance.example.id
.The arguments of the
ebs_block_device
nested blocks can be accessed using a splat expression. For example, to obtain a list of all of thedevice_name
values, useaws_instance.example.ebs_block_device[*].device_name
.The nested blocks in this particular resource type do not have any exported attributes, but if
ebs_block_device
were to have a documentedid
attribute then a list of them could be accessed similarly asaws_instance.example.ebs_block_device[*].id
.Sometimes nested blocks are defined as taking a logical key to identify each block, which serves a similar purpose as the resource's own name by providing a convenient way to refer to that single block in expressions. If
aws_instance
had a hypothetical nested block typedevice
that accepted such a key, it would look like this in configuration:device "foo" {
size = 2
}
device "bar" {
size = 4
}Arguments inside blocks with keys can be accessed using index syntax, such as
aws_instance.example.device["foo"].size
.To obtain a map of values of a particular argument for labelled nested block types, use a
for
expression:{for k, device in aws_instance.example.device : k => device.size}
.
When a resource has the
count
argument set, the resource itself becomes a list of instance objects rather than
a single object. In that case, access the attributes of the instances using
either splat expressions or index syntax:
aws_instance.example[*].id
returns a list of all of the ids of each of the instances.aws_instance.example[0].id
returns just the id of the first instance.
When a resource has the
for_each
argument set, the resource itself becomes a map of instance objects rather than
a single object, and attributes of instances must be specified by key, or can
be accessed using a for
expression.
aws_instance.example["a"].id
returns the id of the "a"-keyed resource.[for value in aws_instance.example: value.id]
returns a list of all of the ids of each of the instances.
Note that unlike count
, splat expressions are not directly applicable to resources managed with for_each
, as splat expressions must act on a list value. However, you can use the values()
function to extract the instances as a list and use that list value in a splat expression:
values(aws_instance.example)[*].id
Sensitive Resource Attributes
When defining the schema for a resource type, a provider developer can mark
certain attributes as sensitive, in which case OpenTF will show a
placeholder marker (sensitive value)
instead of the actual value when rendering
a plan involving that attribute.
A provider attribute marked as sensitive behaves similarly to an an input variable declared as sensitive, where OpenTF will hide the value in the plan and apply messages and will also hide any other values you derive from it as sensitive. However, there are some limitations to that behavior as described in Cases where OpenTF may disclose a sensitive variable.
If you use a sensitive value from a resource attribute as part of an output value then OpenTF will require you to also mark the output value itself as sensitive, to confirm that you intended to export it.
OpenTF will still record sensitive values in the state, and so anyone who can access the state data will have access to the sensitive values in cleartext. For more information, see Sensitive Data in State.
Values Not Yet Known
When OpenTF is planning a set of changes that will apply your configuration, some resource attribute values cannot be populated immediately because their values are decided dynamically by the remote system. For example, if a particular remote object type is assigned a generated unique id on creation, OpenTF cannot predict the value of this id until the object has been created.
OpenTF uses special unknown value placeholders for information that it cannot predict during the plan phase. The OpenTF language automatically handles unknown values in expressions. For example, adding a known value to an unknown value automatically produces an unknown value as a result.
However, there are some situations where unknown values do have a significant effect:
The
count
meta-argument for resources cannot be unknown, since it must be evaluated during the plan phase to determine how many instances are to be created.If unknown values are used in the configuration of a data resource, that data resource cannot be read during the plan phase and so it will be deferred until the apply phase. In this case, the results of the data resource will also be unknown values.
If an unknown value is assigned to an argument inside a
module
block, any references to the corresponding input variable within the child module will use that unknown value.If an unknown value is used in the
value
argument of an output value, any references to that output value in the parent module will use that unknown value.OpenTF will attempt to validate that unknown values are of suitable types where possible, but incorrect use of such values may not be detected until the apply phase, causing the apply to fail.
Unknown values appear in the opentf plan
output as (known after apply)
.