This settles the terraform and providers version management. It does not discuss the terraform module version management.
Be flexible about versions in modules; be unambiguous about versions in layers.
At the root of a layer (ie, the directory where “terraform apply” is run),
best practice is to specify an exact version of Terraform to use. This is due
to terraform state incompatibility between 0.X versions. Use the
= 0.2.3
constraint to do this.
For more information: https://www.terraform.io/docs/language/settings/index.html#specifying-a-required-terraform-version
terraform {
required_version = "0.14.0"
}
In terraform 1.x
and above, even though terraform guarantees no breaking
changes on its state, you should stick with specifying an exact version of
Terraform to use. Use the = 1.0.0
constraint to do this.
For more information: https://www.terraform.io/docs/language/settings/index.html#specifying-a-required-terraform-version
terraform {
required_version = "1.0.0"
}
If the layer uses providers directly, as opposed to only through modules, then you should also specify version contraints for those providers in the layer’s configuration.
In order to make the behaviour of layers completely deterministic, the version
should be fixed to a specific version. Use the = 1.0.0
constraint to do this.
Lock files in Terraform are generated upon running terraform init
. These
files, named .terraform.lock.hcl
, must be committed into the repo. They allow
for deterministic behaviour in dependency management across devices running our
code.
These files only appear in directories in which you run terraform init
,
therefore there are none in modules.
First of all, you have to review the Terraform Changelog to identify changes that may affect the behavior of your code. As a recent example, the optional
function was experimental, and passed standard in 1.3. It causes codes to be remanied to remove the declaration of an experimental feature, in the layer and used submodules.
You will have to change the version of your layer during this, that have to be done in your layer only (modules shouldn’t have fixed versions). Change also your Terraform CLI version, you can use tools such as tfswitch
.
Perform a plan is okay, but don’t apply during the phase of adaptation. You don’t want to create any drift by upgrading your Terraform version. As the version is stored in the state file, under terraform_version
, experimenting an upgrade and applying may lead to undesired conflicts of versions due to the state. Note: this field is automaticly updated when applying with a new Terraform version, even if Terraform output just displays that the infrastructure matches the configuration.
Finally, if your Terraform plan doesn’t see any differences with the actual code, you can commit your code, then applying when merged on the principal branch. This way is impactless for your coworkers or a CI, because the first who will apply the latest code will perform the migration in the state.
In a module, you can allow more flexibility with regards to Terraform’s
minor and/or patch versions. For example, the ~> 1.0
constraint will allow
all 1.x.x versions of Terraform, while the ~> 1.0.0
constraint will allow
all 1.0.x versions.
Modules that use features added in a specific minor version (eg: moved blocks added in 1.1) should require that version as a minimum.
For more information: https://www.terraform.io/docs/language/settings/index.html#specifying-a-required-terraform-version
terraform {
required_version = "~> 1.0"
}
You can indicate that the module requires that certain providers are configured by the caller. Again; use the ”~> 1.0” constraint to allow all minor and/or patch versions.
For more information: https://www.terraform.io/docs/language/providers/requirements.html
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.12"
}
google-beta = {
source = "hashicorp/google-beta"
version = "~> 4.12"
}
mongodbatlas = {
source = "mongodb/mongodbatlas"
version = "~> 1.3"
}
}
}
When using a remote module (eg. from a Terraform registry or from a remote git repository), it should be pinned to a specific version.
This is to ensure that the module’s behaviour is deterministic because it won’t be upgraded unexpectedly.
For example, with a module from the Terraform registry:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2" # <- 🟢
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.1" # <- 🔴 - may be upgraded unexpectedly
}
Another example from a remote git repository:
module "vpc" {
source = "git::https://github.com/padok-team/terraform-aws-network.git?ref=v0.2.0" # <- 🟢
}
module "vpc" {
source = "git::https://github.com/padok-team/terraform-aws-network.git" # <- 🔴 - may be upgraded unexpectedly
}
TODO