github.com/hugorut/terraform@v1.1.3/website/docs/language/upgrade-guides/0-11.mdx (about) 1 --- 2 page_title: Upgrading to Terraform 0.11 3 description: Upgrading to Terraform v0.11 4 --- 5 6 # Upgrading to Terraform v0.11 7 8 Terraform v0.11 is a major release and thus includes some changes that 9 you'll need to consider when upgrading. This guide is intended to help with 10 that process. 11 12 The goal of this guide is to cover the most common upgrade concerns and 13 issues that would benefit from more explanation and background. The exhaustive 14 list of changes will always be the 15 [Terraform Changelog](https://github.com/hugorut/terraform/blob/main/CHANGELOG.md). 16 After reviewing this guide, we recommend reviewing the Changelog to check on 17 specific notes about the resources and providers you use. 18 19 This guide focuses on changes from v0.10 to v0.11. Each previous major release 20 has its own upgrade guide, so please consult the other guides (available 21 in the navigation) if you are upgrading directly from an earlier version. 22 23 ## Interactive Approval in `terraform apply` 24 25 Terraform 0.10 introduced a new mode for `terraform apply` (when run without 26 an explicit plan file) where it would show a plan and prompt for approval 27 before proceeding, similar to `terraform destroy`. 28 29 Terraform 0.11 adopts this as the default behavior for this command, which 30 means that for interactive use in a terminal it is not necessary to separately 31 run `terraform plan -out=...` to safely review and apply a plan. 32 33 The new behavior also has the additional advantage that, when using a backend 34 that supports locking, the state lock will be held throughout the refresh, 35 plan, confirmation and apply steps, ensuring that a concurrent execution 36 of `terraform apply` will not invalidate the execution plan. 37 38 A consequence of this change is that `terraform apply` is now interactive by 39 default unless a plan file is provided on the command line. When 40 [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) 41 it is always recommended to separate plan from apply, but if existing automation 42 was running `terraform apply` with no arguments it may now be necessary to 43 update it to either generate an explicit plan using `terraform plan -out=...` 44 or to run `terraform apply -auto-approve` to bypass the interactive confirmation 45 step. The latter should be done only in unimportant environments. 46 47 **Action:** For interactive use in a terminal, prefer to use `terraform apply` 48 with out an explicit plan argument rather than `terraform plan -out=tfplan` 49 followed by `terraform apply tfplan`. 50 51 **Action:** Update any automation scripts that run Terraform non-interactively 52 so that they either use separated plan and apply or override the confirmation 53 behavior using the `-auto-approve` option. 54 55 ## Relative Paths in Module `source` 56 57 Terraform 0.11 introduces full support for module installation from 58 [Terraform Registry](https://registry.terraform.io/) as well as other 59 private, in-house registries using concise module source strings like 60 `hashicorp/consul/aws`. 61 62 As a consequence, module source strings like `"child"` are no longer 63 interpreted as relative paths. Instead, relative paths must be expressed 64 explicitly by beginning the string with either `./` (for a module in a child 65 directory) or `../` (for a module in the parent directory). 66 67 **Action:** Update existing module `source` values containing relative paths 68 to start with either `./` or `../` to prevent misinterpretation of the source 69 as a Terraform Registry module. 70 71 ## Interactions Between Providers and Modules 72 73 Prior to Terraform 0.11 there were several limitations in deficiencies in 74 how providers interact with child modules, such as: 75 76 * Ancestor module provider configurations always overrode the associated 77 settings in descendent modules. 78 79 * There was no well-defined mechanism for passing "aliased" providers from 80 an ancestor module to a descendent, where the descendent needs access to 81 multiple provider instances. 82 83 Terraform 0.11 changes some of the details of how each resource block is 84 associated with a provider configuration, which may change how Terraform 85 interprets existing configurations. This is notably true in the following 86 situations: 87 88 * If the same provider is configured in both an ancestor and a descendent 89 module, the ancestor configuration no longer overrides attributes from 90 the descendent and the descendent no longer inherits attributes from 91 its ancestor. Instead, each configuration is entirely distinct. 92 93 * If a `provider` block is present in a child module, it must either contain a 94 complete configuration for its associated provider or a configuration must be 95 passed from the parent module using 96 [the new `providers` attribute](/language/configuration-0-11/modules#providers-within-modules). 97 In the latter case, an empty provider block is a placeholder that declares 98 that the child module requires a configuration to be passed from its parent. 99 100 * When a module containing its own `provider` blocks is removed from its 101 parent module, Terraform will no longer attempt to associate it with 102 another provider of the same name in a parent module, since that would 103 often cause undesirable effects such as attempting to refresh resources 104 in the wrong region. Instead, the resources in the module resources must be 105 explicitly destroyed _before_ removing the module, so that the provider 106 configuration is still available: `terraform destroy -target=module.example`. 107 108 The recommended design pattern moving forward is to place all explicit 109 `provider` blocks in the root module of the configuration, and to pass 110 providers explicitly to child modules so that the associations are obvious 111 from configuration: 112 113 ```hcl 114 provider "aws" { 115 region = "us-east-1" 116 alias = "use1" 117 } 118 119 provider "aws" { 120 region = "us-west-1" 121 alias = "usw1" 122 } 123 124 module "example-use1" { 125 source = "./example" 126 127 providers = { 128 "aws" = "aws.use1" 129 } 130 } 131 132 module "example-usw1" { 133 source = "./example" 134 135 providers = { 136 "aws" = "aws.usw1" 137 } 138 } 139 ``` 140 141 With the above configuration, any `aws` provider resources in the module 142 `./example` will use the us-east-1 provider configuration for 143 `module.example-use1` and the us-west-1 provider configuration for 144 `module.example-usw1`. 145 146 When a default (non-aliased) provider is used, and not explicitly 147 declared in a child module, automatic inheritance of that provider is still 148 supported. 149 150 **Action**: In existing configurations where both a descendent module and 151 one of its ancestor modules both configure the same provider, copy any 152 settings from the ancestor into the descendent because provider configurations 153 now inherit only as a whole, rather than on a per-argument basis. 154 155 **Action**: In existing configurations where a descendent module inherits 156 _aliased_ providers from an ancestor module, use 157 [the new `providers` attribute](/language/configuration-0-11/modules#providers-within-modules) 158 to explicitly pass those aliased providers. 159 160 **Action**: Consider refactoring existing configurations so that all provider 161 configurations are set in the root module and passed explicitly to child 162 modules, as described in the following section. 163 164 ### Moving Provider Configurations to the Root Module 165 166 With the new provider inheritance model, it is strongly recommended to refactor 167 any configuration where child modules define their own `provider` blocks so 168 that all explicit configuration is defined in the _root_ module. This approach 169 will ensure that removing a module from the configuration will not cause 170 any provider configurations to be removed along with it, and thus ensure that 171 all of the module's resources can be successfully refreshed and destroyed. 172 173 A common configuration is where two child modules have different configurations 174 for the same provider, like this: 175 176 ```hcl 177 # root.tf 178 179 module "network-use1" { 180 source = "./network" 181 region = "us-east-1" 182 } 183 184 module "network-usw2" { 185 source = "./network" 186 region = "us-west-2" 187 } 188 ``` 189 190 ```hcl 191 # network/network.tf 192 193 variable "region" { 194 } 195 196 provider "aws" { 197 region = "${var.region}" 198 } 199 200 resource "aws_vpc" "example" { 201 # ... 202 } 203 ``` 204 205 The above example is problematic because removing either `module.network-use1` 206 or `module.network-usw2` from the root module will make the corresponding 207 provider configuration no longer available, as described in 208 [issue #15762](https://github.com/hugorut/terraform/issues/15762), which 209 prevents Terraform from refreshing or destroying that module's `aws_vpc.example` 210 resource. 211 212 This can be addressed by moving the `provider` blocks into the root module 213 as _additional configurations_, and then passing them down to the child 214 modules as _default configurations_ via the explicit `providers` map: 215 216 ```hcl 217 # root.tf 218 219 provider "aws" { 220 region = "us-east-1" 221 alias = "use1" 222 } 223 224 provider "aws" { 225 region = "us-west-2" 226 alias = "usw2" 227 } 228 229 module "network-use1" { 230 source = "./network" 231 232 providers = { 233 "aws" = "aws.use1" 234 } 235 } 236 237 module "network-usw2" { 238 source = "./network" 239 240 providers = { 241 "aws" = "aws.usw2" 242 } 243 } 244 ``` 245 246 ```hcl 247 # network/network.tf 248 249 # Empty provider block signals that we expect a default (unaliased) "aws" 250 # provider to be passed in from the caller. 251 provider "aws" { 252 } 253 254 resource "aws_vpc" "example" { 255 # ... 256 } 257 ``` 258 259 After the above refactoring, run `terraform apply` to re-synchoronize 260 Terraform's record (in [the Terraform state](/language/state)) of the 261 location of each resource's provider configuration. This should make no changes 262 to actual infrastructure, since no resource configurations were changed. 263 264 For more details on the explicit `providers` map, and discussion of more 265 complex possibilities such as child modules with additional (aliased) provider 266 configurations, see [_Providers Within Modules_](/language/configuration-0-11/modules#providers-within-modules). 267 268 ## Error Checking for Output Values 269 270 Prior to Terraform 0.11, if an error occurred when evaluating the `value` 271 expression within an `output` block then it would be silently ignored and 272 the empty string used as the result. This was inconvenient because it made it 273 very hard to debug errors within output expressions. 274 275 To give better feedback, Terraform now halts and displays an error message 276 when such errors occur, similar to the behavior for expressions elsewhere 277 in the configuration. 278 279 Unfortunately, this means that existing configurations may have erroneous 280 outputs lurking that will become fatal errors after upgrading to Terraform 0.11. 281 The prior behavior is no longer available; to apply such a configuration with 282 Terraform 0.11 will require adjusting the configuration to avoid the error. 283 284 **Action:** If any existing output value expressions contain errors, change these 285 expressions to fix the error. 286 287 ### Referencing Attributes from Resources with `count = 0` 288 289 A common pattern for conditional resources is to conditionally set count 290 to either `0` or `1` depending on the result of a boolean expression: 291 292 ```hcl 293 resource "aws_instance" "example" { 294 count = "${var.create_instance ? 1 : 0}" 295 296 # ... 297 } 298 ``` 299 300 When using this pattern, it's required to use a special idiom to access 301 attributes of this resource to account for the case where no resource is 302 created at all: 303 304 ```hcl 305 output "instance_id" { 306 value = "${element(concat(aws_instance.example.*.id, list("")), 0)}" 307 } 308 ``` 309 310 Accessing `aws_instance.example.id` directly is an error when `count = 0`. 311 This is true for all situations where interpolation expressions are allowed, 312 but previously _appeared_ to work for outputs due to the suppression of the 313 error. Existing outputs that access non-existent resources must be updated to 314 use the idiom above after upgrading to 0.11.0.