Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer

Terraform / IaC

Terraform Provider Version Management and Upgrade Runbooks

We pin Terraform and provider versions with constraints and a committed lock file, then upgrade on a planned cadence using a written runbook: read the release notes, test in a lower environment, review the plan for replacements, and roll out in stages.

Key takeaways

  • Unpinned providers mean a fresh init next month may plan different changes than today.
  • The .terraform.lock.hcl file should be committed so every engineer and pipeline uses identical provider builds.
  • Major provider upgrades can rename arguments and change defaults, so they need a plan review for unexpected replacements.
  • Upgrade often in small steps; skipping several majors makes the jump painful.
  • Protect stateful resources with prevent_destroy while upgrades are in flight.

The upgrade that proposes to replace your database

Providers are the part of Terraform that talks to AWS or Azure, and they change constantly. Cloud services add features, the provider adds arguments, old arguments get deprecated, and every few years a major release reshapes things. If your versions are not pinned, none of that is under your control. A pipeline that ran cleanly on Friday can show a surprising plan on Monday because a new provider release landed over the weekend.

Good Terraform provider version management turns this into a scheduled, boring chore. It is the maintenance half of our Terraform and infrastructure as code service, and the reason infrastructure code written today is still safe to change in two years.

What good looks like

  • The Terraform (or OpenTofu) version and every provider are constrained.
  • The lock file is committed and includes hashes for all platforms in use.
  • Upgrades happen on a cadence, in lower environments first.
  • Every upgrade has a plan review that looks for replacements and changed defaults.
  • A written runbook means anyone on the team can perform one.

How it works in practice

Constraints and the lock file

Two layers work together. The constraint says what is allowed:

terraform {
  required_version = "~> 1.9"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}

The lock file says what was actually selected and verifies it. Commit it, and make sure it carries hashes for each platform that runs Terraform:

terraform providers lock \
  -platform=linux_amd64 \
  -platform=darwin_arm64

Without multi-platform hashes, a laptop and CI can disagree and the pipeline fails on init. Check current major versions before copying the constraints above, as provider majors move on.

The upgrade runbook

Our runbook is deliberately short enough to follow under pressure:

  1. Read the provider changelog and upgrade guide for every version between current and target.
  2. Create a branch, change the constraint, and run terraform init -upgrade in a non-production stack.
  3. Run terraform plan. A pure version bump should produce no changes. Anything else needs an explanation.
  4. Look for must be replaced markers, especially on databases, disks, clusters and anything with data.
  5. Fix deprecated or renamed arguments in code, repeat until the plan is clean.
  6. Apply to the lowest environment, soak for a few days, then promote through staging to production using the normal pipeline.
  7. Record what changed and any gotchas in the runbook.

For stateful resources, a lifecycle { prevent_destroy = true } block turns an accidental replacement into a plan error rather than an outage.

Keeping the binary consistent

A pinned required_version is not enough if engineers install whatever version they like. Version managers such as tfenv, or tool version files in the repository, and a pinned image in CI, keep everyone aligned. Differences in the binary used are a frequent cause of “works on my machine” plan diffs.

Decisions and trade-offs

Tight pins or ranges. Exact pins maximise reproducibility but increase upgrade chores. Pessimistic ranges within a major version, backed by a committed lock file, give both reproducibility and gentle drift in patch versions. We recommend that default.

Upgrade frequently or rarely. Small, frequent upgrades are easier to review and diagnose. Annual big-bang upgrades accumulate breaking changes and fear. Quarterly is a common rhythm.

Automated upgrade pull requests. Renovate or Dependabot can raise a pull request when a new provider is released. That is excellent for visibility. Merging blindly is not: the plan must still be read.

Staying on an old provider. Sometimes a team is better off staying put, for example during a freeze or while an unrelated migration is underway. Fine, provided it is a decision with an end date, because old providers eventually lack support for new cloud features and security fixes.

Modules and providers together. A module that restricts the provider range can block your upgrade. Aligning constraints is part of module versioning.

Reading a plan after a version bump

