Terraform Modules · Topic 1 of 2
Module Sources and Variable Scope
Terraform Associate 004: module sources from local paths to registry, Git, HTTP and S3, how init installs them, and how inputs and outputs scope values.
- 5 min
- Includes a diagram
- The root module is where you run Terraform; modules it calls are child modules.
- Local paths start with
./or../; registry sources useNAMESPACE/NAME/PROVIDER. - Git sources use
reffor a branch, tag or commit, and//for a subdirectory.
What it is
A module is a set of Terraform files in one directory. The directory where you run Terraform is the root module; any module it calls with a module block is a child module.
The source argument tells Terraform where a child module's code lives. Variable scope describes which values a module can see: each module only sees its own variables, locals and resources.
How it works
Source types
| Source | Example source value |
|---|---|
| Local path | "./modules/network" or "../shared/vpc" |
| Public Terraform Registry | "hashicorp/consul/aws" |
| Private registry | "app.terraform.io/example-org/vpc/aws" |
| GitHub | "github.com/example-org/terraform-aws-vpc" |
| Generic Git | "git::https://example.com/vpc.git" |
| Bitbucket | "bitbucket.org/example-org/terraform-aws-vpc" |
| HTTP URL | "https://example.com/vpc-module.zip" |
| S3 bucket | "s3::https://s3.amazonaws.com/example-bucket/vpc.zip" |
| GCS bucket | "gcs::https://www.googleapis.com/storage/v1/example-bucket/vpc.zip" |
A local path must start with ./ or ../. Without that prefix, Terraform reads the string as a registry address.
Registry addresses use the form NAMESPACE/NAME/PROVIDER. A private registry adds the hostname in front, as in HOSTNAME/NAMESPACE/NAME/PROVIDER.
Selecting a Git revision or subdirectory
For Git sources, the ref query parameter picks a branch, tag or commit. A double slash selects a subdirectory inside the repository or archive.
module "vpc" {
source = "git::https://example.com/infra.git//modules/vpc?ref=v1.2.0"
}
The version argument does not work with Git sources; ref does that job. Page 2 of this chapter covers version for registry modules.
How Terraform installs modules
terraform init installs every module that the configuration calls. Remote modules are downloaded into the .terraform/modules directory; local path modules are used in place and not copied.
terraform init
terraform get
terraform get downloads modules only, without the rest of initialization, and terraform get -update refreshes modules already installed. When you add a module block or change its source, run terraform init again before planning.
Variable scope
A child module cannot read the caller's variables, locals or resources, and the caller cannot reach inside the child. There are no global variables.
| Direction | Mechanism | Reference |
|---|---|---|
| Parent to child | Arguments in the module block set the child's input variables | var.NAME inside the child |
| Child to parent | Output blocks in the child | module.MODULE_NAME.OUTPUT_NAME in the parent |
| Child to its own code | Locals and resources | local.NAME, TYPE.NAME |
module "network" {
source = "./modules/network"
cidr_block = "10.0.0.0/16"
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = "t3.micro"
subnet_id = module.network.subnet_id
}
Here cidr_block must match a variable "cidr_block" in the child, and subnet_id must be declared as an output in the child. Any child variable without a default must be set in the module block.
Variable names do not leak
Two modules can each declare a variable called region without conflict, because each name exists only in its own module. A value reaches a child only when the caller passes it explicitly, for example region = var.region.
Providers are the main exception to strict isolation: by default, a child module inherits the default provider configurations of its parent. The providers argument in a module block can pass a different, aliased configuration. Reusable modules should not declare their own provider blocks, because a module that does cannot be called with count, for_each or depends_on.
How the exam asks about this
- "Terraform reports the module source modules/app as an invalid registry address." The local path needs a
./prefix:./modules/app. - "A child module needs the VPC ID that the root module creates. How does it receive it?" Pass it as an argument in the module block, matching an input variable in the child.
- "How does the root module use the subnet ID a child creates?" The child declares an output, read as
module.network.subnet_id.
What to remember for the exam
- The root module is where you run Terraform; modules it calls are child modules.
- Local paths start with
./or../; registry sources useNAMESPACE/NAME/PROVIDER. - Git sources use
reffor a branch, tag or commit, and//for a subdirectory. terraform init(orterraform get) installs remote modules into.terraform/modules.- Values enter a child only through input variables and leave only through outputs.
- Child modules inherit the parent's default provider configurations unless
providerssays otherwise.
A child module can never use var. to read a variable declared in the root module. If the child needs the value, declare a variable in the child and pass the value in the module block.
Common mistakes
Leaving out the ./ prefix. source = "modules/app" is treated as a registry address and fails. A local directory must be written as "./modules/app".
Forgetting to run init after a change. Adding a module or changing its source leaves the configuration uninstalled, so terraform plan fails with a message asking you to run terraform init.
Referring to a child's resource directly. module.network.aws_subnet.main.id is not valid in the parent. The child must expose the value as an output, which the parent reads as module.network.subnet_id.
Check yourself
Where does terraform init put downloaded remote modules?
In the
.terraform/modulesdirectory of the working directory. Local path modules are used in place and are not copied.How does a child module return a value to its caller?
It declares an
outputblock, and the caller reads it asmodule.MODULE_NAME.OUTPUT_NAME.How do you select a tag of a module stored in Git?
Add the
refquery parameter to the source, for example?ref=v1.2.0; theversionargument does not apply to Git sources.
Topic 1 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.