A future-proof Terraform provider definition
When defining the version of a Terraform provider, do not use >
or =>
conditions. You will run into troubles caused by breaking changes with the next major release. Instead, lock the major version of the Terraform provider by using a ~>
condition.
But let’s start at the beginning.
Problem
When running terraform apply
to deploy a small change to a code base that I had not touched for a while, I ran into the following error.
An argument named "enable_classiclink" is not expected here. |
While debugging the issue, I learned a lot about Terraform version constraints that you should also be aware of to avoid unpleasant surprises.
What happened?
I wrote the following code a few years ago to create a VPC and some other resources.
terraform { |
But, after updating to the latest AWS provider version, I was stuck with an An argument named "enable_classiclink" is not expected here.
error.
terraform { |
After a while, I found out that the major release 5.0.0 of the AWS provider introduced a few breaking changes. The change log includes the following entry.
resource/aws_default_vpc: With the retirement of EC2-Classic the enable_classiclink and enable_classiclink_dns_support attributes have been removed (#30966) |
But, the terraform-aws-modules/vpc/aws
module in version 3.19.0
still used the enable_classiclink_dns_support
attribute, which caused the error.
It turns out that the terraform-aws-modules/vpc/aws module follows the major release cycle of the AWS provider. However, the module does not enforce using an AWS provider with the supported major version.
Here is how the terraform-aws-modules/vpc/aws
module specifies the AWS provider version.
terraform { |
The version condition states that the module works with any AWS provider version greater or equal to 3.73
. But that is not the case because each major version of the AWS provider introduces breaking changes.
Solution
When defining the required version, use the ~>
condition instead, which the Terraform documentation describes as follows:
“Allows only the rightmost version component to increment. For example, to allow new patch releases within a specific minor release, use the full version number: ~> 1.0.4 will allow installation of 1.0.5 and 1.0.10 but not 1.1.0. This is usually called the pessimistic constraint operator.” (see Terraform: Version Constraints)
Back to the example from above, the terraform-aws-modules/vpc/aws
module should specify the AWS provider as follows.
terraform { |
Doing so would pick the latest version of the AWS provider with major version 3. Newer major versions (4 or 5) with potential breaking changes are not supported.
Note that this approach requires all parts of your Terraform configuration -including all the modules you use- to use the same major provider versions. Alternatively, you could use tools like terragrunt that execute modules separately.