The plan after a provider upgrade deserves a specific reading order. First, look for any resource marked for replacement, because that is where data is lost. Second, look for in-place updates on resources you did not touch: these are usually changed defaults or newly tracked attributes, and each should be explained before applying. Third, check for new warnings about deprecated arguments, which tell you what the next upgrade will break.

If an in-place update is only a harmless new attribute being recorded, the apply is safe but still worth doing in a lower environment first, so production sees a plan that has already been exercised. When a replacement appears, stop and decide deliberately: either change the code to avoid it, or schedule the replacement with backups and a maintenance window.

Upgrading Terraform itself

The Terraform or OpenTofu binary follows the same pattern. Bump required_version in a lower environment, run a plan, and be aware that once a newer version writes to state, older versions may refuse to read it. That makes the binary upgrade effectively one-way for each stack, which is a good reason to move every engineer and the pipeline image together.

Failure modes we avoid

  • No lock file in version control, so each init picks something new.
  • Lock file hashes for one platform only, breaking CI or laptops.
  • Upgrading providers and refactoring in one pull request, which makes any plan change impossible to attribute.
  • Applying a major upgrade straight to production without trying it in a lower environment.
  • Ignoring deprecation warnings until the removal arrives.

Delivery and handover

You get pinned constraints and lock files across your stacks, the upgrade runbook, automation that raises upgrade pull requests, and a calendar of upcoming support deadlines for the versions you use. We also run the first upgrade with your team, which is where the runbook gets its real-world corrections. Related work includes scanning rules, which we upgrade in step, covered in Terraform security scanning.

How to tell it is working

Plans are the same on Monday as on Friday. Upgrade pull requests are small and merge within days. Nobody is afraid of the next major release because the last one was uneventful. If your providers have not been touched in a year, contact us and we will assess the gap.

References

How we deliver it

  1. 01

    Inventory versions

    We record the Terraform version, every provider version and every module constraint currently in use across your stacks.

  2. 02

    Pin and lock

    required_version and required_providers constraints are set, and lock files are generated for each platform your team and CI use.

  3. 03

    Write the runbook

    A step-by-step document covers reading notes, upgrading lower environments first, reviewing plans and rolling back.

  4. 04

    Run the first upgrade together

    We upgrade a real stack alongside your engineers, so the process is proven on your code.

  5. 05

    Automate the nudges

    Renovate or Dependabot opens upgrade pull requests, which run plans in the normal pipeline.

  6. 06

    Set a cadence

    Upgrades are scheduled, for example quarterly for minors and planned projects for majors.

Tools and technologies

TerraformOpenTofuAWS providerAzureRM provider.terraform.lock.hclRenovateDependabottfenvtofuenvterraform providers lockterraform testtflint

Frequently asked questions

How should we pin Terraform provider versions?
Set a version constraint in required_providers, such as a pessimistic range within one major version, and commit the dependency lock file. The constraint states what is acceptable, and the lock file records the exact build used, so every run is reproducible across laptops and CI.
What is the .terraform.lock.hcl file?
It records the exact provider versions and checksums chosen by terraform init. Committing it ensures everyone gets the same provider binaries and detects tampering. Generate hashes for every platform you use, for example Linux for CI and macOS for laptops, with terraform providers lock.
How do we safely upgrade a major provider version?
Read the upgrade guide, change the constraint, run init with the upgrade flag in a non-production stack, and review the plan. Look especially for resources marked for replacement. Fix deprecated arguments, apply in lower environments first, then production in a quiet window.
How often should we upgrade providers?
Minor and patch versions can follow a regular cadence such as monthly or quarterly. Major versions deserve a planned slot. Falling several majors behind makes the eventual jump riskier, because you absorb all the breaking changes at once.
Terraform or OpenTofu: does this change provider management?
The process is the same. Both use required_providers and a lock file, though they pull providers from their own registries by default. We check provider availability and licence terms for your choice, and keep the Terraform or OpenTofu binary pinned like any other dependency.

Talk to an engineer about terraform provider version management

Book a free 30-minute call. We review your current setup and tell you honestly whether and how we can help.

Written by Saddique Khan, DevOps / SRE / Cloud Consultant Engineer. Last updated 2026-10-08.