TF-Associate-004 domain - 11% of the exam

Terraform modules

Terraform modules is 11% of the TF-Associate-004 exam. These are the objectives it covers, each with practice questions, with every answer explained.

The domain in numbers

31
Practice questions
4
Objectives
6
Easy
25
Medium

Where people go wrong

  • Applying version to sources that have no releases

    The wrong answers apply the version argument to a local path or a direct Git repository, or expect a github.com shorthand to pick the newest tag on its own. The version argument is honoured only for a module registry source, public or private, because a registry publishes discrete releases to choose between. A Git module is pinned with the ref query parameter in the source URL, such as ref=v1.2.0, and a local path has nothing to pin.

    See it in a sample question
  • Placing module installation at plan, apply or validate

    The wrong answers have plan, apply or validate download the modules. Each of those commands expects the module code to be present already: plan errors and asks for init when the installed module no longer matches the configuration, and validate reports an error if init has not run. Terraform init is the step that installs modules, and for a registry or git:: source it fetches the code into the .terraform/modules directory for later commands to use.

    See it in a sample question
  • Expecting plan or get to upgrade an installed module

    The wrong answers treat plan, apply or terraform get as the way to pull a newer release after the version constraint is loosened. Once a module is installed, Terraform keeps the cached copy and plan keeps running against it. The command that re-resolves the constraint against the registry and downloads the newest permitted version is terraform init -upgrade. Terraform get does not upgrade an installed module unless the -update flag is passed.

    See it in a sample question
  • Assuming a child module inherits from its caller

    The wrong answers have a child module inherit the parent's variables and locals, or receive a value through a tfvars file, a TF_VAR_ environment variable or an output. None of these cross a module boundary downwards. Each module has its own scope, and a value reaches a child only when the module block sets an argument whose name matches a variable the child declares. Outputs carry values up to the caller, never down.

    See it in a sample question
  • Reading child module values without an output

    The wrong answers read a child's value through var, local, an outputs segment or a bare name, or expect the root to see a resource inside the child directly. The caller reads module.NAME.OUTPUT, such as module.network.vpc_id, and the child must declare an output block for it. Sibling modules cannot see each other, so the root passes one module's output into the other as an input variable.

    See it in a sample question

Objectives in this domain

What to study

The official documents this domain's practice questions cite most.

Sample questions from this domain

Free sampleTerraform modulesmedium

How does a module sourced from a local path differ from one sourced from the Terraform Registry in the way Terraform handles it?

  • AA local module is referenced by a filesystem path and takes no version argument, whereas a registry module is referenced by a registry address and can be pinned with a version constraint. Correct
  • BA local module must be downloaded during init, whereas a registry module is read directly from disk without any retrieval step.
  • CA local module requires a version argument to select a release, whereas a registry module ignores any version argument supplied.
  • DA local module can only define data sources, whereas a registry module can define managed resources as well.
Local modules are addressed by filesystem path with no version, while registry modules are downloaded and can be version-pinned. Terraform treats a local path source as code already present in the working tree, so no retrieval or versioning applies; a registry source is a published module Terraform fetches during init and can constrain with the version argument.

Why A is correct: Local modules live alongside the configuration and are addressed by a relative path, so there is nothing to version, while registry modules are published releases that support a version argument for pinning.

Why B is wrong: This reverses the two. Registry modules are downloaded during init; local modules are already on disk and are simply referenced in place.

Why C is wrong: It is the registry module that honours a version argument. Local modules have no releases to choose between, so supplying version there is not how they work.

Why D is wrong: Both kinds of module can define the full range of resources, data sources, variables, and outputs. The source location does not restrict what a module may contain.

Free sampleTerraform modulesmedium

During which Terraform operation are the modules named in module blocks retrieved and installed into the working directory?

  • ADuring terraform plan, immediately before Terraform builds the resource graph for the run.
  • BDuring terraform init, when Terraform prepares the working directory. Correct
  • CDuring terraform apply, at the moment Terraform begins creating or updating resources.
  • DDuring terraform validate, as Terraform checks the syntax of the configuration.
Terraform retrieves and installs modules during init, before plan or apply can use them. Module retrieval is part of working-directory initialisation: init resolves each source, copies or downloads the module code, and records it so subsequent plan and apply operations can read the installed modules.

Why A is wrong: Plan assumes modules are already installed. If they are not, plan fails and asks you to run init first, because plan does not perform module retrieval.

Why B is correct: init is the initialisation step that downloads and installs child modules alongside provider plugins and backend setup, so later plan and apply runs find the module code in place.

Why C is wrong: Apply also expects modules to be present already. Retrieval happens earlier, so relying on apply to fetch modules is a misunderstanding of the workflow.

