830 lines
34 KiB
Plaintext
830 lines
34 KiB
Plaintext
---
|
|
page_title: Upgrading to Terraform 0.12
|
|
description: Upgrading to Terraform v0.12
|
|
---
|
|
|
|
# Upgrading to Terraform v0.12
|
|
|
|
[Terraform v0.12 is a major release](https://hashicorp.com/blog/terraform-0-1-2-preview)
|
|
focused on configuration language improvements and thus includes some
|
|
changes that you'll need to consider when upgrading. The goal of this guide is
|
|
to cover the most common upgrade concerns and issues.
|
|
|
|
For most users, upgrading configuration should be completely automatic. Some
|
|
simple configurations will require no changes at all, and most other
|
|
configurations can be prepared by running
|
|
[the automatic upgrade tool](/cli/commands/0.12upgrade). Please read on
|
|
for more information and recommendations on the upgrade process.
|
|
|
|
-> If you are a developer maintaining a provider plugin, please see
|
|
[the documentation on 0.12 compatibility for providers](/plugin/sdkv2/guides/terraform-0.12-compatibility)
|
|
to learn more about the changes that are required.
|
|
|
|
## Upgrade to Terraform 0.11 first
|
|
|
|
We strongly recommend completing an upgrade to the latest Terraform v0.11
|
|
release first. This will give you an opportunity to address any changes
|
|
required for the previous major version upgrades separately, rather than
|
|
making multiple changes at once.
|
|
|
|
In particular, if you are upgrading from a Terraform version prior to v0.9,
|
|
you _must_ first [upgrade to Terraform v0.9](/language/upgrade-guides/0-9) and
|
|
switch to initializing with `terraform init`, because v0.12 no longer includes
|
|
the functionality for automatically migrating from the legacy remote state
|
|
mechanism.
|
|
|
|
This guide focuses on changes from v0.11 to v0.12. Each previous major release
|
|
has its own upgrade guide, so please consult the other guides (available in the
|
|
navigation) to upgrade step-by-step to v0.11 first.
|
|
|
|
Terraform v0.11.14 (and any subsequent v0.11 releases) also include some
|
|
additional functionality to help smooth the upgrade, which we will use later
|
|
in this guide.
|
|
|
|
Prior versions of Terraform are available from
|
|
[the releases server](https://releases.hashicorp.com/terraform/).
|
|
|
|
## Pre-upgrade Checklist
|
|
|
|
Terraform v0.11.14 introduced a temporary helper command
|
|
`terraform 0.12checklist`, which analyzes your configuration to detect any
|
|
required steps that will be easier to perform before upgrading.
|
|
|
|
To use it, first upgrade to [Terraform v0.11.14](https://releases.hashicorp.com/terraform/0.11.14/).
|
|
Then, perform the following steps:
|
|
|
|
* `terraform init` to ensure your working directory is fully initialized and
|
|
all required plugins are installed and selected.
|
|
* `terraform apply` to ensure that your real infrastructure and Terraform
|
|
state are consistent with the current configuration. The instructions
|
|
produced by the checklist command assume that configuration and state are
|
|
synchronized.
|
|
* `terraform 0.12checklist` to see if there are any pre-upgrade steps in the
|
|
checklist.
|
|
|
|
If all is well, the final command will produce a message like this:
|
|
|
|
```
|
|
Looks good! We did not detect any problems that ought to be
|
|
addressed before upgrading to Terraform v0.12
|
|
|
|
This tool is not perfect though, so please check the v0.12 upgrade
|
|
guide for additional guidance, and for next steps:
|
|
https://www.terraform.io/upgrade-guides/0-12.html
|
|
```
|
|
|
|
As the message suggests, the next step in that case is to read the remainder
|
|
of this page to prepare for and carry out the upgrade.
|
|
|
|
However, the checklist command may instead produce a list of one or more tasks
|
|
that we recommend you perform before upgrading to Terraform 0.12, because they
|
|
are easier to perform with a fully-functional Terraform 0.11 than with a
|
|
Terraform 0.12 that has encountered compatibility problems.
|
|
|
|
The tasks it may suggest you perform could include:
|
|
|
|
* Upgrading any provider versions that are not compatible with Terraform v0.12.
|
|
We recommend upgrading to the latest version of each provider before upgrading
|
|
because that will avoid changing many things in one step.
|
|
* Renaming any resources or provider aliases that have names that start with
|
|
digits, because that is no longer valid in Terraform 0.12.
|
|
* Upgrading any external modules the configuration uses which themselves have
|
|
the above problems.
|
|
|
|
In each case, the tool will give some direction on how to perform the task it
|
|
is suggesting.
|
|
|
|
The output from `terraform 0.12checklist` is in Markdown format so that it can
|
|
easily be pasted into a Markdown-compatible issue tracker, should you want
|
|
to track the necessary tasks or share the work with other team members.
|
|
|
|
After all of the tasks are complete, run `terraform 0.12checklist` one more time
|
|
to verify that everything is complete. If so, continue reading the following
|
|
sections to complete the upgrade!
|
|
|
|
### Addendum: Invalid module names
|
|
|
|
There is one additional pre-upgrade checklist item that the Terraform team did
|
|
not become aware of until after the release of Terraform v0.11.14, and thus
|
|
cannot be detected automatically by the checklist tool: renaming modules which
|
|
have names that start with digits.
|
|
|
|
Terraform 0.11 inadvertently tolerated leading-digit names for modules as a
|
|
result of a validation bug, but Terraform 0.12 has corrected that bug and will
|
|
reject such module names. Unfortunately, module names are also recorded in
|
|
state snapshots and so a state snapshot created for a configuration with an
|
|
invalid module name will itself be invalid as far as Terraform 0.12 is
|
|
concerned.
|
|
|
|
You can address this in a similar way to what the checklist tool suggests for
|
|
invalid resource names and provider aliases:
|
|
|
|
* Rename the module in your configuration.
|
|
* Use `terraform state mv module.old module.new` _in Terraform 0.11.14_ to
|
|
update the state to use the new name instead of the old name.
|
|
|
|
As with all of the pre-upgrade checklist items, be sure to run `terraform apply`
|
|
once more before upgrading in order to ensure that the latest state snapshot is
|
|
synchronized with the latest configuration.
|
|
|
|
## Upgrading to Terraform 0.12
|
|
|
|
Before switching to Terraform 0.12, we recommend using Terraform v0.11.14 (or
|
|
any later v0.11 release) to perform one last `terraform init` and
|
|
`terraform apply` to ensure that everything is initialized and synchronized.
|
|
|
|
Once `terraform apply` shows no changes pending, switch over to a Terraform
|
|
v0.12 release and run `terraform init` again to upgrade the working directory
|
|
metadata to v0.12 format. (Once you've done this, you'll need to delete the
|
|
`.terraform` directory if you wish to return to Terraform v0.11, but no
|
|
real infrastructure or persisted state will be upgraded yet.)
|
|
|
|
It is possible that your configuration may be using configuration constructs
|
|
that are not Terraform v0.12 compatible and thus require upgrade. In that case,
|
|
`terraform init` will produce the following message:
|
|
|
|
```
|
|
Terraform has initialized, but configuration upgrades may be needed.
|
|
|
|
Terraform found syntax errors in the configuration that prevented full
|
|
initialization. If you've recently upgraded to Terraform v0.12, this may be
|
|
because your configuration uses syntax constructs that are no longer valid,
|
|
and so must be updated before full initialization is possible.
|
|
|
|
Terraform has installed the required providers to support the configuration
|
|
upgrade process. To begin upgrading your configuration, run the following:
|
|
terraform 0.12upgrade
|
|
|
|
To see the full set of errors that led to this message, run:
|
|
terraform validate
|
|
```
|
|
|
|
As mentioned in the message, Terraform has partially initialized the directory
|
|
just enough to perform the configuration upgrade process, which is described
|
|
in the following section.
|
|
|
|
We recommend running the configuration upgrade tool even if you do not see
|
|
the above message, because it may detect and fix constructs that are
|
|
syntactically correct but still need some changes to work as expected with
|
|
Terraform v0.12.
|
|
|
|
## Upgrading Terraform configuration
|
|
|
|
Terraform v0.12 includes a new command `terraform 0.12upgrade` that will
|
|
read the configuration files for a module written for Terraform 0.11 and
|
|
update them in-place to use the cleaner Terraform 0.12 syntax and also
|
|
adjust for use of features that have changed behavior in the 0.12 Terraform
|
|
language.
|
|
|
|
Simple configuration files are likely to be understood by Terraform 0.12 as-is,
|
|
because the language is still broadly compatible, but we recommend that everyone
|
|
run the upgrade tool nonetheless. Even if your configuration is already
|
|
compatible, the tool will update your configuration to use the cleaner syntax
|
|
available in Terraform 0.12, which should improve readability.
|
|
|
|
To run the command, first make sure that your local working directory is synced
|
|
with your version control system so that there are no changes outstanding. This
|
|
will make it easier to review the changes that the upgrade tool is proposing,
|
|
using the diff feature of your version control system.
|
|
|
|
With a fully-initialized working directory (all necessary providers and child
|
|
modules installed), run `terraform 0.12upgrade` to begin the process. By default
|
|
it will print some information about what it is about to do and prompt for
|
|
confirmation:
|
|
|
|
```
|
|
This command will rewrite the configuration files in the given directory so
|
|
that they use the new syntax features from Terraform v0.12, and will identify
|
|
any constructs that may need to be adjusted for correct operation with
|
|
Terraform v0.12.
|
|
|
|
We recommend using this command in a clean version control work tree, so that
|
|
you can easily see the proposed changes as a diff against the latest commit.
|
|
If you have uncommitted changes already present, we recommend aborting this
|
|
command and dealing with them before running this command again.
|
|
|
|
Would you like to upgrade the module in the current directory?
|
|
```
|
|
|
|
If you answer yes, the `.tf` and `.tfvars` files in your current working
|
|
directory will be rewritten in-place.
|
|
|
|
The upgrade tool may also print out warnings about constructs it wasn't able to
|
|
migrate fully automatically; in that case, it will also emit comments into the
|
|
rewritten source files containing the special marker `TF-UPGRADE-TODO`, as
|
|
a prompt for a decision you'll need to make to complete the upgrade.
|
|
|
|
Once the upgrade tool has successfully completed and you've resolved any
|
|
`TF-UPGRADE-TODO` prompts, use your version control tool to review the proposed
|
|
changes and then run `terraform plan` to see the effect of those changes.
|
|
|
|
In most cases, `terraform plan` should report that no changes are required,
|
|
because the updated configuration is equivalent to before.
|
|
|
|
The remaining sections below describe both some common changes that the upgrade
|
|
tool is able to make automatically, and some other upgrade situations that
|
|
the configuration tool may not be able to fully resolve. If you encounter
|
|
any errors during the upgrade or during the subsequent `terraform plan`, the
|
|
sections below may give some additional context for how to proceed.
|
|
|
|
Once you're happy with the updated configuration, commit it to version control
|
|
in the usual way and apply it with Terraform 0.12.
|
|
|
|
### Remote state references
|
|
|
|
The `terraform_remote_state` data source has changed slightly for the v0.12
|
|
release to make all of the remote state outputs available as a single map
|
|
value, rather than as top-level attributes as in previous releases.
|
|
|
|
In previous releases, a reference to a `vpc_id` output exported by the remote
|
|
state data source might have looked like this:
|
|
|
|
```hcl
|
|
data.terraform_remote_state.vpc.vpc_id
|
|
```
|
|
|
|
This value must now be accessed via the new `outputs` attribute:
|
|
|
|
```hcl
|
|
data.terraform_remote_state.vpc.outputs.vpc_id
|
|
```
|
|
|
|
The upgrade tool will rewrite remote state references automatically to include
|
|
the additional `outputs` attribute.
|
|
|
|
Where appropriate, you can also access the outputs attribute directly to
|
|
work with the whole map as a single value:
|
|
|
|
```hcl
|
|
data.terraform_remote_state.vpc.outputs
|
|
```
|
|
|
|
Another consideration for `terraform_remote_state` is that this data source must
|
|
be able to parse the latest state snapshot for a separate Terraform
|
|
configuration that may have been updated by a newer version of Terraform.
|
|
To provide flexibility when upgrading decomposed environments that use
|
|
`terraform_remote_state`, Terraform v0.11.14 introduced support for reading
|
|
outputs from the Terraform v0.12 state format, so if you upgrade all of your
|
|
configurations to Terraform v0.11.14 first you can then perform v0.12 upgrades
|
|
of individual configurations in any order, without breaking
|
|
`terraform_remote_state` usage.
|
|
|
|
Note that the `config` block should now be in the form of an assignment with the `=` sign:
|
|
|
|
```hcl
|
|
data "terraform_remote_state" "default" {
|
|
backend = "gcs"
|
|
config = {
|
|
bucket = "..."
|
|
}
|
|
}
|
|
```
|
|
|
|
### Attributes vs. blocks
|
|
|
|
Terraform resource configurations consist of both arguments that set
|
|
individual properties of the main object being described, and nested blocks
|
|
which declare zero or more other objects that are modeled as being part of
|
|
their parent. For example:
|
|
|
|
```hcl
|
|
resource "aws_instance" "example" {
|
|
instance_type = "t2.micro"
|
|
ami = "ami-abcd1234"
|
|
|
|
tags = {
|
|
Name = "example instance"
|
|
}
|
|
|
|
ebs_block_device {
|
|
device_name = "sda2"
|
|
volume_type = "gp2"
|
|
volume_size = 24
|
|
}
|
|
}
|
|
```
|
|
|
|
In the above resource, `instance_type`, `ami`, and `tags` are both direct
|
|
arguments of the `aws_instance` resource, while `ebs_block_device` describes
|
|
a separate EBS block device object that is connected to the parent instance.
|
|
|
|
Due to the design of the configuration language decoder in Terraform v0.11 and
|
|
earlier, it was in many cases possible to interchange the argument syntax
|
|
(with `=`) and the block syntax (with just braces) when dealing with map
|
|
arguments vs. nested blocks. However, this led to some subtle bugs and
|
|
limitations, so Terraform v0.12 now requires consistent usage of argument
|
|
syntax for arguments and nested block syntax for nested blocks.
|
|
|
|
In return for this new strictness, Terraform v0.12 now allows map keys to be
|
|
set dynamically from expressions, which is a long-requested feature. The
|
|
main difference between a map attribute and a nested block is that a map
|
|
attribute will usually have user-defined keys, like we see in the `tags`
|
|
example above, while a nested block always has a fixed set of supported
|
|
arguments defined by the resource type schema, which Terraform will validate.
|
|
|
|
The configuration upgrade tool uses the provider's schema to recognize the
|
|
nature of each construct and will select the right syntax automatically. For
|
|
most simple usage, this will just involve adding or removing the equals sign
|
|
as appropriate.
|
|
|
|
A more complicated scenario is where users found that they could exploit this
|
|
flexibility to -- with some caveats -- dynamically generate nested blocks even
|
|
though this wasn't intentionally allowed:
|
|
|
|
```hcl
|
|
# Example of no-longer-supported workaround from 0.11 and earlier
|
|
ebs_block_device = "${concat(map("device_name", "sda4"), var.extra_block_devices)}"
|
|
```
|
|
|
|
Terraform v0.12 now includes a first-class feature for dynamically generating
|
|
nested blocks using expressions, using the special `dynamic` block type. The
|
|
above can now be written like this, separating the static block device from
|
|
the dynamic ones:
|
|
|
|
```hcl
|
|
ebs_block_device {
|
|
device_name = "sda4"
|
|
}
|
|
dynamic "ebs_block_device" {
|
|
for_each = var.extra_block_devices
|
|
content {
|
|
device_name = ebs_block_device.value.device_name
|
|
volume_type = ebs_block_device.value.volume_type
|
|
volume_size = ebs_block_device.value.volume_size
|
|
}
|
|
}
|
|
```
|
|
|
|
The configuration upgrade tool will detect use of the above workaround and
|
|
rewrite it as a `dynamic` block, but it may make non-ideal decisions for how to
|
|
flatten your expression down into static vs. dynamic blocks, so we recommend
|
|
reviewing the generated `dynamic` blocks to see if any simplifications are
|
|
possible.
|
|
|
|
Terraform v0.12 now also requires that each argument be set only once within
|
|
a particular block, whereas before Terraform would either take the last
|
|
definition or, in some cases, attempt to merge together multiple definitions
|
|
into a list. The upgrade tool does not remove or attempt to consolidate
|
|
any existing duplicate arguments, but other commands like `terraform validate`
|
|
will detect and report these after upgrading.
|
|
|
|
### Integer vs. Float Number Types
|
|
|
|
From Terraform v0.12, the Terraform language no longer distinguishes between
|
|
integer and float types, instead just having a single "number" type that can
|
|
represent high-precision floating point numbers. This new type can represent
|
|
any value that could be represented before, plus many new values due to the
|
|
expanded precision.
|
|
|
|
In most cases this change should not cause any significant behavior change, but
|
|
please note that in particular the behavior of the division operator is now
|
|
different: it _always_ performs floating point division, whereas before it
|
|
would sometimes perform integer division by attempting to infer intent from
|
|
the argument types.
|
|
|
|
If you are relying on integer division behavior in your configuration, please
|
|
use the `floor` function to obtain the previous result. A common place this
|
|
would arise is in index operations, where the index is computed by division:
|
|
|
|
```hcl
|
|
example = var.items[floor(count.index / var.any_number)]
|
|
```
|
|
|
|
Using a fractional number to index a list will produce an error telling you
|
|
that this is not allowed, serving as a prompt to add `floor`:
|
|
|
|
```
|
|
Error: Invalid index
|
|
|
|
The given key does not identify an element in this collection value: indexing a
|
|
sequence requires a whole number, but the given index (0.5) has a fractional
|
|
part.
|
|
```
|
|
|
|
Unfortunately the automatic upgrade tool cannot apply a fix for this case
|
|
because it does not have enough information to know if floating point or integer
|
|
division was intended by the configuration author, so this change must be made
|
|
manually where needed.
|
|
|
|
### Referring to List Variables
|
|
|
|
In early versions of Terraform, before list support became first-class, we
|
|
required using seemingly-redundant list brackets around a single expression
|
|
in order to hint to the language interpreter that a list interpretation was
|
|
desired:
|
|
|
|
```hcl
|
|
# Example for older versions of Terraform; not valid for v0.12
|
|
example = ["${var.any_list}"]
|
|
```
|
|
|
|
This strange requirement was subsequently lifted after the introduction of
|
|
first-class list support, but we retained compatibility with this older usage
|
|
for a transitional period by including some fixup logic that would detect when
|
|
list brackets contain list expressions and automatically flatten to a single
|
|
list.
|
|
|
|
As part of implementing the first-class expressions support for v0.12, we needed
|
|
to finally remove that backward-compatibility mechanism to avoid ambiguity
|
|
in the language, so an expression like the above will now produce a list of
|
|
lists and thus produce a type checking error for any argument that was expecting
|
|
a list of some other type.
|
|
|
|
The upgrade tool is able to recognize most simple usage of this pattern and
|
|
rewrite automatically to just refer to the list directly:
|
|
|
|
```hcl
|
|
example = var.any_list
|
|
```
|
|
|
|
However, an unintended side-effect of this compatibility mechanism was to
|
|
also flatten mixed lists of single-value and list expressions into a single
|
|
list automatically. We didn't intend for this to be a part of the language, but
|
|
in retrospect it was an obvious consequence of how the compatibility mechanism
|
|
was implemented. If you have expressions in your modules that produce a list
|
|
of strings by using list brackets with a mixture of string and list-of-string
|
|
sub-expressions, you will need to rewrite this to explicitly use
|
|
[the `flatten` function](/language/functions/flatten)
|
|
to make the special treatment more obvious to the reader:
|
|
|
|
```hcl
|
|
example = flatten([
|
|
"single string",
|
|
var.any_list,
|
|
])
|
|
```
|
|
|
|
The configuration upgrade tool unfortunately cannot make this change
|
|
automatically, because it doesn't have enough information to know for certain
|
|
which interpretation was intended for a given list.
|
|
|
|
For complex examples that the upgrade tool is not able to adjust automatically,
|
|
subsequent Terraform operations may produce an error message like the following:
|
|
|
|
```
|
|
Error: Incorrect attribute value type
|
|
|
|
on redundant-list-brackets.tf line 9, in resource "aws_security_group" "foo":
|
|
9: cidr_blocks = ["${var.cidr_blocks}"]
|
|
|
|
Inappropriate value for attribute "cidr_blocks": element 0: string required.
|
|
```
|
|
|
|
This message is reporting that Terraform has understood this expression as a
|
|
list of lists, and therefore element zero is a list rather than a string. To
|
|
fix the error, remove the redundant list brackets and possibly add a
|
|
`flatten` function call as described above, for more complex cases.
|
|
|
|
### Reserved Variable Names
|
|
|
|
In preparation for new features planned for future releases, Terraform 0.12
|
|
reserves some additional names that can no longer be used as input variable
|
|
names for modules. These reserved names are:
|
|
|
|
* `count`
|
|
* `depends_on`
|
|
* `for_each`
|
|
* `lifecycle`
|
|
* `providers`
|
|
* `source`
|
|
|
|
When any of these names is used as the label of a `variable` block, Terraform
|
|
will now generate the following error:
|
|
|
|
```
|
|
Error: Invalid variable name
|
|
|
|
on reserved-variable-names.tf line 2, in variable "count":
|
|
2: variable "count" {
|
|
|
|
The variable name "count" is reserved due to its special meaning inside module
|
|
blocks.
|
|
```
|
|
|
|
The upgrade tool cannot automatically adjust for these reserved names, because
|
|
it does not know what new name would be more appropriate. To proceed, you must
|
|
unfortunately rename these input variables and make a new major release of
|
|
the module in question, since renaming input variables is a breaking change.
|
|
|
|
### Type Constraints on Variables
|
|
|
|
In Terraform v0.11, variables were documented as accepting only strings, lists
|
|
of strings, and maps of strings. However, in practice Terraform permitted
|
|
lists of lists and lists of maps and other nested structures in some cases,
|
|
even though it was then generally inconvenient to work with those values
|
|
elsewhere in the module due to limitations of the index syntax, `element`
|
|
function, and `lookup` function.
|
|
|
|
Terraform now allows various [type constraints](/language/expressions/type-constraints)
|
|
to be specified, as part of the language's new type system and generalized
|
|
functions and operators. However, because lists and maps of non-string values
|
|
were not officially supported in 0.11, existing configurations do not have
|
|
enough information for the upgrade tool to know what element type was intended.
|
|
It will therefore assume that lists and maps are of strings as documented,
|
|
which will be incorrect for configurations using more complex structures. The
|
|
result will be one of the following error messages:
|
|
|
|
```
|
|
Error: Invalid default value for variable
|
|
|
|
on child_module/example.tf line 4, in variable "example":
|
|
4: default = [
|
|
5: {
|
|
6: "foo" = "bar"
|
|
7: },
|
|
8: ]
|
|
|
|
This default value is not compatible with the variable's type constraint:
|
|
element 0: string required.
|
|
```
|
|
|
|
```
|
|
Error: Invalid value for module argument
|
|
|
|
on variables-incorrect-elem-type.tf line 4, in module "child":
|
|
4: example = [
|
|
5: {
|
|
6: "foo" = "bar"
|
|
7: },
|
|
8: ]
|
|
|
|
The given value is not suitable for child module variable "example" defined at
|
|
child/child.tf:1,1-19: element 0: string required.
|
|
```
|
|
|
|
To fix this, change the `type` argument from `list(string)` or `map(string)`
|
|
to a more appropriate [type constraint](/language/expressions/type-constraints).
|
|
|
|
If you're not sure what type constraint to use yet, another option is to
|
|
use the type constraint `any`, which will effectively disable validation and
|
|
allow any value. We recommend using specific types where possible, but selecting
|
|
`any` during upgrade may be preferable, so that the work to select and define
|
|
a more precise type can be saved for a later change at your leisure, once
|
|
upgrading is complete.
|
|
|
|
### Working with `count` on resources
|
|
|
|
The `count` feature allows declaration of multiple instances of a particular
|
|
resource constructed from the same configuration. In Terraform v0.11, any
|
|
use of `count` would generally lead to referring to the resource in question
|
|
using the "splat expression" syntax elsewhere in the configuration:
|
|
|
|
```
|
|
aws_instance.example.*.id[0]
|
|
```
|
|
|
|
Because `aws_instance.example` itself was not directly referencable in
|
|
Terraform v0.11, the expression system allowed some flexibility in how such
|
|
expressions were resolved. For example, Terraform would treat
|
|
`aws_instance.example.id` as an alias for `aws_instance.example.*.id[0]`.
|
|
|
|
Terraform v0.12 allows referring to an entire resource as an object value,
|
|
but that required making a decision on what type of value is returned by
|
|
`aws_instance.example`. The new rules are as follows:
|
|
|
|
* For resources where `count` is _not_ set, a reference like
|
|
`aws_instance.example` returns a single object, whose attributes can be
|
|
accessed in the usual way, like `aws_instance.example.id`.
|
|
|
|
* For resources where `count` _is_ set -- even if the expression evaluates to
|
|
`1` -- `aws_instance.example` returns a list of objects whose length is
|
|
decided by the count. In this case `aws_instance.example.id` is an error,
|
|
and must instead be written as `aws_instance.example[0].id` to access
|
|
one of the objects before retrieving its `id` attribute value.
|
|
|
|
The splat syntax is still available and will still be useful in situations
|
|
where a list result is needed, but we recommend updating expressions like
|
|
`aws_instance.example.*.id[count.index]` to instead be
|
|
`aws_instance.example[count.index].id`, which should be easier to read and
|
|
understand for those who are familiar with other languages.
|
|
|
|
Another consequence of the new handling of `count` is that you can use the
|
|
`length` function directly with references to resources that have `count` set:
|
|
|
|
```
|
|
length(aws_instance.example)
|
|
```
|
|
|
|
This replaces the v0.11 special case of `aws_instance.example.count`, which
|
|
can no longer be supported due to `aws_instance.example` being a list.
|
|
|
|
The upgrade tool will automatically detect references that are inconsistent
|
|
with the `count` setting on the target resource and rewrite them to use the
|
|
new syntax. The upgrade tool will _not_ rewrite usage of splat syntax to
|
|
direct index syntax, because the old splat syntax form is still compatible.
|
|
|
|
Another `count`-related change is that Terraform now requires `count` to be
|
|
assigned a numeric value, and will not automatically convert a boolean value
|
|
to a number in the interests of clarity. If you wish to use a boolean value
|
|
to activate or deactivate a particular resource, use the conditional operator
|
|
to show clearly how the boolean value maps to a number value:
|
|
|
|
```hcl
|
|
count = var.enabled ? 1 : 0
|
|
```
|
|
|
|
### First-class expressions
|
|
|
|
Terraform v0.11 and earlier allowed expressions only within interpolation
|
|
sequences, like `"${var.example}"`. Because expressions are such an important
|
|
part of Terraform -- they are the means by which we connect the attributes of
|
|
one resource to the configuration of another -- Terraform v0.12 now allows
|
|
you to use expressions directly when defining most attributes.
|
|
|
|
```
|
|
ami = var.ami
|
|
```
|
|
|
|
The generalization of expression handling also has some other benefits. For
|
|
example, it's now possible to directly construct lists and maps within
|
|
expressions using the normal syntax, whereas in Terraform v0.11 we required
|
|
using the `list` and `map` functions:
|
|
|
|
```
|
|
# Old 0.11 example
|
|
tags = "${merge(map("Name", "example"), var.common_tags)}"
|
|
|
|
# Updated 0.12 example
|
|
tags = merge({ Name = "example" }, var.common_tags)
|
|
```
|
|
|
|
The automatic upgrade tool will perform rewrites like these automatically,
|
|
making expressions easier to read and understand.
|
|
|
|
### Default settings in `connection` blocks
|
|
|
|
Terraform v0.11 and earlier allowed providers to pre-populate certain arguments
|
|
in a `connection` block for use with remote provisioners. Several resource
|
|
type implementations use this to pre-populate `type` as `"ssh"` and `host`
|
|
as one of the IP addresses of the compute instance being created.
|
|
|
|
While that feature was convenient in some cases, we found that in practice it
|
|
was hard for users to predict how it would behave, since each provider had its
|
|
own rules for whether to prefer public vs. private IP addresses, which network
|
|
interface to use, whether to use IPv4 or IPv6, etc.
|
|
|
|
It also violated our design principle of "explicit is better than implicit": we
|
|
think it's important that someone who is unfamiliar with a particular Terraform
|
|
configuration (or with Terraform itself) to be able to read the configuration
|
|
and make a good guess as to what it will achieve, and the default connection
|
|
settings feature left an important detail unstated: how do the provisioners
|
|
access the host?
|
|
|
|
With this in mind, Terraform v0.12 no longer performs any automatic population
|
|
of `connection` blocks. Instead, if you are using any remote provisioners you
|
|
should explicitly set the connection type and the hostname to connect to:
|
|
|
|
```hcl
|
|
connection {
|
|
type = "ssh"
|
|
host = self.public_ip
|
|
# ...
|
|
}
|
|
```
|
|
|
|
The automatic upgrade tool will detect existing `connection` blocks that are
|
|
lacking these settings within resource types that are known to have previously
|
|
set defaults, and it will write out an expression that approximates whatever
|
|
selection logic the provider was previously doing in its own implementation.
|
|
|
|
Unfortunately in some cases the provider did not export the result of the
|
|
possibly-rather-complex host selection expression as a single attribute, and so
|
|
for some resource types the generated `host` expression will be quite
|
|
complicated. We recommend reviewing these and replacing them with a simpler
|
|
expression where possible, since you will often know better than Terraform does
|
|
which of the instance IP addresses are likely to be accessible from the host
|
|
where Terraform is running.
|
|
|
|
### Equality operations must be valid on value and type
|
|
|
|
In 0.11, `"1"` would compare truthfully against `1`, however, in 0.12,
|
|
values must be equal on both value and type in order to be true. That is, in 0.11
|
|
you would see:
|
|
|
|
```
|
|
> "1" == 1
|
|
true
|
|
```
|
|
|
|
and in 0.12:
|
|
|
|
```
|
|
> "1" == 1
|
|
false
|
|
```
|
|
|
|
This means special care should be taken if you have any conditionals comparing to say,
|
|
`count.index` where you were previously expecting it to be a string, when it is now a number.
|
|
|
|
This is a scenario where you would need to update existing 0.11 code to work as you expect in 0.12:
|
|
|
|
```
|
|
resource "server_instance" "app" {
|
|
server_status = "${count.index == local.prod_index ? "production" : "standby"}"
|
|
}
|
|
}
|
|
|
|
locals {
|
|
# when migrating to 0.12, be sure to change this value to a number
|
|
# to ensure expected behavior
|
|
prod_index = "0"
|
|
}
|
|
```
|
|
|
|
Also take care that if you have a variable that is a number, but defined as a string,
|
|
the upgrade tool will not change it to a number, so take care to inspect your code:
|
|
|
|
```
|
|
locals {
|
|
some_count = "3" # will not be changed to a number after config upgrade
|
|
}
|
|
```
|
|
|
|
## Upgrades for reusable modules
|
|
|
|
If you are making upgrades to a reusable module that is consumed by many
|
|
different configurations, you may need to take care with the timing of your
|
|
upgrade and of how you publish it.
|
|
|
|
We strongly recommend using module versioning, either via a Terraform registry
|
|
or via version control arguments in your module source addresses, to pin
|
|
existing references to the old version of the module and then publish the
|
|
upgraded version under a new version number. If you are using semantic
|
|
versioning, such as in a Terraform registry, the updates made by the upgrade
|
|
tool should be considered a breaking change and published as a new major
|
|
version.
|
|
|
|
The migration tool will automatically add a `>= 0.12.0` Terraform version
|
|
constraint to indicate that the module has been upgraded to use v0.12-only
|
|
features. By using version constraints, users can gradually update their callers
|
|
to use the newly-upgraded version as they begin to use Terraform v0.12 with
|
|
those modules.
|
|
|
|
For simpler modules it may be possible to carefully adapt them to be both
|
|
0.11 and 0.12 compatible at the same time, by following the upgrade notes in
|
|
earlier sections and avoiding any v0.12-only features. However, for any module
|
|
using a undocumented workarounds for v0.11 limitations it is unlikely to be
|
|
possible to both update it for Terraform v0.12 and retain v0.11 compatibility
|
|
at the same time, because those undocumented workarounds have been replaced
|
|
with new features in Terraform v0.12.
|
|
|
|
## Map variables no longer merge when overridden
|
|
|
|
In prior versions of Terraform, a variable of type `"map"` had a special
|
|
behavior where any value provided via mechanisms such as the `-var` command
|
|
line option would be keywise-merged with any default value associated with
|
|
the variable. This was useful in early versions of Terraform that lacked
|
|
mechanisms for doing such merging explicitly, but since Terraform v0.10
|
|
introduced the concept of local values we consider it preferable to perform
|
|
such merges manually so that they are explicit in configuration:
|
|
|
|
```
|
|
variable "example_map" {
|
|
type = map(string)
|
|
default = {}
|
|
}
|
|
|
|
locals {
|
|
default_map_keys = {
|
|
"a" = "b"
|
|
}
|
|
merged_map_keys = merge(local.default_map_keys, var.example_map)
|
|
}
|
|
```
|
|
|
|
In order to improve the consistency of variable handling across types, the
|
|
map variable merging behavior is removed in Terraform v0.12. Because this
|
|
mechanism was driven by command line options rather than configuration, the
|
|
automatic upgrade tool cannot automatically handle it. If you are relying on
|
|
the merging feature, you must reorganize your configuration to use explicit
|
|
merging like in the above example, or else your default map value will be
|
|
entirely overridden by any explicitly-set value.
|
|
|
|
## Upgrading `remote` Backend Configuration
|
|
|
|
Terraform Cloud and Terraform Enterprise users will need
|
|
to run `terraform init -reconfigure` to upgrade to Terraform 0.12.
|
|
|
|
Terraform provides a message stating that `terraform init` is required; while
|
|
there is no harm in running this command, the next error message will clarify
|
|
that `terraform init -reconfigure` is required.
|
|
|
|
## Upgrading Sentinel policies
|
|
|
|
The Terraform Sentinel imports have been updated to work with Terraform 0.12.
|
|
Care has been taken to ensure that the API is as backwards compatible as
|
|
possible and most policies will continue to work without modification. However,
|
|
there are some important changes and certain policies will need to modified.
|
|
|
|
More information on the changes can be found in our page on [using Sentinel with
|
|
Terraform 0.12](/cloud-docs/sentinel/sentinel-tf-012).
|
|
|
|
It's strongly advised that you test your Sentinel policies after upgrading to
|
|
Terraform 0.12 to ensure they continue to work as expected. [Mock
|
|
generation](/cloud-docs/sentinel/mock) has also been updated to
|
|
produce mock data for the Sentinel imports as they appear in Terraform 0.12.
|
|
|
|
For more information on testing a policy with 0.11 and 0.12 at the same time,
|
|
see the section on [testing a policy with 0.11 and 0.12
|
|
simultaneously](/cloud-docs/sentinel/sentinel-tf-012#testing-a-policy-with-0-11-and-0-12-simultaneously).
|