Terraform input variables are the public interface of a module. Designed carefully, they boost reusability and prevent real-world incidents like type mismatches or swapped values. Designed sloppily — with vague typing or unclear precedence — unintended values get applied or secrets leak into outputs.
This article walks through variable block type design, validation, sensitive handling, value precedence, and how to pass values across module boundaries — following the official documentation. It also highlights the angles that come up most often on the Terraform Associate exam, with implementation patterns you can drop straight into your own code.
A variable block defines an external input to a module. You declare the name, type, description, default, validation, sensitive flag, and so on. A variable without a default is treated as a required input and must be supplied at plan and apply time.
Terraform is declarative: variables do not hold state — they are resolved at evaluation time. So a correct understanding of where values come from and which value ultimately wins (precedence) is the starting point of any solid design.
Types are the core of variable design. Picking among primitives (string/number/bool), collections (list/set/map), and structural (object/tuple) types eliminates ambiguity in inputs. The distinctions between list vs set and map vs object matter on both the exam and the job.
Choose the strictest type that fits the use case. To avoid future breaking changes, inputs likely to grow are safest defined as an object with an explicit schema.
| Type / Pattern | Example | Use cases, gotchas, and exam points |
|---|---|---|
| Primitive | string / number / bool | Simplest. Flags, names, numbers. Do not confuse bool with strings — use true, not "true". |
| list vs set | list(string) / set(string) | Use list to keep order. Use set (unordered) when you want deduplication and easy pairing with for_each. |
| map vs object | map(string) / object({k=...}) | Use map for free-key things like tags. Use object when you want to lock the schema and prevent breaking changes later. |
| tuple vs list | tuple([string, number]) | Fixed position and type. Tough to maintain as an external input. For the exam, remember fixed-length and position-dependent. |
| any | type = any | Flexible but unsafe. If you must use it, constrain with validation. On the exam, remember strict typing is recommended. |
Input safety and developer experience depend heavily on attribute design. With a default an input becomes optional; without one it is required. Use the validation block to spell out the accepted range and catch errors early. The sensitive flag masks plan output and UI displays, but does not hide values in state (encrypt the backend separately).
Terraform 1.x lets you use nullable to control whether null is accepted. Behavior details can vary across provider and language versions, so it is safest to pin your team's version before adopting it.
variable block example: type constraints, validation, sensitive, and null strategy
variable "environment" {
type = string
description = "デプロイ環境: dev/stg/prod"
validation {
condition = contains(["dev", "stg", "prod"], var.environment)
error_message = "environment は dev, stg, prod のいずれかにしてください。"
}
}
variable "instance_count" {
type = number
description = "作成するインスタンス数"
default = 2
validation {
condition = var.instance_count >= 1 && var.instance_count <= 20
error_message = "instance_count は 1〜20 の範囲で指定してください。"
}
}
variable "tags" {
type = map(string)
description = "共通タグ"
default = {}
}
variable "admin_password" {
type = string
description = "管理者パスワード"
sensitive = true
}
variable "optional_domain" {
type = string
description = "省略可のカスタムドメイン。未設定なら null"
default = null
nullable = true
}
variable "network" {
type = object({
vpc_id = string
subnet_ids = list(string)
enable_public = bool
})
description = "ネットワーク設定"
}
locals {
# null や空をフォールバック
effective_tags = length(var.tags) > 0 ? var.tags : {
"managed-by" = "terraform"
}
}
resource "example_instance" "this" {
count = var.instance_count
name = "${var.environment}-app-${count.index}"
tags = local.effective_tags
# 注意: var.admin_password を平文で出力に流さない。
}
The key to a reusable module is a stable input interface. Group related values into an object so consumers can pass them in meaningful units. Free-key sets like tags fit map(string); fixed-schema configuration groups fit object.
Manage breaking changes (type changes, switching to required, etc.) through module versioning. Toggling required vs optional has a big consumer impact, so when moving to optional, the safest path is to add a default or accept null and centralize the fallback in locals.
When a variable has multiple sources, Terraform picks the final value using a fixed precedence. More explicit sources win, and the last one resolved is the one applied. This shows up often on the Associate exam.
Understand the difference between auto-loaded files (terraform.tfvars, *.auto.tfvars) and explicit CLI options (-var, -var-file). The TF_VAR_name environment variable has low precedence and is useful for overriding defaults.
Variable value precedence (left = low → right = high)
Here are the common pitfalls in variable design. Use this as a checklist during code review.
Overusing any, passing booleans as strings, skipping validation, and ignoring sensitive are the classic causes of incidents.
Associate
問題 1
For the variable name, the variable block defines default="app". You set the environment variable TF_VAR_name=api, place name="web" in terraform.tfvars in the same directory, and then run plan with -var-file=env/dev.tfvars (which contains name="batch"). Which name is ultimately applied?
正解: A
Variable precedence is roughly default < env var < terraform.tfvars < *.auto.tfvars < -var-file < -var. So the name="batch" inside -var-file is what ultimately gets applied.
Does setting sensitive = true make state safe?
No. The sensitive flag only masks values in plan output and UI displays; it does not encrypt the state file itself. You need to protect state through the backend (for example, Terraform Cloud, encrypted remote backends, or storage-side encryption) and design outputs so they never pass secrets through directly.
Should I use map(string) or object?
Use map(string) for free-key dictionaries like tags. Use object when you want fixed attribute names and types for a configuration group (such as network settings), so future extensions stay schema-managed. For the exam, remember: map means free keys; object means a fixed schema.
What is the best way to pass per-environment values?
Prepare per-environment tfvars files and pass them explicitly, for example -var-file=env/dev.tfvars. This is easy to manage in practice and the precedence is clear. Put shared values in terraform.tfvars and apply individual overrides with -var or the TF_VAR_name environment variable.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...