538 lines
23 KiB
Markdown
538 lines
23 KiB
Markdown
---
|
|
layout: "language"
|
|
page_title: "Upgrading to Terraform v0.15"
|
|
sidebar_current: "upgrade-guides-0-15"
|
|
description: |-
|
|
Upgrading to Terraform v0.15
|
|
---
|
|
|
|
# Upgrading to Terraform v0.15
|
|
|
|
Terraform v0.15 is a major release and so it includes some small changes in
|
|
behavior that you may need to consider when upgrading. This guide is intended
|
|
to help with that process.
|
|
|
|
The goal of this guide is to cover the most common upgrade concerns and
|
|
issues that would benefit from more explanation and background. The exhaustive
|
|
list of changes will always be
|
|
[the Terraform Changelog](https://github.com/hashicorp/terraform/blob/v0.15/CHANGELOG.md).
|
|
After reviewing this guide, we recommend reviewing the Changelog to check for
|
|
specific notes about less-commonly-used features.
|
|
|
|
This guide is also not intended as an overview of the new features in
|
|
Terraform v0.15. This release includes other enhancements that don't need any
|
|
special attention during upgrade, but those are described in the changelog and
|
|
elsewhere in the Terraform documentation.
|
|
|
|
This guide focuses on changes from v0.14 to v0.15. Terraform supports upgrade
|
|
tools and features only for one major release upgrade at a time, so if you are
|
|
currently using a version of Terraform prior to v0.14 please upgrade through
|
|
the latest minor releases of all of the intermediate versions first, reviewing
|
|
the previous upgrade guides for any considerations that may be relevant to you.
|
|
|
|
Unlike the previous few Terraform major releases, v0.15's upgrade concerns are
|
|
largely conclusions of deprecation cycles left over from previous releases,
|
|
many of which already had deprecation warnings in v0.14. If you previously
|
|
responded to those while using Terraform v0.14 then you hopefully won't need
|
|
to many any special changes to upgrade, although we still recommend reviewing
|
|
the content below to confirm, particularly if you see new errors or unexpected
|
|
behavior after upgrading from Terraform v0.14.
|
|
|
|
-> If you run into any problems during upgrading that are not addressed by the
|
|
information in this guide, please feel free to start a topic in
|
|
[The Terraform community forum](https://discuss.hashicorp.com/c/terraform-core),
|
|
describing the problem you've encountered in enough detail that other readers
|
|
may be able to reproduce it and offer advice.
|
|
|
|
Upgrade guide sections:
|
|
|
|
* [Sensitive Output Values](#sensitive-output-values)
|
|
* [Legacy Configuration Language Features](#legacy-configuration-language-features)
|
|
* [Alternative (Aliased) Provider Configurations Within Modules](#alternative-provider-configurations-within-modules)
|
|
* [Commands Accepting a Configuration Directory Argument](#commands-accepting-a-configuration-directory-argument)
|
|
* [Microsoft Windows Terminal Support](#microsoft-windows-terminal-support)
|
|
* [Other Minor Command Line Behavior Changes](#other-minor-command-line-behavior-changes)
|
|
* [Azure Backend `arm_`-prefixed Arguments](#azure-backend-removed-arguments)
|
|
|
|
## Sensitive Output Values
|
|
|
|
Terraform v0.14 previously introduced the ability for Terraform to track and
|
|
propagate the "sensitivity" of values through expressions that include
|
|
references to sensitive input variables and output values. For example:
|
|
|
|
```hcl
|
|
variable "example" {
|
|
type = string
|
|
sensitive = true
|
|
}
|
|
|
|
resource "example" "example" {
|
|
# The following value is also treated as sensitive, because it's derived
|
|
# from var.example.
|
|
name = "foo-${var.example}"
|
|
}
|
|
```
|
|
|
|
As part of that feature, Terraform also began requiring you to mark an output
|
|
value as sensitive if its definition includes any sensitive values itself:
|
|
|
|
```hcl
|
|
output "example" {
|
|
value = "foo-${var.example}"
|
|
|
|
# Must mark this output value as sensitive, because it's derived from
|
|
# var.example that is declared as sensitive.
|
|
sensitive = true
|
|
}
|
|
```
|
|
|
|
Terraform v0.15 extends this mechanism to also work for values derived from
|
|
resource attributes that the provider has declared as being sensitive.
|
|
Provider developers will typically mark an attribute as sensitive if the
|
|
remote system has documented the corresponding field as being sensitive, such
|
|
as if the attribute always contains a password or a private key.
|
|
|
|
As a result of that, after upgrading to Terraform v0.15 you may find that
|
|
Terraform now reports some of your output values as invalid, if they were
|
|
derived from sensitive attributes without also being marked as sensitive:
|
|
|
|
```
|
|
╷
|
|
│ Error: Output refers to sensitive values
|
|
│
|
|
│ on sensitive-resource-attr.tf line 5:
|
|
│ 5: output "private_key" {
|
|
│
|
|
│ Expressions used in outputs can only refer to sensitive values if the
|
|
│ sensitive attribute is true.
|
|
╵
|
|
```
|
|
|
|
If you were intentionally exporting a sensitive value, you can address the
|
|
error by adding an explicit declaration `sensitive = true` to the output
|
|
value declaration:
|
|
|
|
```hcl
|
|
output "private_key" {
|
|
value = tls_private_key.example.private_key_pem
|
|
sensitive = true
|
|
}
|
|
```
|
|
|
|
With that addition, if this output value was a root module output value then
|
|
Terraform will hide its value in the `terraform plan` and `terraform apply`
|
|
output:
|
|
|
|
```
|
|
Changes to Outputs:
|
|
+ private_key = (sensitive value)
|
|
```
|
|
|
|
-> **Note:** The declaration of an output value as sensitive must be made
|
|
within the module that declares the output, so if you depend on a third-party
|
|
module that has a sensitive output value that is lacking this declaration then
|
|
you will need to wait for a new version of that module before you can upgrade
|
|
to Terraform v0.15.
|
|
|
|
The value is only hidden in the main UI, and so the sensitive value
|
|
will still be recorded in the state. If you declared this output value in order
|
|
to use it as part of integration with other software, you can still retrieve
|
|
the cleartext value using commands intended for machine rather than human
|
|
consumption, such as `terraform output -json` or `terraform output -raw`:
|
|
|
|
```shellsession
|
|
$ terraform output -raw private_key
|
|
-----BEGIN RSA PRIVATE KEY-----
|
|
MIIEowIBAAKCAQEAoahsvJ1rIxTIOOmJZ7yErs5eOq/Kv9+5l3h0LbxW78K8//Kb
|
|
OMU3v8F3h8jp+AB/1zGr5UBYfnYp5ncJm/OTCXLFAHxGibEbRnf1m2A3o0hEaWsw
|
|
# (etc...)
|
|
```
|
|
|
|
If you consider Terraform's treatment of a sensitive value to be too
|
|
conservative and you'd like to force Terraform to treat a sensitive value as
|
|
non-sensitive, you can use
|
|
[the `nonsensitive` function](/docs/language/functions/nonsensitive.html) to
|
|
override Terraform's automatic detection:
|
|
|
|
```hcl
|
|
output "private_key" {
|
|
# WARNING: Terraform will display this result as cleartext
|
|
value = nonsensitive(tls_private_key.example.private_key_pem)
|
|
}
|
|
```
|
|
|
|
For more information on the various situations where sensitive values can
|
|
originate in Terraform, refer to the following sections:
|
|
|
|
* [Sensitive Input Variables](/docs/language/values/variables.html#suppressing-values-in-cli-output)
|
|
* [Sensitive Resource Attributes](/docs/language/expressions/references.html#sensitive-resource-attributes)
|
|
* [Sensitive Output Values](/docs/language/values/outputs.html#sensitive)
|
|
|
|
-> **Note:** The new behavior described in this section was previously
|
|
available in Terraform v0.14 as the
|
|
[language experiment](/docs/language/settings/#experimental-language-features)
|
|
`provider_sensitive_attrs`. That experiment has now concluded, so if you were
|
|
participating in that experiment then you'll now need to remove the experiment
|
|
opt-in from your module as part of upgrading to Terraform v0.15.
|
|
|
|
## Legacy Configuration Language Features
|
|
|
|
Terraform v0.12 introduced new syntax for a variety of existing Terraform
|
|
language features that were intended to make the language easier to read and
|
|
write and, in some cases, to better allow for future changes to the language.
|
|
|
|
Many of the old forms remained available but deprecated from v0.12 through to
|
|
v0.14, with these deprecations finally concluding in the v0.15 release. Those
|
|
who used the `terraform 0.12upgrade` command when upgrading from Terraform v0.11
|
|
to v0.12 will have had these updated automatically, but we've summarized the
|
|
changes below to help with any remaining legacy forms you might encounter while
|
|
upgrading to Terraform v0.15:
|
|
|
|
* The built-in functions `list` and `map` were replaced with first-class syntax
|
|
`[ ... ]` and `{ ... }` in Terraform v0.12, and we've now removed the
|
|
deprecated functions in order to resolve the ambiguity with the syntax used
|
|
to declare list and map type constraints inside `variable` blocks.
|
|
|
|
If you need to update a module which was using the `list` function, you
|
|
can get the same result by replacing `list(...)` with `tolist([...])`.
|
|
For example:
|
|
|
|
```diff
|
|
- list("a", "b", "c")
|
|
+ tolist(["a", "b", "c"])
|
|
```
|
|
|
|
If you need to update a module which was using the `map` function, you
|
|
can get the same result by replacing `map(...)` with `tomap({...})`.
|
|
For example:
|
|
|
|
```diff
|
|
- map("a", 1, "b", 2)
|
|
+ tomap({ a = 1, b = 2 })
|
|
```
|
|
|
|
The above examples include the type conversion functions `tolist` and
|
|
`tomap` to ensure that the result will always be of the same type as
|
|
before. However, in most situations those explicit type conversions won't
|
|
be necessary because Terraform can infer the necessary type conversions
|
|
automatically from context. In those cases, you can just use the
|
|
`[ ... ]` or `{ ... }` syntax directly, without a conversion function.
|
|
|
|
* In `variable` declaration blocks, the `type` argument previously accepted
|
|
v0.11-style type constraints given as quoted strings. This legacy syntax
|
|
is removed in Terraform v0.15.
|
|
|
|
To update an old-style type constraint to the modern syntax, start by
|
|
removing the quotes so that the argument is a bare keyword rather than
|
|
a string:
|
|
|
|
```hcl
|
|
variable "example" {
|
|
type = string
|
|
}
|
|
```
|
|
|
|
Additionally, if the previous type constraint was either `"list"` or
|
|
`"map`", add a type argument to specify the element type of the collection.
|
|
Terraform v0.11 typically supported only collections of strings, so in
|
|
most cases you can set the element type to `string`:
|
|
|
|
```hcl
|
|
variable "example" {
|
|
type = list(string)
|
|
}
|
|
|
|
variable "example" {
|
|
type = map(string)
|
|
}
|
|
```
|
|
|
|
* In `lifecycle` blocks nested inside `resource` blocks, Terraform previously
|
|
supported a legacy value `["*"]` for the `ignore_changes` argument, which
|
|
is removed in Terraform v0.15.
|
|
|
|
Instead, use the `all` keyword to indicate that you wish to ignore changes
|
|
to all of the resource arguments:
|
|
|
|
```hcl
|
|
lifecycle {
|
|
ignore_changes = all
|
|
}
|
|
```
|
|
|
|
* Finally, Terraform v0.11 and earlier required all non-constant expressions
|
|
to be written using string interpolation syntax, even if the result was
|
|
not a string. Terraform v0.12 introduced a less confusing syntax where
|
|
arguments can accept any sort of expression without any special wrapping,
|
|
and so the interpolation-style syntax has been redundant and deprecated
|
|
in recent Terraform versions.
|
|
|
|
For this particular change we have not made the older syntax invalid, but
|
|
we do still recommend updating interpolation-only expressions to bare
|
|
expressions to improve readability:
|
|
|
|
```diff
|
|
- example = "${var.foo}"
|
|
+ example = var.foo
|
|
```
|
|
|
|
This only applies to arguments where the value is a single expression without
|
|
any string concatenation. You must continue to use the `${ ... }` syntax for
|
|
situations where you are combining string values together into a larger
|
|
string.
|
|
|
|
The `terraform fmt` command can detect and repair simple examples of the
|
|
legacy interpolation-only syntax, and so we'd recommend running
|
|
`terraform fmt` on your modules once you've addressed any of the other
|
|
situations above that could block configuration parsing in order to update
|
|
your configurations to the typical Terraform language style conventions.
|
|
|
|
## Alternative Provider Configurations Within Modules
|
|
|
|
Terraform's provider configuration scheme includes the idea of a "default"
|
|
(unaliased) provider configuration along with zero or more alternative
|
|
(aliased) provider configurations.
|
|
|
|
The `required_providers` block now has a new field for providers to indicate
|
|
aliased configuration names, replacing the need for an empty "proxy
|
|
configuration block" as a placeholder. In order to declare
|
|
[configuration aliases](/docs/language/modules/develop/providers.html#provider-aliases-within-modules),
|
|
add the desired names to the `configuration_aliases` argument for the provider
|
|
requirements.
|
|
|
|
```hcl
|
|
terraform {
|
|
required_providers {
|
|
aws = {
|
|
source = "hashicorp/aws"
|
|
version = ">= 2.7.0"
|
|
configuration_aliases = [ aws.alternate ]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Warnings will be emitted now where empty configuration blocks are present but
|
|
no longer required, though they continue to work unchanged in the 0.15 release.
|
|
There are a few cases where existing configurations may return new errors:
|
|
|
|
* The `providers` map in a module call cannot override a provider configured
|
|
within the module. This is not a supported configuration, but was previously
|
|
missed in validation and now returns an error.
|
|
|
|
* A provider alias within a module that has no configuration _requires_ a
|
|
provider configuration be supplied in the module `providers` map.
|
|
|
|
* All entries in the `providers` map in a module call must correspond to a
|
|
provider name within the module. Passing in a configuration to an undeclared
|
|
provider is now an error.
|
|
|
|
## Commands Accepting a Configuration Directory Argument
|
|
|
|
A subset of Terraform's CLI commands have historically accepted a final
|
|
positional argument to specify which directory contains the root module of
|
|
the configuration, overriding the default behavior of expecting to find it
|
|
in the current working directory.
|
|
|
|
However, the design of that argument was flawed in a number of ways due to
|
|
it being handled at the wrong level of abstraction: it only changed where
|
|
Terraform looks for configuration and not any of the other files that Terraform
|
|
might search for, and that could therefore violate assumptions that Terraform
|
|
configurations might make about the locations of different files, leading
|
|
to confusing error messages. It was also not possible to support this usage
|
|
pattern across all commands due to those commands using positional arguments
|
|
in other ways.
|
|
|
|
To address these design flaws, Terraform v0.14 introduced a new global option
|
|
`-chdir` which you can use before the subcommand name, causing Terraform to
|
|
run the subcommand as if the given directory had been the current working
|
|
directory:
|
|
|
|
```shellsession
|
|
$ terraform -chdir=example init
|
|
```
|
|
|
|
This command causes the Terraform process to actually change its current
|
|
working directory to the given directory before launching the subcommand, and
|
|
so now any relative paths accessed by the subcommand will be treated as
|
|
relative to that directory, including (but not limited to) the following key
|
|
directory conventions:
|
|
|
|
* As with the positional arguments that `-chdir` replaces, Terraform will look
|
|
for the root module's `.tf` and `.tf.json` files in the given directory.
|
|
|
|
* The `.tfvars` and `.tfvars.json` files that Terraform automatically searches
|
|
for, and any relative paths given in `-var-file` options, will be searched
|
|
in the given directory.
|
|
|
|
* The `.terraform` directory which Terraform creates to retain the working
|
|
directory internal state will appear in the given directory, rather than
|
|
the current working directory.
|
|
|
|
After treating the v0.14 releases as a migration period for this new behavior,
|
|
Terraform CLI v0.15 no longer accepts configuration directories on any
|
|
command except `terraform fmt`. (`terraform fmt` is special compared to the
|
|
others because it primarily deals with configuration files in isolation,
|
|
rather than modules or configurations as a whole.)
|
|
|
|
If you built automation which previously relied on overriding the configuration
|
|
directory alone, you will need to transition to using the `-chdir` command line
|
|
option before upgrading to Terraform v0.15.
|
|
|
|
Since the `-chdir` argument behavior is more comprehensive than the positional
|
|
arguments it has replaced, you may need to make some further changes in the
|
|
event that your automation was relying on the limitations of the old mechanism:
|
|
|
|
* If your system depends on the `.terraform` directory being created in the
|
|
_real_ current working directory while using a root module defined elsewhere,
|
|
you can use the `TF_DATA_DIR` environment variable to specify the absolute
|
|
path where Terraform should store its working directory internal state:
|
|
|
|
```bash
|
|
TF_DATA_DIR="$PWD/.terraform"
|
|
```
|
|
|
|
* If your system uses `.tfvars` or `.tfvars.json` files either implicitly found
|
|
or explicitly selected in the current working directory, you must either
|
|
move those variables files into the root module directory or specify your
|
|
files from elsewhere explicitly using the `-var-file` command line option:
|
|
|
|
```bash
|
|
terraform plan -var-file="$PWD/example.tfvars"
|
|
```
|
|
|
|
As a special case for backward compatibility, Terraform ensures that the
|
|
language expression `path.cwd` will return the _original_ working directory,
|
|
before overriding with `-chdir`, so that existing configurations referring to
|
|
files in that directory can still work. If you want to refer to files in the
|
|
directory given in `-chdir` then you can use `path.root`, which returns the
|
|
directory containing the configuration's root module.
|
|
|
|
## Microsoft Windows Terminal Support
|
|
|
|
Until the first Windows 10 update, Microsoft Windows had a console window
|
|
implementation with an API incompatible with the virtual terminal approach
|
|
taken on all other platforms that Terraform supports.
|
|
|
|
Previous versions of Terraform accommodated this by using an API translation
|
|
layer which could convert a subset of typical virtual terminal sequences into
|
|
corresponding Windows Console API function calls, but as a result this has
|
|
prevented Terraform from using more complex terminal features such as progress
|
|
indicators that update in place, menu prompts, etc.
|
|
|
|
Over the course of several updates to Windows 10, Microsoft has introduced
|
|
virtual terminal support similar to other platforms and
|
|
[now recommends the virtual terminal approach for console application developers](https://docs.microsoft.com/en-us/windows/console/classic-vs-vt).
|
|
In response to that recommendation, Terraform v0.15 no longer includes the
|
|
terminal API translation layer and consequently it will, by default, produce
|
|
incorrectly-formatted output on Windows 8 and earlier, and on non-updated
|
|
original retail Windows 10 systems.
|
|
|
|
If you need to keep using Terraform on an older version of Windows, there are
|
|
two possible workarounds available in the v0.15.0 release:
|
|
|
|
* Run Terraform commands using the `-no-color` command line option to disable
|
|
the terminal formatting sequences.
|
|
|
|
This will cause the output to be unformatted plain text, but as a result
|
|
will avoid the output being interspersed with uninterpreted terminal
|
|
control sequences.
|
|
|
|
* Alternatively, you can use Terraform v0.15.0 in various third-party
|
|
virtual terminal implementations for older Windows versions, including
|
|
[ConEmu](https://conemu.github.io/), [Cmder](https://cmder.net/),
|
|
and [mintty](https://mintty.github.io/).
|
|
|
|
Although we have no immediate plans to actively block running Terraform on
|
|
older versions of Windows, we will not be able to test future versions of
|
|
Terraform on those older versions and so later releases may contain
|
|
unintended regressions. We recommend planning an upgrade to a modern Windows
|
|
release on any system where you expect to continue using Terraform CLI.
|
|
|
|
## Other Minor Command Line Behavior Changes
|
|
|
|
Finally, Terraform v0.15 includes a small number of minor changes to the
|
|
details of some commands and command line arguments, as part of a general
|
|
cleanup of obsolete features and improved consistency:
|
|
|
|
* Interrupting Terraform commands with your operating system's interrupt
|
|
signal (`SIGINT` on Unix systems) will now cause Terraform to exit with
|
|
a non-successful exit code. Previously it would, in some cases, exit with
|
|
a success code.
|
|
|
|
This signal is typically sent to Terraform when you press
|
|
Ctrl+C or similar interrupt keyboard shortcuts in an interactive terminal,
|
|
but might also be used by automation in order to gracefully cancel a
|
|
long-running Terraform operation.
|
|
|
|
* The `-lock` and `-lock-timeout` options are no longer available for the
|
|
`terraform init` command. Locking applies to operations that can potentially
|
|
change remote objects, to help ensure that two concurrent Terraform processes
|
|
don't try to run conflicting operations, but `terraform init` does not
|
|
interact with any providers in order to possibly effect such changes.
|
|
|
|
These options didn't do anything in the `terraform init` command before,
|
|
and so you can remove them from any automated calls with no change
|
|
in behavior.
|
|
|
|
* The `-verify-plugins` and `-get-plugins` options to `terraform init` are
|
|
no longer available. These have been non-functional since Terraform v0.13,
|
|
with the introduction of the new Terraform Registry-based provider installer,
|
|
because in practice there are very few operations Terraform can perform which
|
|
both require a `terraform init` but can also run without valid provider
|
|
plugins installed.
|
|
|
|
If you were using these options in automated calls to `terraform init`,
|
|
remove them from the command line for compatibility with Terraform v0.15.
|
|
There is no longer an option to initialize without installing the
|
|
required provider plugins.
|
|
|
|
* The `terraform destroy` command no longer accepts the option `-force`. This
|
|
was a previous name for the option in earlier Terraform versions, but we've
|
|
since adopted `-auto-approve` for consistency with the `terraform apply`
|
|
command.
|
|
|
|
If you are using `-force` in an automated call to `terraform destroy`,
|
|
change to using `-auto-approve` instead.
|
|
|
|
## Azure Backend Removed Arguments
|
|
|
|
In an earlier release the `azure` backend changed to remove the `arm_` prefix
|
|
from a number of the configuration arguments:
|
|
|
|
| Old Name | New Name |
|
|
|-----------------------|-------------------|
|
|
| `arm_client_id` | `client_id` |
|
|
| `arm_client_secret` | `client_secret` |
|
|
| `arm_subscription_id` | `subscription_id` |
|
|
| `arm_tenant_id` | `tenant_id` |
|
|
|
|
The old names were previously deprecated, but we've removed them altogether
|
|
in Terraform v0.15 in order to conclude that deprecation cycle.
|
|
|
|
If you have a backend configuration using the old names then you may see
|
|
errors like the following when upgrading to Terraform v0.15:
|
|
|
|
```
|
|
╷
|
|
│ Error: Invalid backend configuration argument
|
|
│
|
|
│ The backend configuration argument "arm_client_id" given on
|
|
│ the command line is not expected for the selected backend type.
|
|
╵
|
|
```
|
|
|
|
If you see errors like this, rename the arguments in your backend configuration
|
|
as shown in the table above and then run the following to re-initialize your
|
|
backend configuration:
|
|
|
|
```
|
|
terraform init -reconfigure
|
|
```
|
|
|
|
The `-reconfigure` argument instructs Terraform to just replace the old
|
|
configuration with the new configuration directly, rather than offering to
|
|
migrate the latest state snapshots from the old to the new configuration.
|
|
Migration would not be appropriate in this case because the old and new
|
|
configurations are equivalent and refer to the same remote objects.
|