002 · Domain 6: Advanced Module Usage and Registry Practices · 10% of the exam
Task 6.2: Understand the contract of a Terraform module
What a module promises its callers: typed and validated inputs, optional object attributes, outputs, provider requirements and configuration_aliases, and which changes break callers.
Study it
The module contract: inputs, outputs, providers and breaking changes
Lesson coming
Sample questions
Try each one before opening the answer. Every option is explained, with the HashiCorp documentation page that proves it.
Question 1 · choose 1
A module declares:
variable "settings" {
type = object({
name = string
retention_days = optional(number, 30)
})
}
A caller passes settings = { name = "audit", retention_days =
var.override }, and var.override is null in this environment. What value
does var.settings.retention_days have inside the module?
Anull, because the caller set the attribute explicitly
BAn error, because null is not a number
C30, because the default also replaces an explicit null
D0, because a missing number defaults to zero
Show the answer and why
Anull, because the caller set the attribute explicitly
Incorrect
For an optional attribute with a non-null default, an explicit null is replaced by the default.
BAn error, because null is not a number
Incorrect
null is allowed here; Terraform substitutes the default for it.
C30, because the default also replaces an explicit null
Correct
An optional attribute with a non-null default is never null inside the module: Terraform inserts the default when the attribute is omitted or set to null.
D0, because a missing number defaults to zero
Incorrect
Terraform does not use zero values for missing numbers; it uses the declared default, or null when there is none.
Optional attribute defaults let a module promise non-null values to its own code. Callers can still leave an attribute unset dynamically with a conditional that returns null.
A widely used module is about to ship a new minor version. Which two changes break existing callers that upgrade without editing their module blocks? (Choose TWO.)
AA new input variable with no default value
Bconfiguration_aliases = [aws.dr] added to the module's required_providers
CA new optional input variable whose default keeps the old behavior
DA new output that exposes an additional resource attribute of the module
EA deprecated argument added to an output that will be removed in the next major version
Show the answer and why
AA new input variable with no default value
Correct
Variables without a default are required, so every existing module block that does not set it fails.
Bconfiguration_aliases = [aws.dr] added to the module's required_providers
Correct
Callers must now pass an aws.dr configuration in a providers map, which existing module blocks do not do.
CA new optional input variable whose default keeps the old behavior
Incorrect
Callers that do not set it get the default, so nothing changes for them.
DA new output that exposes an additional resource attribute of the module
Incorrect
New outputs are available to callers that want them and affect no one else.
EA deprecated argument added to an output that will be removed in the next major version
Incorrect
A deprecated output still works; Terraform only shows its message as a warning to module consumers.
A module's contract is its required inputs, its outputs and the provider configurations it expects. Adding requirements breaks callers; adding optional inputs, outputs or deprecation warnings does not.
A root module calls module "svc" with for_each = var.services. Inside the module, a resource sets name = each.key, and validation fails. The author wants each instance to know its service name. What should the module's interface provide?
ANothing; the module can read each.key from its caller
BA data source that looks up the calling module instance and its key
CA count argument in the module that mirrors for_each
DAn input variable such as name, set to each.key by the caller
Show the answer and why
ANothing; the module can read each.key from its caller
Incorrect
each exists only in the block that sets for_each, which here is the caller's module block.
BA data source that looks up the calling module instance and its key
Incorrect
No data source returns the instance key of the module call.
CA count argument in the module that mirrors for_each
Incorrect
count would create instances of its own; it does not pass the caller's key.
DAn input variable such as name, set to each.key by the caller
Correct
The each object is available only in the block that sets for_each, so the caller passes the key as an argument that the module declares as a variable.
A module sees only its own variables. Values that come from the call, such as the instance key, become part of the module's interface as input variables.