Skip to content

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?

  1. Anull, because the caller set the attribute explicitly
  2. BAn error, because null is not a number
  3. C30, because the default also replaces an explicit null
  4. 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.

Question 2 · choose 2

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.)

  1. AA new input variable with no default value
  2. Bconfiguration_aliases = [aws.dr] added to the module's required_providers
  3. CA new optional input variable whose default keeps the old behavior
  4. DA new output that exposes an additional resource attribute of the module
  5. 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.

Question 3 · choose 1

A module's README is drifting from its variables.tf. According to the standard module structure, what should the README contain?

  1. AA full table of every input and output, kept by hand
  2. BOnly the license text
  3. CA description of what it does and when to use it
  4. DA copy of main.tf for reference
Show the answer and why
  • AA full table of every input and output, kept by hand

    Incorrect

    Inputs and outputs need no manual documentation there.

  • BOnly the license text

    Incorrect

    The license goes in a LICENSE file.

  • CA description of what it does and when to use it

    Correct

    The README describes the module; tooling generates the inputs and outputs documentation.

  • DA copy of main.tf for reference

    Incorrect

    Usage examples belong in the examples directory.

Keep variable and output descriptions accurate, since generated documentation is built from them.

Question 4 · choose 1

A variable declares type = list(string) and nullable = false. A caller passes ["a", null]. What happens?

  1. AAccepted: only the collection itself cannot be null
  2. BTerraform replaces the null element with the default
  3. CTerraform rejects it, as no element may be null
  4. DTerraform drops the null element silently
Show the answer and why
  • AAccepted: only the collection itself cannot be null

    Correct

    With a collection type, nested elements can be null as long as the collection itself is not null.

  • BTerraform replaces the null element with the default

    Incorrect

    Defaults apply to the whole variable, not to elements.

  • CTerraform rejects it, as no element may be null

    Incorrect

    nullable applies to the variable value as a whole.

  • DTerraform drops the null element silently

    Incorrect

    Terraform does not filter elements.

Add a validation block if elements must not be null either.

Question 5 · choose 1

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?

  1. ANothing; the module can read each.key from its caller
  2. BA data source that looks up the calling module instance and its key
  3. CA count argument in the module that mirrors for_each
  4. 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.

Question 6 · choose 1

A module author plans to expose a variable for every argument of every resource. What does the style guide advise?

  1. AExpose them all, since more inputs always mean more reuse
  2. BExpose settings that change between deployments
  3. CUse locals instead of any variables
  4. DExpose only sensitive settings
Show the answer and why
  • AExpose them all, since more inputs always mean more reuse

    Incorrect

    Overusing variables makes code hard to understand.

  • BExpose settings that change between deployments

    Correct

    The guide asks whether a parameter will change between deployments before exposing it.

  • CUse locals instead of any variables

    Incorrect

    The guide also warns against overusing locals.

  • DExpose only sensitive settings

    Incorrect

    Sensitivity is not the deciding factor.

Give every variable a type and description, and a sensible default when it is optional.

Practice domain 6 →Practice all domains →