| --- |
| 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). |