Terraform Associate
Deep Dive
Modules are the main mechanism for reuse and encapsulation in Terraform. Understanding the difference between the root module and child modules, how data flows between them, and the standard file structure is essential for the exam.
Contents
A module is simply a directory containing one or more .tf files. There is nothing special about it: any directory with .tf files is a module.
Modules let you encapsulate and reuse configuration. Instead of repeating the same VPC configuration across 10 projects, you define it once in a module and call it with different parameters.
Encapsulation
Hides internal complexity. The user only needs to know the inputs and outputs.
Reuse
The same module can be used with different variables for dev, staging and prod.
Consistency
All projects follow the same pattern because they use the same module.
Root module
terraform init/plan/applyChild modules
# Typical structure with modules:
infra/
├── prod/
│ ├── main.tf ← root module (calls child modules)
│ ├── variables.tf
│ └── outputs.tf
└── modules/
├── vpc/ ← child module (networking)
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── eks/ ← child module (cluster)
├── main.tf
├── variables.tf
└── outputs.tfYou use the module block with a label (local name), the source argument (required), and the module's inputs as additional arguments.
# Call a local module
module "vpc" {
source = "./modules/vpc" # source required
# Module inputs (variables defined in modules/vpc/variables.tf)
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
environment = var.environment
}
# Call a module from the registry (version required)
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "my-cluster"
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id # output from the vpc module
subnet_ids = module.vpc.private_subnet_ids
}
# Accessing module outputs:
# module.<local_name>.<output_name>
output "cluster_endpoint" {
value = module.eks.cluster_endpoint
}💡 terraform get vs terraform init
Both download modules into the .terraform/modules/ directory. The difference is that terraform init also installs providers and configures the backend, whereas terraform get only downloads modules. In practice you always use init.
Modules communicate with their environment through variables (inputs) and outputs. This is the module's contract.
modules/vpc/variables.tf (inputs)
variable "name" {
type = string
description = "VPC name"
}
variable "cidr" {
type = string
default = "10.0.0.0/16"
description = "VPC CIDR block"
}
variable "azs" {
type = list(string)
description = "List of availability zones"
}modules/vpc/outputs.tf (outputs)
output "vpc_id" {
value = aws_vpc.main.id
description = "ID of the created VPC"
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
description = "IDs of the private subnets"
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
description = "IDs of the public subnets"
}⚠️ You can only access what the module exposes as an output
From the parent module, you cannot access internal resources of the child module directly (for example, module.vpc.aws_vpc.main.id does not work). You can only use the outputs explicitly defined in the child module (module.vpc.vpc_id). This guarantees encapsulation.
HashiCorp defines a recommended standard structure for modules. Modules on the Terraform Registry must follow it.
main.tf
RecommendedThe module's main resources. It is the conceptual entry point. For complex modules, it can be split into multiple files (network.tf, compute.tf, etc.).
variables.tf
RecommendedAll variable {} declarations. Defines the module's input contract.
outputs.tf
RecommendedAll output {} blocks. Defines what the module exposes to the outside.
README.md
RegistryModule documentation: what it does, inputs, outputs, usage examples. Required to publish to the registry.
versions.tf
RecommendedA terraform {} block with required_version and required_providers. Separates version requirements from the main code.
locals.tf
OptionalA locals {} block for computed values. Only needed if there are many locals.
CHANGELOG.md
RegistryChange history by version. Important for published modules.
examples/
RegistryA directory with usage examples for the module. Each subdirectory is a complete, working example.
Did you get this topic?
Test what you just learned
The root module calls a child module 'networking' that creates a VPC and defines an output named 'vpc_id'. How does the root module access that value?
Put what you learned to the test with original Terraform Associate exam questions.
Start Terraform Associate mock examUnlimited mock exams with the Pro plan
Log in to track your progress.