Terraform
Import: tfplan/v2
Note: This is documentation for the next version of the tfplan
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 "tfplan/v2" as tfplan
.
The tfplan/v2
import provides access to a Terraform plan.
A Terraform plan is the file created as a result of terraform plan
and is the
input to terraform apply
. The plan represents the changes that Terraform needs
to make to infrastructure to reach the desired state represented by the
configuration.
In addition to the diff data available in the plan, there is a "planned state"
that is available through this import, via the
planned_values
collection. This collection
presents the Terraform state as how it might look after the plan data is
applied, but is not guaranteed to be the final state.
The data in the tfplan/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.
The entirety of the JSON output file is exposed as a Sentinel map via the
raw
collection. This allows direct, low-level access to
the JSON data, but should only be used in complex situations where the
higher-level collections do not serve the purpose.
Import Overview
The tfplan/v2
import is structured as a series of collections, keyed as a
specific format depending on the collection.
tfplan/v2
├── terraform_version (string)
├── variables
│ └── (indexed by name)
│ ├── name (string)
│ └── value (value)
├── planned_values
│ ├── outputs (tfstate/v2 outputs representation)
│ └── resources (tfstate/v2 resources representation)
├── resource_changes
│ └── (indexed by address[:deposed])
│ ├── address (string)
│ ├── module_address (string)
│ ├── mode (string)
│ ├── type (string)
│ ├── name (string)
│ ├── index (float (number) or string)
│ ├── provider_name (string)
│ ├── deposed (string)
│ └── change (change representation)
├── resource_drift
│ └── (indexed by address[:deposed])
│ ├── address (string)
│ ├── module_address (string)
│ ├── mode (string)
│ ├── type (string)
│ ├── name (string)
│ ├── index (float (number) or string)
│ ├── provider_name (string)
│ ├── deposed (string)
│ └── change (change representation)
├── output_changes
│ └── (indexed by name)
│ ├── name (string)
│ └── change (change representation)
└── raw (map)
The collections are:
variables
- The values of variables that have been set in the plan itself. This collection only contains variables set in the root module.planned_values
- The state representation of planned values, or an estimation of what the state will look like after the plan is applied.resource_changes
- The set of change operations for resources and data sources within this plan.resource_drift
- A description of the changes Terraform detected when it compared the most recent state to the prior saved state.output_changes
- The changes to outputs within this plan. This collection only contains outputs set in the root module.raw
- Access to the raw plan data as stored by HCP Terraform.
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 discovery code. As
an example, the following code will return all aws_instance
resource changes,
across all modules in the plan:
all_aws_instances = filter tfplan.resource_changes as _, rc {
rc.mode is "managed" and
rc.type is "aws_instance"
}
You can add specific attributes to the filter to narrow the search, such as the
module address, or the operation being performed. The following code would
return resources in a module named foo
only, and further narrow the search
down to only resources that were being created:
all_aws_instances = filter tfplan.resource_changes as _, rc {
rc.module_address is "module.foo" and
rc.mode is "managed" and
rc.type is "aws_instance" and
rc.change.actions is ["create"]
}
Change Representation
Certain collections in this import contain a change representation, an object
with details about changes to a particular entity, such as a resource (within
the resource_changes
collection), or
output (within the output_changes
collection).
(change representation)
├── actions (list)
├── before (value, or map)
├── after (value, or map)
└── after_unknown (boolean, or map of booleans)
This change representation contains the following fields:
actions
- A list of actions being carried out for this change. The order is important, for example a regular replace operation is denoted by["delete", "create"]
, but acreate_before_destroy
resource will have an operation order of["create", "delete"]
.before
- The representation of the resource data object value before the action. For create-only actions, this is unset. For no-op actions, this value will be identical withafter
.after
- The representation of the resource data object value after the action. For delete-only actions, this is unset. For no-op actions, this value will be identical withbefore
. Note that unknown values will not show up in this field.after_unknown
- A deep object of booleans that denotes any values that are unknown in a resource. These values were previously referred to as "computed" values. If the value cannot be found in this map, then its value should be available withinafter
, so long as the operation supports it.
Actions
As mentioned above, actions show up within the actions
field of a change
representation and indicate the type of actions being performed as part of the
change, and the order that they are being performed in.
The current list of actions are as follows:
create
- The action will create the associated entity. Depending on the order this appears in, the entity may be created alongside a copy of the entity before replacing it.read
- The action will read the associated entity. In practice, seeing this change type should be rare, as reads generally happen before a plan is executed (usually during a refresh).update
- The action will update the associated entity in a way that alters its state in some way.delete
- The action will remove the associated entity, deleting any applicable state and associated real resources or infrastructure.no-op
- No action will be performed on the associated entity.
The actions
field is a list, as some real-world actions are actually a
composite of more than one primitive action. At this point in time, this
is generally only applicable to resource replacement, in which the following
action orders apply:
- Normal replacement:
["delete", "create"]
- Applies to default lifecycle configurations. - Create-before-destroy:
["create", "delete"]
- Applies whencreate_before_destroy
is used in a lifecycle configuration.
Note that, in most situations, the plan will list all "changes", including no-op changes. This makes filtering on change type crucial to the accurate selection of data if you are concerned with the state change of a particular resource.
To filter on a change type, use exact list comparison. For example, the following example from the Import Overview filters on exactly the resources being created only:
all_aws_instances = filter tfplan.resource_changes as _, rc {
rc.module_address is "module.foo" and
rc.mode is "managed" and
rc.type is "aws_instance" and
rc.change.actions is ["create"]
}
before
, after
, and after_unknown
The exact attribute changes for a particular operation are outlined in the
before
and after
attributes. Depending on the entity being operated on, this
will either be a map (as with
resource_changes
) or a singular value (as
with output_changes
).
What you can expect in these fields varies depending on the operation:
- For fresh create operations,
before
will generally benull
, andafter
will contain the data you can expect to see after the change. - For full delete operations, this will be reversed -
before
will contain data, andafter
will benull
. - Update or replace operations will have data in both fields relevant to their states before and after the operation.
- No-op operations should have identical data in
before
andafter
.
For resources, if a field cannot be found in after
, it generally means one of
two things:
- The attribute does not exist in the resource schema. Generally, known
attributes that do not have a value will show up as
null
or otherwise empty inafter
. - The attribute is unknown, that is, it was unable to be determined at plan time and will only be available after apply-time values have been able to be calculated.
In the latter case, there should be a value for the particular attribute in
after_unknown
, which can be checked to assert that the value is indeed
unknown, versus invalid:
import "tfplan/v2" as tfplan
no_unknown_amis = rule {
all filter tfplan.resource_changes as _, rc {
rc.module_address is "module.foo" and
rc.mode is "managed" and
rc.type is "aws_instance" and
rc.change.actions is ["create"]
} as _, rc {
rc.change.after_unknown.ami else false is false
}
}
For output changes, after_unknown
will simply be true
if the value won't be
known until the plan is applied.
The terraform_version
Value
The top-level terraform_version
value in this import gives the Terraform
version that made the plan. This can be used to do version validation.
import "tfplan/v2" as tfplan
import "strings"
v = strings.split(tfplan.terraform_version, ".")
version_major = int(v[1])
version_minor = int(v[2])
main = rule {
version_major is 12 and version_minor >= 19
}
NOTE: The above example will give errors when working with pre-release
versions (example: 0.12.0beta1
). Future versions of this import will include
helpers to assist with processing versions that will account for these kinds of
exceptions.
The variables
Collection
The variables
collection is a collection of the variables set in the root
module when creating the plan.
This collection is indexed on the name of the variable.
The valid values are:
name
- The name of the variable, also used as the collection key.value
- The value of the variable assigned during the plan.
The planned_values
Collection
The planned_values
collection is a special collection in that it contains two
fields that alias to state collections with the planned state set. This is the
best prediction of what the state will look like after the plan is executed.
The two fields are:
outputs
- The prediction of what output values will look like after the state is applied. For more details on the structure of this collection, see theoutputs
collection in thetfstate/v2
documentation.resources
- The prediction of what resource values will look like after the state is applied. For more details on the structure of this collection, see theresources
collection in thetfstate/v2
documentation.
NOTE: Unknown values are omitted from the planned_values
state
representations, regardless of whether or not they existed before. Use
resource_changes
if awareness of unknown
data is important.
The resource_changes
and resource_drift
Collections
The resource_changes
and resource_drift
collections are a set of change operations for resources
and data sources within this plan.
The resource_drift
collection provides a description of the changes Terraform detected
when it compared the most recent state to the prior saved state.
The resource_changes
collection includes all resources that have been found in the configuration and state,
regardless of whether or not they are changing.
When resource targeting is in effect, the resource_changes
collection will only include the resources specified as targets for the run. This may lead to unexpected outcomes if a policy expects a resource to be present in the plan. To prohibit targeted runs altogether, ensure tfrun.target_addrs
is undefined or empty.
This collection is indexed on the complete resource address as the key. If
deposed
is non-empty, it is appended to the end, and may look something like
aws_instance.foo:deposed-abc123
.
An element contains the following fields:
address
- The absolute resource address - also the key for the collection's index, ifdeposed
is empty.module_address
- The module portion of the absolute resource address.mode
- The resource mode, eithermanaged
(resources) ordata
(data sources).type
- The resource type, example:aws_instance
foraws_instance.foo
.name
- The resource name, example:foo
foraws_instance.foo
.index
- The resource index. Can be either a number or a string.provider_name
- The name of the provider this resource belongs to. This allows the provider to be interpreted unambiguously in the unusual situation where a provider offers a resource type whose name does not start with its own name, such as thegooglebeta
provider offeringgoogle_compute_instance
.Note: Starting with Terraform 0.13, the
provider_name
field contains the full source address to the provider in the Terraform Registry. Example:registry.terraform.io/hashicorp/null
for the null provider.deposed
- An identifier used during replacement operations, and can be used to identify the exact resource being replaced in state.change
- The data describing the change that will be made to this resource. For more details, see Change Representation.
The output_changes
Collection
The output_changes
collection is a collection of the change operations for
outputs within this plan.
Only outputs for the root module are included.
This collection is indexed by the name of the output. The fields in a collection value are below:
name
- The name of the output, also the index key.change
- The data describing the change that will be made to this output. For more details, see Change Representation.
The raw
Collection
The raw
collection exposes the raw, unprocessed plan data, direct from the
data stored by HCP Terraform.
This is the same data that is produced by terraform show -json
on the plan file for the run this
policy check is attached to.
Use of this data is only recommended in expert situations where the data the collections present may not exactly serve the needs of the policy. For more information on the file format, see the JSON Output Format page.
NOTE: Although designed to be relatively stable, the actual makeup for the JSON output format is a Terraform CLI concern and as such not managed by HCP Terraform. Use at your own risk, follow the Terraform CLI project, and watch the file format documentation for any changes.