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:
- Read the provider changelog and upgrade guide for every version between current and target.
- Create a branch, change the constraint, and run
terraform init -upgradein a non-production stack. - Run
terraform plan. A pure version bump should produce no changes. Anything else needs an explanation. - Look for
must be replacedmarkers, especially on databases, disks, clusters and anything with data. - Fix deprecated or renamed arguments in code, repeat until the plan is clean.
- Apply to the lowest environment, soak for a few days, then promote through staging to production using the normal pipeline.
- 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.