| --- |
| page_title: Upgrading to Terraform v0.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 make 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](/language/functions/nonsensitive) 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](/language/values/variables#suppressing-values-in-cli-output) |
| * [Sensitive Resource Attributes](/language/expressions/references#sensitive-resource-attributes) |
| * [Sensitive Output Values](/language/values/outputs#sensitive) |
| |
| -> **Note:** The new behavior described in this section was previously |
| available in Terraform v0.14 as the |
| [language experiment](/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](/language/modules/develop/providers#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. |