Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk5 min

Terraform Variables, Repetition, and Outputs Explained

A practical guide to Terraform’s module inputs, locals, repeated instances, dynamic nested blocks, and outputs, with examples and key constraints.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Terraform variables accept values from a module’s caller; locals name expressions for reuse inside a module; count and for_each create repeated instances; dynamic blocks generate nested provider-defined blocks; and outputs expose selected values to callers or the CLI. The key choice is whether repeated instances are best identified by a number or by meaningful keys.

How values move through a Terraform module

A reusable module has an input interface, internal expressions, and outputs. A caller supplies values to input variables. The module can transform or reuse those values with expressions and locals, then pass them to resources and data sources. Outputs expose selected results to the caller, Terraform CLI, or HCP Terraform. HashiCorp explains this module value flow in its Manage values in modules documentation.

As an Amazon Associate I earn from qualifying purchases.

Terraform uses the word “variable” specifically for an input declared in a variable block. Names such as each.key, each.value, and count.index are contextual values available within particular repeated blocks; they are not module inputs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How Terraform input variables work

Input variables let module consumers customize a module without editing its source. They can be declared in root or child modules. A parent module passes a value to a child through its module block. Root-module inputs can be set through CLI options, environment variables, variable definition files, or HCP Terraform. See HashiCorp’s variable block reference for the supported mechanisms and version-specific details.

Declare an input

variable "service_name" {
  type        = string
  description = "Name used to identify the service"
  default     = "example-service"
}

Without a default, a caller must provide a value. The type constrains its expected shape, while description records its purpose. Add a validation block when a value must satisfy rules beyond its type. The nullable argument controls whether null can be assigned.

A variable marked sensitive is hidden from ordinary CLI output, but that setting alone does not prevent the value from being stored in state or plan files. The variable reference also documents ephemeral and const; check that reference for their semantics in the Terraform version your project pins.

Pass a value to a child module

module "service" {
  source       = "./modules/service"
  service_name = var.service_name
}

Here the root module forwards its input to the child module’s input of the same name. HashiCorp’s style guide recommends a consistent variable argument order: type, description, optional default, optional sensitive, then validation. Use descriptive names and explain the expected value rather than relying on a name alone; see the Terraform configuration style guide.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use locals to name reusable expressions

A local value is an expression named inside a module, not a value supplied by the caller. Locals can refer to variables, resources, data sources, and other locals. They are useful when a calculated value is needed in multiple places or when a long expression deserves a meaningful name. HashiCorp describes locals in its locals block reference.

locals {
  service_name_normalized = lower(trimspace(var.service_name))
}

Use local.service_name_normalized where that expression is needed. A local does not create a new input or change the underlying value; it gives an expression a reusable name.

Choose count or for_each by instance identity

Both count and for_each repeat resource or module instances, but they give each instance a different identity and address shape. Use count when the instances are nearly identical and a numeric index is a natural fit. Use for_each when each instance has a meaningful identity represented by a map key or set member. A single block cannot use both. These distinctions are covered in HashiCorp’s count and for_each references.

Construct What it repeats Identity and access Best fit Main constraint
count Resource, module, and other documented block instances Numeric index; use count.index Nearly identical instances where a number represents them well Cannot be used in the same block as for_each
for_each Resource, module, data, and other documented block instances Map key or set member; use each.key and each.value Instances with meaningful identities or distinct values Keys or members must be known before apply; sensitive values are disallowed
dynamic Nested configuration blocks Iterator object with key and value Generating provider-defined nested blocks from a collection Cannot generate Terraform meta-argument blocks

Use count for indexed instances

resource "example_resource" "worker" {
  count = 2

  name = "worker-${count.index}"
}

This produces two instances addressed by numeric index. A counted resource or module has a list-like collection shape when referenced, so an individual instance is selected by index. The resource arguments in this example are illustrative: the valid resource type and arguments depend on the provider schema.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use for_each when keys carry meaning

variable "workers" {
  type = map(string)
}

resource "example_resource" "worker" {
  for_each = var.workers

  name = each.key
  size = each.value
}

For a map, each map key identifies an instance and each.value provides its associated value. A resource or module using for_each has a map-like collection shape when referenced, keyed by those identities. Using stable, understandable keys is a practical way to make instance identity clear in configuration and addresses.

For resources and modules, for_each accepts a map or a set of strings. Its keys or set members must be known before Terraform performs the apply; they cannot depend on impure functions such as uuid, bcrypt, or timestamp. Sensitive values are not allowed as the argument because instance identities are disclosed in Terraform’s UI. Data-source instances can also be addressed separately with count or for_each; see HashiCorp’s data source documentation.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

What dynamic blocks do—and do not do

A dynamic block generates nested configuration blocks inside a resource, data source, provider, or provisioner block. It is not another way to create repeated resource instances. Its label names the nested block type, its for_each supplies the elements, and content defines each generated block’s body.

dynamic "setting" {
  for_each = var.settings
  iterator = item

  content {
    name  = item.key
    value = item.value
  }
}

The optional iterator changes the temporary iterator’s name; without it, the iterator defaults to the dynamic block label. The iterator exposes key and value. The nested block label and its arguments must be supported by the containing provider-defined schema, so this pattern is illustrative rather than a complete resource for a particular provider.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dynamic blocks can generate only nested blocks belonging to the configured construct. They cannot generate Terraform meta-argument blocks such as lifecycle or provisioner, which Terraform processes earlier. HashiCorp advises using literal nested blocks when possible and avoiding dynamic blocks when they make configuration harder to read; see Dynamic Blocks.

Expose selected values with outputs

Outputs make selected values available outside the module. A caller can read a child module’s declared output through the module result; outputs can also appear in Terraform CLI output and HCP Terraform. HashiCorp’s Manage values in modules documentation explains module inputs, locals, and outputs.

output "normalized_service_name" {
  description = "Normalized name used by the service module"
  value       = local.service_name_normalized
}

A parent can refer to this child output as module.service.normalized_service_name. If the module itself is repeated, the result’s shape follows the repetition method: a keyed map for for_each, or a list for count. HashiCorp recommends output descriptions and clear output names so the module’s interface is understandable.

Check examples against your project

Terraform language constructs are not tied to one provider, but resource arguments and nested block schemas are. Treat the examples above as illustrations of language behavior, not provider-ready resource definitions. Validate configuration against the Terraform version and provider versions pinned by your project, especially when relying on newer variable arguments such as ephemeral or const.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.