ハブ記事: Complete Guide to Terraform Modules →
Hub article covering the full picture of Terraform modules — design, distribution, and operations
Terraform modules specify their origin with source, and version constraints can only be applied when the source is a registry. For URL sources such as Git, version is invalid — you pin tags or commits via ref instead.
This article walks through, end to end, the minimal principles for achieving safe upgrades in practice and the points most frequently tested on certification exams.
source is required in a module block. Only registry-backed modules can use the version argument for version constraints. For non-registry sources such as Git or local paths, version is not allowed and will error during initialization.
For URL-based sources like Git, you pin by specifying a tag name, branch, or commit SHA via the ref query. If reproducibility is the priority, pinning by tag or commit SHA is the standard.
Module resolution flow
module "x" {
source + version? terraform init
| | |
| | Dependency resolution engine
| | v
+--> Source address ----> Origin detection
| |
| +--> Registry: resolve version constraint and pick latest match
| |
| +--> Git/HTTP etc.: if ref is set, fetch that tag/commit
v (otherwise default branch)
Expanded into .terraform/modules
Basic example via the registry
terraform {
required_version = ">= 1.3"
}
module "consul" {
source = "hashicorp/consul/aws" # Registry address
version = "~> 0.10" # Pin to 0.10.x (excludes future 0.11.x)
}
The difference between registry and Git specifications is commonly confused both on exams and in practice. version can only be used with the registry. For Git, you specify a tag or commit via ref=. Local paths and HTTP archives also do not support version.
Reproducibility is the top priority. In production, avoid pinning to branches (main or master) — pin to a tag or commit SHA instead.
| Source type | How to specify the version | Example | Caveats |
|---|---|---|---|
| Registry (public/private) | Constraint expression on version | source = "app.terraform.io/acme/network/aws" version = "~> 2.4" | init -upgrade resolves the latest allowed version. Semantic Versioning |
| Git (tag) | ref=<tag> on source | source = "git::https://github.com/org/terraform-modules.git//vpc?ref=v1.2.3" | Range constraints are not supported. Disciplined tag management is required |
| Git (commit SHA) | ref=<SHA> on source | source = "git::ssh://[email protected]/org/mods.git//eks?ref=9f2c0ab" | Full pinning gives high reproducibility but is hard for humans to read |
| Local path | None | source = "../modules/alb" | Local changes apply immediately. Not recommended for CI/CD or multi-environment use |
| HTTP archive | None | source = "https://example.com/mods/vpc-1.2.3.zip" | Manage checksums to guard against tampering |
Example of Git tag pinning with subdirectory specification
module "vpc" {
source = "git::https://github.com/org/terraform-modules.git//network/vpc?ref=v1.2.3"
}
module "eks" {
source = "git::ssh://[email protected]/org/iac.git//modules/eks?ref=9f2c0abd1"
}
In the Terraform Cloud/Enterprise private module registry, use app.terraform.io/<org>/<name>/<provider> as the source and constrain via version. Register SemVer release tags from your VCS, and consumers receive them through version constraints.
Authenticate the first time with terraform login; modules are then resolved at init. To upgrade, keep the version constraint in code and run terraform init -upgrade to move to the latest version within the allowed range.
Example: Using a private registry
module "network" {
source = "app.terraform.io/acme/network/aws"
version = "~> 2.4" # Allow 2.4.x
}
# First-time authentication: terraform login
For registry sources, use SemVer-compliant constraints. The ~> operator follows the convention of "pin the two leftmost components", so ~> 1.2 means >= 1.2.0 and < 1.3.0. You can design per-environment policies — e.g., production allows patches only, while staging allows minor updates.
Git sources do not support ranges; you upgrade explicitly by changing the tag or commit. If you want automatic upgrades, choose registry publication and version constraints.
Concrete constraint examples
module "web" {
source = "app.terraform.io/acme/web/aws"
version = "~> 1.8" # 1.8.x
}
module "batch" {
source = "app.terraform.io/acme/batch/aws"
version = ">= 2.3, < 3.0" # Compound condition
}
# Git does not support ranges. Pin with a tag/commit.
module "ops" {
source = "git::https://github.com/acme/ops-mods.git//alerts?ref=v0.9.5"
}
For registry-backed modules, init reuses the previously downloaded version if it exists. To move to a newer version within the allowed range, run terraform init -upgrade. Git sources are not updated unless you change the ref in code.
The dependency lock file (.terraform.lock.hcl) is for providers only and does not apply to modules. Reproducibility for modules is ensured through version constraints (or tag/commit pinning for Git) and by recreating the .terraform directory.
Example CI job
# Bash example
set -euo pipefail
rm -rf .terraform/ # Clean reproduction
terraform init -upgrade
terraform validate
terraform plan -out=tfplan
Specifying version on a Git source is a common mistake that frequently appears on exams. version is registry-only; Git uses ref. Also avoid pinning to mutable branches like main in production.
Don't confuse provider version pinning with module version pinning. required_providers is for providers only; module version is for registry modules only.
Bad and good examples
# Bad: version on a Git source
module "bad" {
source = "git::https://github.com/org/mods.git//vpc"
version = "~> 1.2" # ← Error: version is not valid here
}
# Good: Pin Git by tag
module "good_git" {
source = "git::https://github.com/org/mods.git//vpc?ref=v1.2.3"
}
# Good: Range constraint on the registry
module "good_registry" {
source = "app.terraform.io/acme/vpc/aws"
version = "~> 1.2"
}
Associate / Pro
問題 1
You want to use a module hosted on GitHub and ensure reproducibility in production. Which specification is recommended?
正解: B
version is for registry modules only and is not valid on Git sources. To ensure reproducibility, pin a tag (or commit SHA) via ref. Local paths are poor for sharing and reproducibility, and pinning to main is too volatile for production.
Is there a lock file for modules like the one used for providers?
No. .terraform.lock.hcl is a lock file for providers only. For modules, reproducibility is ensured via version constraints (registry) or ref pinning (Git).
Is it OK to prefix tags with v in the registry?
The common SemVer v-prefix (v1.2.3) is supported. SemVer-compliant tags are also recommended for the Terraform Cloud/Enterprise private registry.
Does init alone automatically upgrade to the latest allowed version?
No. If a module is already downloaded, the same version is reused. To upgrade to the latest version within the allowed range, run terraform init -upgrade. Git sources are not updated unless you rewrite the ref.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...