Terraform
Import: tfconfig/v2
Note: This is documentation for the next version of the tfconfig
Sentinel import, designed specifically for Terraform 0.12. This import requires
Terraform 0.12 or higher, and must currently be loaded by path, using an alias,
example: import "tfconfig/v2" as tfconfig
.
The tfconfig/v2
import provides access to a Terraform configuration.
The Terraform configuration is the set of *.tf
files that are used to
describe the desired infrastructure state. Policies using the tfconfig
import can access all aspects of the configuration: providers, resources,
data sources, modules, and variables.
Some use cases for tfconfig
include:
- Organizational naming conventions: requiring that configuration elements are named in a way that conforms to some organization-wide standard.
- Required inputs and outputs: organizations may require a particular set of input variable names across all workspaces or may require a particular set of outputs for asset management purposes.
- Enforcing particular modules: organizations may provide a number of "building block" modules and require that each workspace be built only from combinations of these modules.
- Enforcing particular providers or resources: an organization may wish to require or prevent the use of providers and/or resources so that configuration authors cannot use alternative approaches to work around policy restrictions.
The data in the tfconfig/v2
import is sourced from the JSON configuration file
that is generated by the terraform show -json
command. For more information on
the file format, see the JSON Output Format
page.
Import Overview
The tfconfig/v2
import is structured as a series of collections, keyed as a
specific format, such as resource address, module address, or a
specifically-formatted provider key.
tfconfig/v2
├── strip_index() (function)
├── providers
│ └── (indexed by [module_address:]provider[.alias])
│ ├── provider_config_key (string)
│ ├── name (string)
│ ├── full_name (string)
│ ├── alias (string)
│ ├── module_address (string)
│ ├── config (block expression representation)
│ └── version_constraint (string)
├── resources
│ └── (indexed by address)
│ ├── address (string)
│ ├── module_address (string)
│ ├── mode (string)
│ ├── type (string)
│ ├── name (string)
│ ├── provider_config_key (string)
│ ├── provisioners (list)
│ │ └── (ordered provisioners for this resource only)
│ ├── config (block expression representation)
│ ├── count (expression representation)
│ ├── for_each (expression representation)
│ └── depends_on (list of strings)
├── provisioners
│ └── (indexed by resource_address:index)
│ ├── resource_address (string)
│ ├── type (string)
│ ├── index (string)
│ └── config (block expression representation)
├── variables
│ └── (indexed by module_address:name)
│ ├── module_address (string)
│ ├── name (string)
│ ├── default (value)
│ └── description (string)
├── outputs
│ └── (indexed by module_address:name)
│ ├── module_address (string)
│ ├── name (string)
│ ├── sensitive (boolean)
│ ├── value (expression representation)
│ ├── description (string)
│ └── depends_on (list of strings)
└── module_calls
└── (indexed by module_address:name)
├── module_address (string)
├── name (string)
├── source (string)
├── config (block expression representation)
├── count (expression representation)
├── depends_on (expression representation)
├── for_each (expression representation)
└── version_constraint (string)
The collections are:
providers
- The configuration for all provider instances across all modules in the configuration.resources
- The configuration of all resources across all modules in the configuration.variables
- The configuration of all variable definitions across all modules in the configuration.outputs
- The configuration of all output definitions across all modules in the configuration.module_calls
- The configuration of all module calls (individualmodule
blocks) across all modules in the configuration.
These collections are specifically designed to be used with the
filter
quantifier expression in Sentinel, so that one can collect a list of resources
to perform policy checks on without having to write complex module or
configuration traversal. As an example, the following code will return all
aws_instance
resource types within the configuration, regardless of what
module they are in:
all_aws_instances = filter tfconfig.resources as _, r {
r.mode is "managed" and
r.type is "aws_instance"
}
You can add specific attributes to the filter to narrow the search, such as the
module address. The following code would return resources in a module named
foo
only:
all_aws_instances = filter tfconfig.resources as _, r {
r.module_address is "module.foo" and
r.mode is "managed" and
r.type is "aws_instance"
}
Address Differences Between tfconfig
, tfplan
, and tfstate
This import deals with configuration before it is expanded into a resource graph by Terraform. As such, it is not possible to compute an index as the import is building its collections and computing addresses for resources and modules.
As such, addresses found here may not always match the expanded addresses found
in the tfplan/v2
and tfstate/v2
imports, specifically when
count
and
for_each
,
are used.
As an example, consider a resource named null_resource.foo
with a count of 2
located in a module named bar
. While there will possibly be entries in the
other imports for module.bar.null_resource.foo[0]
and
module.bar.null_resource.foo[1]
, in tfconfig/v2
, there will only be a
module.bar.null_resource.foo
. As mentioned in the start of this section, this
is because configuration actually defines this scaling, whereas expansion
actually happens when the resource graph is built, which happens as a natural
part of the refresh and planning process.
The strip_index
helper function, found in this import, can assist in
removing the indexes from addresses found in the tfplan/v2
and tfstate/v2
imports so that data from those imports can be used to reference data in this
one.
The strip_index
Function
The strip_index
helper function can be used to remove indexes from addresses
found in tfplan/v2
and tfstate/v2
,
by removing the indexes from each resource.
This can be used to help facilitate cross-import lookups for data between plan, state, and config.
import "tfconfig/v2" as tfconfig
import "tfplan/v2" as tfplan
main = rule {
all filter tfplan.resource_changes as _, rc {
rc.mode is "managed" and
rc.type is "aws_instance"
} as _, rc {
tfconfig.resources[tfconfig.strip_index(rc.address)].config.ami.constant_value is "ami-abcdefgh012345"
}
}
Expression Representations
Most collections in this import will have one of two kinds of expression representations. This is a verbose format for expressing a (parsed) configuration value independent of the configuration source code, which is not 100% available to a policy check in HCP Terraform.
(expression representation)
├── constant_value (value)
└── references (list of strings)
There are two major parts to an expression representation:
- Any strictly constant value is expressed as an expression with a
constant_value
field. - Any expression that requires some degree of evaluation to generate the final
value - even if that value is known at plan time - is not expressed in
configuration. Instead, any particular references that are made are added to
the
references
field. More details on this field can be found in the expression representation section of the JSON output format documentation.
For example, to determine if an output is based on a particular resource value, one could do:
import "tfconfig/v2" as tfconfig
main = rule {
tfconfig.outputs["instance_id"].value.references is ["aws_instance.foo"]
}
Note: The representation does not account for
complex interpolations or other expressions that combine constants with other
expression data. For example, the partially constant data in "foo${var.bar}"
would be lost.
Block Expression Representation
Expanding on the above, a multi-value expression representation (such as the
kind found in a resources
collection element) is
similar, but the root value is a keyed map of expression representations. This
is repeated until a "scalar" expression value is encountered, ie: a field that
is not a block in the resource's schema.
(block expression representation)
└── (attribute key)
├── (child block expression representation)
│ └── (...)
├── constant_value (value)
└── references (list of strings)
As an example, one can validate expressions in an
aws_instance
resource using the
following:
import "tfconfig/v2" as tfconfig
main = rule {
tfconfig.resources["aws_instance.foo"].config.ami.constant_value is "ami-abcdefgh012345"
}
Note that nested blocks, sometimes known as sub-resources, will be nested in
configuration as a list of blocks (reflecting their ultimate nature as a list
of objects). An example would be the aws_instance
resource's
ebs_block_device
block:
import "tfconfig/v2" as tfconfig
main = rule {
tfconfig.resources["aws_instance.foo"].config.ebs_block_device[0].volume_size < 10
}
The providers
Collection
The providers
collection is a collection representing the configurations of
all provider instances across all modules in the configuration.
This collection is indexed by an opaque key. This is currently
module_address:provider.alias
, the same value as found in the
provider_config_key
field. module_address
and the colon delimiter are
omitted for the root module.
The provider_config_key
field is also found in the resources
collection and
can be used to locate a provider that belongs to a configured resource.
The fields in this collection are as follows:
provider_config_key
- The opaque configuration key, used as the index key.name
- The name of the provider, ie:aws
.full_name
- The fully-qualified name of the provider, e.g.registry.terraform.io/hashicorp/aws
.alias
- The alias of the provider, ie:east
. Empty for a default provider.module_address
- The address of the module this provider appears in.config
- A block expression representation with provider configuration values.version_constraint
- The defined version constraint for this provider.
The resources
Collection
The resources
collection is a collection representing all of the resources
found in all modules in the configuration.
This collection is indexed by the resource address.
The fields in this collection are as follows:
address
- The resource address. This is the index of the collection.module_address
- The module address that this resource was found in.mode
- The resource mode, eithermanaged
(resources) ordata
(data sources).type
- The type of resource, ie:null_resource
innull_resource.foo
.name
- The name of the resource, ie:foo
innull_resource.foo
.provider_config_key
- The opaque configuration key that serves as the index of theproviders
collection.provisioners
- The ordered list of provisioners for this resource. The syntax of the provisioners matches those found in theprovisioners
collection, but is a list indexed by the order the provisioners show up in the resource.config
- The block expression representation of the configuration values found in the resource.count
- The expression data for thecount
value in the resource.for_each
- The expression data for thefor_each
value in the resource.depends_on
- The contents of thedepends_on
config directive, which declares explicit dependencies for this resource.
The provisioners
Collection
The provisioners
collection is a collection of all of the provisioners found
across all resources in the configuration.
While normally bound to a resource in an ordered fashion, this collection allows for the filtering of provisioners within a single expression.
This collection is indexed with a key following the format
resource_address:index
, with each field matching their respective field in the
particular element below:
resource_address
: The address of the resource that the provisioner was found in. This can be found in theresources
collection.type
: The provisioner type, ie:local_exec
.index
: The provisioner index as it shows up in the resource provisioner order.config
: The block expression representation of the configuration values in the provisioner.
The variables
Collection
The variables
collection is a collection of all variables across all modules
in the configuration.
Note that this tracks variable definitions, not values. See the tfplan/v2
variables
collection for variable
values set within a plan.
This collection is indexed by the key format module_address:name
, with each
field matching their respective name below. module_address
and the colon
delimiter are omitted for the root module.
module_address
- The address of the module the variable was found in.name
- The name of the variable.default
- The defined default value of the variable.description
- The description of the variable.
The outputs
Collection
The outputs
collection is a collection of all outputs across all modules in
the configuration.
Note that this tracks variable definitions, not values. See the tfstate/v2
outputs
collection for the final
values of outputs set within a state. The tfplan/v2
output_changes
collection also contains a more
complex collection of planned output changes.
This collection is indexed by the key format module_address:name
, with each
field matching their respective name below. module_address
and the colon
delimiter are omitted for the root module.
module_address
- The address of the module the output was found in.name
- The name of the output.sensitive
- Indicates whether or not the output was marked assensitive
.value
- An expression representation for the output.description
- The description of the output.depends_on
- A list of resource names that the output depends on. These are the hard-defined output dependencies as defined in thedepends_on
field in an output declaration, not the dependencies that get derived from natural evaluation of the output expression (these can be found in thereferences
field of the expression representation).
The module_calls
Collection
The module_calls
collection is a collection of all module declarations at all
levels within the configuration.
Note that this is the
module
stanza in
any particular configuration, and not the module itself. Hence, a declaration
for module.foo
would actually be declared in the root module, which would be
represented by a blank field in module_address
.
This collection is indexed by the key format module_address:name
, with each
field matching their respective name below. module_address
and the colon
delimiter are omitted for the root module.
module_address
- The address of the module the declaration was found in.name
- The name of the module.source
- The contents of thesource
field.config
- A block expression representation for all parameter values sent to the module.count
- An expression representation for thecount
field.depends_on
: An expression representation for thedepends_on
field.for_each
- An expression representation for thefor_each
field.version_constraint
- The string value found in theversion
field of the module declaration.