Warning
This repository is an experiment for generative AI coding tools. It may contain bugs, incomplete features, or other issues. Review every change before applying it to production.
tf-version-bump updates Terraform module versions, Terraform required_version constraints, and provider versions across files selected by a glob pattern. It parses and writes HCL with HashiCorp's hclwrite package instead of editing Terraform as plain text.
- Every module whose literal
sourcematches a requested source required_versionin existingterraformblocks- Provider versions in
required_providersblocks - Any combination of those updates from one YAML config file
Changed files retain their comments and HCL structure, but are formatted by hclwrite; do not expect byte-for-byte preservation of whitespace. Original file permissions are retained.
Go 1.25 or later is required:
go install github.com/yesdevnull/tf-version-bump@latestGo installs the command into GOBIN, or into GOPATH/bin when GOBIN is unset. Ensure that directory is on your PATH.
Pre-built archives and Linux packages are published on the GitHub Releases page. Releases may be marked as pre-releases, so choose the tag deliberately. Release assets include checksums alongside builds for Linux, macOS, and Windows on amd64 and arm64. Releases that include SLSA provenance publish a matching .intoto.jsonl asset.
See Release process and verification for artefact names and verification commands.
git clone https://github.com/yesdevnull/tf-version-bump.git
cd tf-version-bump
go build -o tf-version-bump .
./tf-version-bump -helpQuote glob patterns so your shell passes them to tf-version-bump unchanged.
tf-version-bump \
-pattern "**/*.tf" \
-module "terraform-aws-modules/vpc/aws" \
-to "5.0.0"Module sources are compared as exact strings. All eligible module blocks with a matching source are updated, no matter what their block labels are. Eligibility depends on the current-version, module-name, and missing-version controls described below.
tf-version-bump -pattern "**/*.tf" -terraform-version ">= 1.9"This sets required_version in every existing top-level terraform block in the matching files. It does not create a missing terraform block.
tf-version-bump -pattern "**/*.tf" -provider aws -to "~> 6.0"Only the named provider is changed; other providers and required_version are left alone.
Create versions.yml:
# yaml-language-server: $schema=https://raw.githubusercontent.com/yesdevnull/tf-version-bump/main/schema/config-schema.json
terraform_version: ">= 1.9"
providers:
- name: "aws"
version: "~> 6.0"
modules:
- source: "terraform-aws-modules/vpc/aws"
version: "5.0.0"
- source: "terraform-aws-modules/s3-bucket/aws"
version: "4.0.0"
from:
- "3.0.0"
- "~> 3.0"Then apply it:
tf-version-bump -pattern "**/*.tf" -config versions.ymlValidate the config structure without selecting or changing Terraform files:
tf-version-bump -validate-config versions.ymlConfig mode is exclusive with -module, -provider, -terraform-version, -to, and the module-filter flags. It can still be combined with global behaviour flags such as -dry-run, -check, -force-add, -verbose, -branch, -output, -report-file, and -audit-file, subject to the check-mode and audit restrictions below.
The command writes files in place. Start with a clean version-control worktree, preview the operation, then inspect the real diff:
tf-version-bump \
-pattern "**/*.tf" \
-module "terraform-aws-modules/vpc/aws" \
-to "5.0.0" \
-dry-runRemove -dry-run to write the files, then review them:
git diff -- '*.tf'
terraform fmt -check -recursive
terraform validatetf-version-bump checks HCL syntax but cannot determine whether a new version is compatible with your Terraform configuration. Use your normal validation and planning workflow before deployment.
For CI, use -check instead of -dry-run. It writes nothing and exits 0 when no eligible version value would change, 2 when updates are required, and 1 on an error. Ignored modules, unmatched targets, and matching modules skipped for a missing version can still return 0. Check mode cannot be combined with -dry-run or -report-file.
Automation can pass -report-file update-report.json in write mode to receive exact updated Terraform, module, and provider block counts as JSON. A run that changed files and then failed removes any report already at that path rather than leaving an earlier run's counts to be read as this one's. See the usage reference for the report contract and the examples cookbook for a complete workflow.
To compare files with a config without changing them, pass -audit-file audit.json in config mode instead. The audit lists every configured value's current and expected version; the usage reference describes it.
Repeat -from to update only modules whose current version string equals one of the supplied values:
tf-version-bump \
-pattern "**/*.tf" \
-module "terraform-aws-modules/vpc/aws" \
-to "5.0.0" \
-from "4.0.0" \
-from "~> 4.0"These are exact string comparisons, not semantic-version or Terraform-constraint evaluation. For example, -from "~> 4.0" matches the literal constraint ~> 4.0; it does not match 4.3.0.
Use repeatable -ignore-version flags to exclude exact current-version strings. Exclusions take precedence over -from.
-ignore-modules accepts comma-separated block labels and * wildcards:
tf-version-bump \
-pattern "**/*.tf" \
-module "terraform-aws-modules/vpc/aws" \
-to "5.0.0" \
-ignore-modules "legacy-vpc,test-*,*-deprecated"A pattern can be scoped to particular branches by prefixing it with a branch pattern. The final / separates the branch pattern from the module pattern, because Terraform module names cannot contain /:
tf-version-bump \
-pattern "**/*.tf" \
-module "terraform-aws-modules/vpc/aws" \
-to "5.0.0" \
-ignore-modules "legacy-vpc,state/staging/example-thing/shared-vpc" \
-branch "state/staging/example-thing"An unscoped pattern applies to every branch. A scoped pattern requires -branch; the command never reads the branch from Git, and expects the short name that git branch --show-current prints. See Branch-scoped module-name filters.
Matching registry modules without a version attribute are skipped with a warning by default. Use -force-add to add the attribute:
tf-version-bump \
-pattern "**/*.tf" \
-module "terraform-aws-modules/vpc/aws" \
-to "5.0.0" \
-force-addLocal and non-registry remote module sources are always skipped when their version attribute is missing, including with -force-add. Terraform supports version only for registry modules; Git and other remote sources select revisions through their source address.
*matches within one path segment.**spans zero or more directories, so**/*.tfalso matches a root-levelmain.tf.- Wildcard traversal skips dot-directories such as
.terraformand.git. - A dot-directory named explicitly, such as
.terraform/**/*.tf, can still match. - Directory symlinks are not followed; results are processed in lexicographical order.
- Brace alternation such as
{dev,prod}and character classes such as[0-9]are supported.
See Usage reference for the complete matching behaviour.
| Guide | Contents |
|---|---|
| Usage reference | Every CLI flag, update semantics, glob behaviour, output, and limitations |
| Configuration | Complete YAML format, filters, precedence, and examples |
| Advanced usage | Automating updates across Git branches, locally or with the GitHub Actions example |
| Examples | YAML samples, Terraform fixtures, runnable scenarios, and automation scripts |
| Release process | Building, publishing, and verifying release artefacts |
Run tf-version-bump -help for the command's built-in flag reference and tf-version-bump -version for build metadata.
go test -v -race -coverprofile=coverage.out -covermode=atomic ./...
golangci-lint run --timeout=5mSee CLAUDE.md and AGENTS.md for repository architecture and contributor guidance.
This project is available under the MIT Licence.