Terraform Modules · Topic 2 of 2
Using Modules and Managing Versions
Terraform Associate 004: calling modules with inputs, count and for_each, standard module structure, version constraints and upgrading with terraform init.
- 5 min
- A module block needs
source; other arguments set the child's input variables. - Modules support
count,for_each,depends_onandproviders. - Read outputs as
module.NAME.OUTPUT, ormodule.NAME["key"].OUTPUTfor for_each instances.
What it is
Using a module means calling it from a module block, passing it input values and reading its outputs. A module lets you package a set of resources once and reuse it across projects and environments.
Managing module versions means pinning the module code your configuration accepts, so an upstream change cannot alter your infrastructure without your approval.
How it works
Calling a module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "main"
cidr = "10.0.0.0/16"
}
source and version are read by Terraform itself; name and cidr set the module's input variables. Each output of the module is available as module.vpc.OUTPUT_NAME.
Module blocks also accept the meta-arguments count, for_each, depends_on and providers, listed with the two Terraform arguments below.
| Argument | Use in a module block |
|---|---|
| source | Where the module code comes from (required) |
| version | Accepted versions, registry modules only |
| count / for_each | Create several instances of the module |
| depends_on | Explicit dependency for the whole module |
| providers | Pass specific provider configurations to the child |
Several instances with for_each
module "bucket" {
source = "./modules/bucket"
for_each = toset(["logs", "assets"])
name = each.key
}
Each instance has its own address, such as module.bucket["logs"], and outputs are read as module.bucket["logs"].arn.
Standard module structure
HashiCorp recommends a standard layout for reusable modules.
| File or folder | Contents |
|---|---|
main.tf | The main resources |
variables.tf | Input variable declarations |
outputs.tf | Output declarations |
README.md | What the module does and how to use it |
LICENSE | License for a published module |
modules/ | Nested child modules |
examples/ | Example calls of the module |
Terraform does not require these file names; it reads every .tf file in the directory. The layout simply makes modules easier to read and lets the registry generate documentation. For composition, HashiCorp advises a flat module tree, where the root module connects small modules by passing one module's outputs into another's inputs.
Version constraints
The version argument takes a constraint string with these operators.
| Constraint | Accepts |
|---|---|
= 5.1.0 or 5.1.0 | Exactly 5.1.0 |
!= 5.1.0 | Any version except 5.1.0 |
>= 5.0.0 | 5.0.0 or newer |
>= 5.0, < 6.0 | Any 5.x version |
~> 5.1 | 5.1 or newer within 5.x |
~> 5.1.0 | 5.1.0 or newer within 5.1.x |
The ~> operator, the pessimistic constraint, allows only the rightmost version part to increase. HashiCorp advises pinning modules from outside your organization to avoid unexpected changes.
version works only with registries
The version argument applies only to modules from the public Terraform Registry or a private registry, such as the HCP Terraform private registry. For Git sources you pin with ?ref=, and local path modules have no version at all, because they are read in place.
Upgrading modules
terraform init installs the newest version that matches the constraint when no copy exists yet. Afterwards it keeps the installed version until you ask for an upgrade.
terraform init -upgrade
-upgrade checks for the newest versions of modules and providers that still match their constraints. The dependency lock file .terraform.lock.hcl records provider versions only; it does not lock module versions, so the constraint in the module block is your module pin.
How the exam asks about this
- "Which constraint lets a module take 2.3.x patches but not 2.4.0?"
~> 2.3.0. - "Can the version argument pin a module sourced from a Git repository?" No; use
refin the source URL. - "True or false: the dependency lock file pins module versions." False; it records provider versions only.
What to remember for the exam
- A module block needs
source; other arguments set the child's input variables. - Modules support
count,for_each,depends_onandproviders. - Read outputs as
module.NAME.OUTPUT, ormodule.NAME["key"].OUTPUTfor for_each instances. versionworks only for public and private registry modules.~> 1.2allows 1.2 and later 1.x versions;~> 1.2.0allows only 1.2.x.terraform init -upgrademoves modules and providers to the newest versions their constraints allow.
Changing a module's version constraint does nothing until you run terraform init again. If the installed version still matches the new constraint, you need terraform init -upgrade to pick up a newer release.
Common mistakes
Pinning a Git module with version. Terraform rejects a version argument on a non-registry source with an error. Put the tag in the source with ?ref=v1.2.0.
Trusting the lock file for modules. .terraform.lock.hcl only records providers. Without a version constraint, a fresh terraform init can pull a newer registry module than your teammate has.
Misreading the pessimistic operator. ~> 5.0 allows any 5.x version, not only 5.0.x. Write ~> 5.0.0 to stay on 5.0 patches.
Check yourself
Which module sources accept the version argument?
Only modules from the public Terraform Registry or a private registry; Git sources use
ref, and local paths have no versions.What does
~> 3.1allow?Version 3.1 and any later 3.x release, but not 4.0 or later.
Which command updates an installed module to the newest version its constraint allows?
terraform init -upgrade, which also upgrades providers within their constraints.
Topic 2 of 2 in Terraform Modules
Test yourself on Terraform Modules
The full HashiCorp Terraform Associate 004 question bank has 600 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by HashiCorp. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.