Search code examples
terraformterraform-provider-azureazure-rm

Terraform, how to centralize providers versioning


We use terraform for Azure PAAS resources creation and it runs as a separate pipeline steps for each component. For instance - first step data component plan and apply, second step web component plan and apply and so on. So the code is arranged into multiple components and each of those will have it's own definition for provider azurerm block. Inside the block we want to pin the provider version and we want to control it in centralized manner. So currently we came up with the following approach.

provider "azurerm" {
  version                    = "=${ps.AzureRmVersion}"
  skip_provider_registration = "true"
  features {}
}

When the release process runs there is a powershell functionality that replaces the ps.AzureRmVerison marker with the version. My question is if there is another way to control the provider version without involving third party such as powerhsell to control it.


Solution

  • The version argument in provider blocks is a legacy pattern from older versions of Terraform for specifying version constraints (a set of versions that this module is compatible with) rather than version selections (a single selected version that you want to use).

    Since you want to centrally control which exact version is selected I think the best approach would be to have your automation script generate a Dependency Lock File containing the versions you want to prescribe.

    Normally Terraform manages this lock file itself as you install and upgrade providers, but in that case each configuration will have its own set of locks and may therefore differ from one another. Since you want to impose central policy, you can instead use Terraform CLI with a simple configuration that only contains provider requirements declarations for the providers you want to use:

    terraform {
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "1.0.0"
        }
      }
    }
    

    In that directory you can run terraform providers lock to cause Terraform to select that particular version from the registry and generate a .terraform.lock.hcl file recording the checksums for all of the platforms you specified:

    terraform providers lock -platform=windows_amd64 -platform=linux_amd64
    

    You can then save that .terraform.lock.hcl file to your central location and configure your automation to copy that file into the working directory each time (overwriting any file that might already be there) before running terraform init. Terraform will then select whatever package the lock file recorded, and make sure that it matches the checksums previously recorded.

    Your individual Terraform configurations may optionally contain their own non-exact version constraints specifying which Terraform versions they are compatible with, which will then cause Terraform to report an error if the centrally-selected version recorded in your shared lock file is not compatible with one of your configurations.


    Note that the lock file only constrains providers that are already recorded in it. If one of your configurations requires a different provider that's not already in the lock file then by default terraform init will select the newest compatible version of that provider and overwrite the lock file to include it.

    If you want to prevent that and require all new providers to be added to the centrally-maintained lock file, you can add an additional option to terraform init to tell Terraform to fail if the action it's taking would require changes to the locked providers:

    terraform init -lockfile=readonly
    

    To add a new provider with this usage pattern, you'd need to return to the requirements-only configuration I described earlier, add the new provider to it, re-run the same terraform providers lock command to regenerate it, and then update your "master" lock file to that new version of the file.