--- layout: "language" page_title: "Upgrading to Terraform 0.11" sidebar_current: "upgrade-guides-0-11" description: |- Upgrading to Terraform v0.11 --- # Upgrading to Terraform v0.11 Terraform v0.11 is a major release and thus includes some changes that you'll 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/main/CHANGELOG.md). After reviewing this guide, we recommend reviewing the Changelog to check on specific notes about the resources and providers you use. This guide focuses on changes from v0.10 to v0.11. Each previous major release has its own upgrade guide, so please consult the other guides (available in the navigation) if you are upgrading directly from an earlier version. ## Interactive Approval in `terraform apply` Terraform 0.10 introduced a new mode for `terraform apply` (when run without an explicit plan file) where it would show a plan and prompt for approval before proceeding, similar to `terraform destroy`. Terraform 0.11 adopts this as the default behavior for this command, which means that for interactive use in a terminal it is not necessary to separately run `terraform plan -out=...` to safely review and apply a plan. The new behavior also has the additional advantage that, when using a backend that supports locking, the state lock will be held throughout the refresh, plan, confirmation and apply steps, ensuring that a concurrent execution of `terraform apply` will not invalidate the execution plan. A consequence of this change is that `terraform apply` is now interactive by default unless a plan file is provided on the command line. When [running Terraform in automation](https://learn.hashicorp.com/tutorials/terraform/automate-terraform?in=terraform/automation&utm_source=WEBSITE&utm_medium=WEB_IO&utm_offer=ARTICLE_PAGE&utm_content=DOCS) it is always recommended to separate plan from apply, but if existing automation was running `terraform apply` with no arguments it may now be necessary to update it to either generate an explicit plan using `terraform plan -out=...` or to run `terraform apply -auto-approve` to bypass the interactive confirmation step. The latter should be done only in unimportant environments. **Action:** For interactive use in a terminal, prefer to use `terraform apply` with out an explicit plan argument rather than `terraform plan -out=tfplan` followed by `terraform apply tfplan`. **Action:** Update any automation scripts that run Terraform non-interactively so that they either use separated plan and apply or override the confirmation behavior using the `-auto-approve` option. ## Relative Paths in Module `source` Terraform 0.11 introduces full support for module installation from [Terraform Registry](https://registry.terraform.io/) as well as other private, in-house registries using concise module source strings like `hashicorp/consul/aws`. As a consequence, module source strings like `"child"` are no longer interpreted as relative paths. Instead, relative paths must be expressed explicitly by beginning the string with either `./` (for a module in a child directory) or `../` (for a module in the parent directory). **Action:** Update existing module `source` values containing relative paths to start with either `./` or `../` to prevent misinterpretation of the source as a Terraform Registry module. ## Interactions Between Providers and Modules Prior to Terraform 0.11 there were several limitations in deficiencies in how providers interact with child modules, such as: * Ancestor module provider configurations always overrode the associated settings in descendent modules. * There was no well-defined mechanism for passing "aliased" providers from an ancestor module to a descendent, where the descendent needs access to multiple provider instances. Terraform 0.11 changes some of the details of how each resource block is associated with a provider configuration, which may change how Terraform interprets existing configurations. This is notably true in the following situations: * If the same provider is configured in both an ancestor and a descendent module, the ancestor configuration no longer overrides attributes from the descendent and the descendent no longer inherits attributes from its ancestor. Instead, each configuration is entirely distinct. * If a `provider` block is present in a child module, it must either contain a complete configuration for its associated provider or a configuration must be passed from the parent module using [the new `providers` attribute](/docs/configuration-0-11/modules.html#providers-within-modules). In the latter case, an empty provider block is a placeholder that declares that the child module requires a configuration to be passed from its parent. * When a module containing its own `provider` blocks is removed from its parent module, Terraform will no longer attempt to associate it with another provider of the same name in a parent module, since that would often cause undesirable effects such as attempting to refresh resources in the wrong region. Instead, the resources in the module resources must be explicitly destroyed _before_ removing the module, so that the provider configuration is still available: `terraform destroy -target=module.example`. The recommended design pattern moving forward is to place all explicit `provider` blocks in the root module of the configuration, and to pass providers explicitly to child modules so that the associations are obvious from configuration: ```hcl provider "aws" { region = "us-east-1" alias = "use1" } provider "aws" { region = "us-west-1" alias = "usw1" } module "example-use1" { source = "./example" providers = { "aws" = "aws.use1" } } module "example-usw1" { source = "./example" providers = { "aws" = "aws.usw1" } } ``` With the above configuration, any `aws` provider resources in the module `./example` will use the us-east-1 provider configuration for `module.example-use1` and the us-west-1 provider configuration for `module.example-usw1`. When a default (non-aliased) provider is used, and not explicitly declared in a child module, automatic inheritance of that provider is still supported. **Action**: In existing configurations where both a descendent module and one of its ancestor modules both configure the same provider, copy any settings from the ancestor into the descendent because provider configurations now inherit only as a whole, rather than on a per-argument basis. **Action**: In existing configurations where a descendent module inherits _aliased_ providers from an ancestor module, use [the new `providers` attribute](/docs/configuration-0-11/modules.html#providers-within-modules) to explicitly pass those aliased providers. **Action**: Consider refactoring existing configurations so that all provider configurations are set in the root module and passed explicitly to child modules, as described in the following section. ### Moving Provider Configurations to the Root Module With the new provider inheritance model, it is strongly recommended to refactor any configuration where child modules define their own `provider` blocks so that all explicit configuration is defined in the _root_ module. This approach will ensure that removing a module from the configuration will not cause any provider configurations to be removed along with it, and thus ensure that all of the module's resources can be successfully refreshed and destroyed. A common configuration is where two child modules have different configurations for the same provider, like this: ```hcl # root.tf module "network-use1" { source = "./network" region = "us-east-1" } module "network-usw2" { source = "./network" region = "us-west-2" } ``` ```hcl # network/network.tf variable "region" { } provider "aws" { region = "${var.region}" } resource "aws_vpc" "example" { # ... } ``` The above example is problematic because removing either `module.network-use1` or `module.network-usw2` from the root module will make the corresponding provider configuration no longer available, as described in [issue #15762](https://github.com/hashicorp/terraform/issues/15762), which prevents Terraform from refreshing or destroying that module's `aws_vpc.example` resource. This can be addressed by moving the `provider` blocks into the root module as _additional configurations_, and then passing them down to the child modules as _default configurations_ via the explicit `providers` map: ```hcl # root.tf provider "aws" { region = "us-east-1" alias = "use1" } provider "aws" { region = "us-west-2" alias = "usw2" } module "network-use1" { source = "./network" providers = { "aws" = "aws.use1" } } module "network-usw2" { source = "./network" providers = { "aws" = "aws.usw2" } } ``` ```hcl # network/network.tf # Empty provider block signals that we expect a default (unaliased) "aws" # provider to be passed in from the caller. provider "aws" { } resource "aws_vpc" "example" { # ... } ``` After the above refactoring, run `terraform apply` to re-synchoronize Terraform's record (in [the Terraform state](/docs/language/state/index.html)) of the location of each resource's provider configuration. This should make no changes to actual infrastructure, since no resource configurations were changed. For more details on the explicit `providers` map, and discussion of more complex possibilities such as child modules with additional (aliased) provider configurations, see [_Providers Within Modules_](/docs/configuration-0-11/modules.html#providers-within-modules). ## Error Checking for Output Values Prior to Terraform 0.11, if an error occurred when evaluating the `value` expression within an `output` block then it would be silently ignored and the empty string used as the result. This was inconvenient because it made it very hard to debug errors within output expressions. To give better feedback, Terraform now halts and displays an error message when such errors occur, similar to the behavior for expressions elsewhere in the configuration. Unfortunately, this means that existing configurations may have erroneous outputs lurking that will become fatal errors after upgrading to Terraform 0.11. The prior behavior is no longer available; to apply such a configuration with Terraform 0.11 will require adjusting the configuration to avoid the error. **Action:** If any existing output value expressions contain errors, change these expressions to fix the error. ### Referencing Attributes from Resources with `count = 0` A common pattern for conditional resources is to conditionally set count to either `0` or `1` depending on the result of a boolean expression: ```hcl resource "aws_instance" "example" { count = "${var.create_instance ? 1 : 0}" # ... } ``` When using this pattern, it's required to use a special idiom to access attributes of this resource to account for the case where no resource is created at all: ```hcl output "instance_id" { value = "${element(concat(aws_instance.example.*.id, list("")), 0)}" } ``` Accessing `aws_instance.example.id` directly is an error when `count = 0`. This is true for all situations where interpolation expressions are allowed, but previously _appeared_ to work for outputs due to the suppression of the error. Existing outputs that access non-existent resources must be updated to use the idiom above after upgrading to 0.11.0.