Terraform

Terraform Module Source Types Explained: git / registry / local

2026-04-19
NicheeLab Editorial Team

ハブ記事: Terraform Modules: Complete Guide

A hub article that covers the full picture of Terraform modules — design, distribution, and operations.

The source argument on a module block is the single source of truth for where a module comes from. Get the syntax wrong and initialization, updates, and pinning all break down.

This article focuses on the three forms — registry / git / local — and zeroes in on real-world pitfalls and the points that show up most often on the Associate exam.

Module Source Basics and Resolution Flow

The source argument on a module block is the address Terraform uses to locate the module. The main forms are registry (public/private), git, and local (relative/absolute paths). terraform init resolves the source and fetches the module into .terraform/modules (local sources are referenced in place, not fetched).

The version argument — which lets you constrain a module's version — is only accepted by registry sources. With git you pin via ?ref= (tag, branch, or commit). With local you rely on repository management and release procedures. On the Associate exam, it's important to distinguish exactly which form lets you pin what.

  • init resolves and fetches modules; always run it before plan/apply to refresh dependencies
  • Only registry sources accept the version argument (SemVer constraints); git and local do not
  • .terraform.lock.hcl primarily locks providers — module update control comes from the source spec and how you run init
  • Common traps for git: //subdir and ?ref=. For local: relative paths are resolved from the working directory

Module source resolution flow (conceptual diagram)

root module (module blocks)source resolverRegistry (registry.*)Download to cache (.terraform/modules)Git remoteClone to cache (.terraform/modules)Local pathUse as-is (no copy)root module → source resolver → registry / git / local → plan / apply

Registry-Based Sources

Fetched from the public Terraform Registry or from a Terraform Cloud/Enterprise private registry. The syntax is <optional-hostname>/<namespace>/<module-name>/<provider>. If the hostname is omitted, it defaults to registry.terraform.io. For private registries, prefix the hostname (e.g. app.terraform.io).

The version argument accepts SemVer constraints (~>, >=, =, etc.). terraform init -upgrade updates to the latest version within the constraint, while init without -upgrade respects the existing cache. Authentication is typically handled by token management via terraform login.

  • Public syntax example: source = "hashicorp/consul/aws"
  • Private syntax example: source = "app.terraform.io/acme/network/aws"
  • The version argument is registry-only (frequently tested)
  • Combine init/upgrade behavior with version constraints to manage pinning in practice

Git-Based Sources

Fetched from a Git repository. Prefix the source with git:: and use either HTTPS or SSH. To target a subdirectory inside the repo, append //path/to/module. To pin the version, use ?ref=tag|branch|sha.

For authentication, prepare an HTTPS personal access token, or an SSH deploy key / SSH agent. In CI, the practical baseline is to inject credentials via environment variables or an auth agent — never hardcode them into the URL.

  • HTTPS: source = "git::https://github.com/acme/infra-mods.git//modules/vpc?ref=v1.2.0"
  • SSH: source = "git::ssh://[email protected]/acme/infra-mods.git//modules/vpc?ref=main"
  • The version argument is not supported. Pin via ?ref= (tag/branch/commit) instead
  • Without ref, you track the latest of the default branch, which destroys reproducibility (a classic exam trap)

Local Path Sources

Reference a module that exists in the same repository or on the local filesystem via a relative or absolute path. There is no fetch step — the module is referenced in place. Relative paths are resolved from the root module.

Changes are picked up immediately, but there is no version-pinning concept — reproducibility comes from branch workflows and release processes. Local sources don't scale to reuse across repositories; for that, promote to git or registry distribution.

  • Relative path example: source = "./modules/network"
  • Parent directory reference: source = "../shared/vpc"
  • init does not build a cache, so module changes apply immediately (but always verify diffs with plan)
  • For distribution and reuse, promote to git or registry-based modules

Comparing git / registry / local

