Safeguard
Supply Chain•

Pinning Terraform modules: registry version or git ref?

How to pin Terraform module sources so an upstream change cannot slip into your infrastructure: registry versions, git tags and commit SHAs compared.

Safeguard Security Team
4 min read

Pin every Terraform module to a fixed registry version or a git ref, and prefer a full commit SHA when the source is a repository you do not control. Without a pin, Terraform uses the repository default branch, so upstream changes can reach your next plan unreviewed.

Why modules deserve attention

A module can create IAM roles, open networks and run code at plan time through its providers. Your root configuration trusts it completely. The provider lock file does not cover this, because it tracks only provider dependencies.

The two source types

Registry modules use a version argument with a version constraint. An exact version such as 5.1.2 fixes it. A range such as ~> 5.1 allows newer releases within the range.

Git modules do not use version. HashiCorp's documentation says Terraform clones the repository and uses the default branch unless you specify otherwise. You choose a revision with the ref query parameter, which accepts a branch, SHA-1 hash or tag. The documentation shows examples pinned to a tag and to a full SHA-1.

Comparing the options

Pin styleMoves silently?Notes
No ref (git)Yes, tracks default branchAvoid
Branch refYes, branch movesAvoid for anything outside your team
Tag refOnly if the tag is movedReadable, but tags can be re-pointed
Full commit SHANoImmutable, harder to read
Exact registry versionNo, per registry rulesEasy to review
Registry version rangeWithin the rangeUpgrades arrive on init

A tag is a reasonable choice for modules owned by your own organization where tag changes are controlled. For outside repositories, the SHA is the safest option, with a comment noting which release it matches.

Handling the checksum gap

HashiCorp's module sources page does not mention checksums or integrity checks for module sources. That means no lock file will warn you if a tag is re-pointed or a registry version is replaced. Pinning narrows the window, and review fills the rest:

  • Review the module diff when you bump a pin, not just the pin line.
  • Vendor or mirror modules you depend on heavily.
  • Prefer modules from publishers you can name, with active maintenance.

Variables in source

Input variables used in source or version must set const = true, because Terraform has to resolve the source during terraform init. This also means module versions cannot be changed casually through run time variables, which helps.

A workable standard

  1. Every module call has an explicit pin.
  2. External git modules pin to a SHA with a comment.
  3. Internal modules pin to a tag released through your own process.
  4. Bumps arrive as their own pull request with the module diff attached.
  5. A lint rule or policy fails any module block without a pin.

Safeguard's policies and gates can enforce rules like step five at pull request time, and its dependency scanning can list the modules and providers in a repository.

Quick check

Does a tag pin guarantee the code never changes?

No. A tag can be moved by whoever controls the repository. A full commit SHA cannot.

Can I use the version argument with a git source?

No. Per HashiCorp, git sources use the ref query parameter. The version argument is for registry modules.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.