Modules are the mechanism for splitting Terraform configuration into small units and exposing them as reusable interfaces. The larger the team, the more module boundaries and version management dictate the stability of the result.
The Associate exam frequently covers the syntax of the module block, the kinds of source, the meaning of version, the availability of count/for_each/depends_on, and how Provider inheritance and aliases work. This article focuses on the points that are easy to get wrong, paired with real-world conventions.
A Module bundles inputs (variables), processing (resource definitions), and outputs together, hiding the internal implementation from the caller. This eliminates repetition of the same configuration and keeps the blast radius of changes small.
The keys to abstraction are a clear interface and a version-based contract. Be strict about variable types, defaults, and validation; keep outputs minimal; and adopt meaningful semantic versioning.
The relationship between Root and child Modules (inputs/outputs and Provider inheritance)
A module block specifies its origin in source and passes the required inputs. From Terraform 0.13 onward, count/for_each/depends_on can also be applied to modules. Note that the version argument is only valid for modules sourced from the Terraform Registry; it is ignored for Git and local paths.
For reproducible runs, pin version with semantic versioning on the Registry, and pin ref (tag or hash) on Git. Local paths are convenient during development, but using the Registry or Git when distributing across a team makes dependencies explicit.
Concrete module call examples (Registry / Git / local, with for_each and depends_on)
# From the Registry (version is honored)
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # Pin with semantic versioning
name = var.project
cidr = var.vpc_cidr
enable_dns_hostnames = true
}
# From Git (version is ignored; pin via ref)
module "webapp" {
source = "github.com/acme/terraform-webapp?ref=v1.2.3"
name = "webapp"
}
# Local path (development / unit testing)
module "sg" {
source = "./modules/sg"
name = "default-sg"
}
# Applying for_each and depends_on to a module (0.13+)
module "sg_by_tier" {
source = "./modules/sg"
for_each = toset(["app", "db"])
name = each.key
depends_on = [module.vpc] # Wait for the VPC to be created
}For inputs (variables), make types, defaults, and validation explicit to narrow the range of accepted values. When passing structures (object types) in particular, separate required and optional fields with future compatibility in mind.
Keep outputs to the truly necessary minimum. Mark secrets with sensitive = true to prevent leakage through outputs. Use locals to organize internal naming and calculations, and keep them out of the external contract.
Provider configuration normally lives in the Root module, and child modules inherit from it. When a single provider has multiple configurations (aliases), pass them explicitly through the providers map on the module block.
On the child module side, declare the providers you use with required_providers, but as a rule do not include concrete provider blocks. This keeps control with the caller and lets you switch environment differences (regions/accounts) cleanly.
The choice of source directly affects reproducibility, review, and ease of distribution. The Registry is the easiest to handle because it supports pinning by version. Git is flexible with ref-based pinning, but branch references undermine reproducibility. Treat local as a development aid and avoid it for production distribution.
| Source | Example source notation | How to pin the version | Reproducibility / caching |
|---|---|---|---|
| Terraform Registry | terraform-aws-modules/vpc/aws | version = "~> 5.0" (valid on the module block) | High (supports proxies and mirrors) |
| Git (GitHub/GitLab, etc.) | github.com/acme/app?ref=v1.2.3 | Pin ref to a tag/hash (version is ignored) | Medium to high (depends on how ref is pinned) |
| Local path / archive | ./modules/network or ./pkg.zip//modules/vpc | Cannot be pinned (it is just the caller's own files) | Low (heavily affected by execution environment differences) |
The exam tends to target the relationship between a module's source and version, the meta-arguments usable on a module block, and how Provider inheritance and alias mapping work. In practice, semantic versioning and strict input/output contracts determine quality.
Neglecting dependency reproducibility (pinning version/ref) produces diffs on every init and erodes the trustworthiness of the plan. Avoid branch references and implicit provider references.
Associate
問題 1
Which of the following is a case where the version argument on a module block is honored?
正解: A
The version argument only applies to modules fetched from the Terraform Registry. For non-Registry sources such as Git/HTTP/S3/local, version is ignored: Git needs to be pinned with ref, and HTTP/S3 needs to be pinned through the URL.
When should I extract configuration into a Module?
When you expect to repeat the same configuration two or three times or more, or when you want to enforce guardrails (tags, encryption, logging). Start small, make the inputs (variables) and outputs explicit, and only then roll it out across the organization to reduce failure risk.
How do I pin and upgrade Module versions?
On the Terraform Registry, pin with a range constraint such as ~> on the module block's version argument, and bump with terraform init -upgrade. For Git, pin ref to a tag or commit hash. Receive breaking changes through a major version bump, review the plan diff in a staging environment, and only then apply to production.
Is it OK to configure providers inside a child Module?
As a rule, avoid it. A child Module should only declare the providers it needs via required_providers, while configuration (credentials, region, and so on) belongs in the Root, with aliases passed explicitly through the providers map when needed. This makes environment switching and auditing far easier.
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...