Choose based on two criteria: reproducibility guarantees and ease of distribution. Registry is best for stable distribution across teams and organizations; git fits the development phase and internal sharing; local fits fast iteration inside a single repository.

  • For maximum reproducibility: registry (version constraints + init workflow)
  • As a middle ground: git (pin with ?ref=, mind authentication management)
  • For local optimization and prototyping: local (promote to git/registry later)
Source TypeSyntax ExampleVersion PinningSubdirectory Specification
Registry (public/private)hashicorp/consul/aws, app.terraform.io/acme/network/awsSemVer constraints such as version = "~> 1.2"Not needed (modules are distributed as units)
Gitgit::https://github.com/acme/infra.git//modules/vpc?ref=v1.2.0?ref= tag/branch/commitAppend //path/to/module
Local Path./modules/network, ../shared/vpcNot supported (rely on repository workflow)Reach via the path itself (no special notation)

Associate Exam Key Points and Samples

The three frequently tested pillars are: "version is registry-only", "for git, // means subdirectory and ?ref= means pinning", and "local sources are referenced directly without being fetched". Also nail down the meaning of init vs init -upgrade, the risk of omitting ref, and the hostname prefix for private registries.

Below are minimal examples of all three forms. Compare the syntax and pinning mechanism for each.

  • registry: pin with version constraints and manage updates via upgrade
  • git: combine //subdir with ?ref=. The version argument is not available
  • local: path resolution is based on the root module's working directory

Minimal example of the three module source forms

module "net_registry" {
  source  = "app.terraform.io/acme/network/aws"
  version = "~> 1.2"
  cidr    = "10.0.0.0/16"
}

module "net_git" {
  source = "git::https://github.com/acme/infra-mods.git//modules/network?ref=v1.2.3"
  cidr   = "10.1.0.0/16"
}

module "net_local" {
  source = "./modules/network"
  cidr   = "10.2.0.0/16"
}

Check Your Understanding

Associate

問題 1

Which is the correct syntax to reference the modules/network subdirectory of a Git repository, pinned to tag v1.2.3?

  1. source = "git::https://github.com/acme/infra-mods.git//modules/network?ref=v1.2.3"
  2. source = "git::https://github.com/acme/infra-mods.git//modules/network#v1.2.3"
  3. Combine source = "git::https://github.com/acme/infra-mods.git//modules/network" with version = "1.2.3"
  4. source = "https://github.com/acme/infra-mods.git//modules/network?ref=v1.2.3"

正解: A

Git sources require the git:: prefix, use //path for subdirectories, and pin via ?ref=tag|branch|sha. The version argument is registry-only, and #tag is not supported.

Frequently Asked Questions

Can I use the version argument with git or local sources?

No. The version argument is registry-only. With git you pin via ?ref= (tag, branch, or commit), and with local you rely on repository workflow and release procedures for reproducibility.

How do I reference a module from a Terraform Cloud private registry?

Include the hostname in source, e.g. app.terraform.io/<organization>/<name>/<provider>. Run terraform login first to acquire a CLI token used for authentication during init.

How do I safely access private Git modules from CI?

Inject read-only SSH deploy keys or HTTPS personal access tokens via environment variables or an auth agent. The practical rules: never hardcode credentials in the URL, and always pin with ref to guarantee reproducibility.

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

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.


Related articles
Terraform

HCL Syntax: Terraform's Configuration Language (2026)

HCL2 fundamentals for Terraform — blocks, attributes, expres...

Terraform

Terraform Authoring & Operations Pro: Complete Guide (2026)

Tactics for the Terraform Pro exam — module authoring, works...

Terraform

Terraform Providers: Plugin Management Fundamentals (2026)

Provider mechanics — required_providers, versions, mirrors, ...

Terraform

Terraform Resource Blocks: Declarative Infra Units (2026)

Resource block fundamentals — addresses, references, common ...

Terraform

Terraform Data Sources: Read-Only External Data (2026)

Data source basics — declaration, refresh behavior, dependen...

Browse all Terraform articles (102)
© 2026 NicheeLab All rights reserved.