Why D is wrong: validate checks configuration correctness and needs modules already installed to do so; it does not retrieve them, and it will report an error if init has not been run.

Free sampleTerraform modulesmedium

A team has been running with a registry module pinned at 2.3.0. They edit the constraint to allow a newer release and want Terraform to download the updated module code. After changing the version constraint, which command must they run for Terraform to fetch the newer module version?

module "database" {
  source  = "acme/database/aws"
  version = "~> 2.5"
}
  • Aterraform plan, because planning re-reads the module block and pulls any newer version that the updated constraint allows.
  • Bterraform init -upgrade, so Terraform re-evaluates the constraint and downloads the newest permitted module version. Correct
  • Cterraform get, which always upgrades installed modules to the highest version the constraint permits.
  • Dterraform apply -refresh-only, which reconciles module versions in state and installs the newer release.
After loosening a module version constraint, terraform init -upgrade is required to re-resolve and download a newer permitted version. Terraform records the installed module version and reuses it on subsequent runs; only 'terraform init -upgrade' forces it to re-evaluate the version constraint and fetch a newer release that now satisfies it.

Why A is wrong: Plan reads already-installed modules but does not download or upgrade module code; it would run against the still-cached 2.3.0.

Why B is correct: Once a module is installed Terraform keeps the cached version until init -upgrade tells it to re-resolve constraints and fetch a newer permitted release.

Why C is wrong: terraform get installs missing modules but, like a plain init, does not upgrade an already-installed module unless the -update flag is passed.

Why D is wrong: A refresh-only apply reconciles resource state with real infrastructure and has nothing to do with downloading module code from the registry.

Free sampleTerraform modulesmedium

Inside a child module a practitioner writes an expression that references 'var.region', but 'region' is only declared as a variable in the root module, not in the child. What does Terraform do when this configuration is initialised and planned?

# modules/compute/main.tf
resource "aws_instance" "this" {
  availability_zone = "${var.region}a"
}
  • AIt inherits the value from the root's 'region' variable, because child modules can read any variable declared higher up the call tree.
  • BIt substitutes an empty string for the undeclared variable and continues the plan with a warning.
  • CIt reads the root value at plan time only if 'region' is also exported as a root output.
  • DIt fails with an error that 'var.region' is not declared, because each module has its own variable scope and must declare the variables it uses. Correct
Variable scope is local to each module, so a variable must be declared in the module that references it. Terraform scopes variables per module and does not share them across the module boundary. A reference to var.region in a child that never declares region is unresolved, so Terraform reports an undeclared variable error.

Why A is wrong: Tempting if you assume lexical inheritance, but Terraform modules do not inherit variables from their callers.

Why B is wrong: Plausible because Terraform tolerates some unset optional inputs, but an entirely undeclared variable is an error, not a silent default.

Why C is wrong: Tempting because it mixes real concepts, but outputs expose values to callers and never make a root variable visible inside a child.

Why D is correct: Correct: variable scope is confined to the declaring module, so referencing an undeclared variable inside the child is a configuration error.

Free sampleTerraform modulesmedium

A root module needs to reference the ID of a security group that is created inside a child module named 'firewall'. The resource address inside the child is 'aws_security_group.web'. What must the practitioner add so the root can read that ID as 'module.firewall.web_sg_id'?

# modules/firewall/main.tf
resource "aws_security_group" "web" {
  name = "web"
}
  • ANothing; the root can reference 'aws_security_group.web.id' directly because resources in child modules share the root namespace.
  • BDeclare 'output "web_sg_id" { value = aws_security_group.web.id }' in the child module. Correct
  • CDeclare 'variable "web_sg_id"' in the child module and let Terraform populate it from the created resource.
  • DAdd 'output "web_sg_id"' in the root module referencing the child resource by its full address.
A parent module can consume a child's internal value only if the child publishes it through a declared output. Module encapsulation hides a child's resources from its caller. The only supported channel for a value to travel from child to parent is an output block in the child, which the parent references as module.NAME.OUTPUT.

Why A is wrong: Tempting if you treat modules as flat, but child resources are encapsulated and cannot be addressed from the root without an output.

Why B is correct: Correct: a child exposes an internal attribute to its caller only through a named output, which the root then reads as module.<name>.<output>.

Why C is wrong: Plausible because it names the right identifier, but variables are inputs to the child, not a way to publish a computed value upward.

Why D is wrong: Tempting since an output is involved, but placing it in the root still cannot reach an encapsulated child resource that has no output of its own.

Other domains in this exam

See also the TF-Associate-004 cert hub, the study guide, and the cheat sheet.

Examworthy is not affiliated with or endorsed by HashiCorp. Original, blueprint-aligned practice material only.