In Terraform, what happens when a root-module variable has no default and no value is supplied — first in an interactive terminal, and then in a CI job running terraform apply -input=false?
answer
- no default means required
- interactive Terraform asks on the terminal
- -input=false replaces the prompt with an error
- TF_INPUT=0 is the environment equivalent
- never rely on a prompt in automation
basics
~20 sInteractively, Terraform stops and prompts on the terminal for each unset variable that has no default, then continues with what you type. With -input=false there is no prompt: the run fails with an error saying there is no value for a required variable.
solid answer
~50 sA variable with no `default` is required. In an interactive run Terraform pauses before planning and prompts for each missing value on the terminal, which is fine for a human and useless for automation. Passing `-input=false` — or setting `TF_INPUT=0` — turns that prompt off, and the run fails instead with an error stating that the root module input variable is not set and has no default value, suggesting `-var` or `-var-file`. That failure is the behaviour you want in a pipeline: a fast, loud error rather than a job that blocks on stdin until it times out. It is also why `-input=false` belongs on every automated `plan` and `apply`, together with deliberately leaving no default on variables such as the environment name, so forgetting to pass one cannot silently produce a run against the wrong target.
code
hcl · 9 linesvariable "environment" {
type = string
description = "Target environment name; must be supplied per run"
}
variable "log_retention_days" {
type = number
default = 30
}go deeper
Know that a variable without a default is required, that an interactive run prompts for it, and that -input=false makes the run fail instead.
Describe both behaviours precisely, name the -input=false flag and its TF_INPUT=0 equivalent, and explain that prompting applies to root-module variables only.
Show why the loud failure is the goal: a runner without -input=false can block on stdin holding a state lock, and a needless default can turn a forgotten -var-file into a successful run against the wrong environment.
Own the interface rule across modules — which classes of variable are allowed a default at all — so that an incomplete invocation always fails rather than proceeding with plausible inputs.
## Required versus optional variables A `variable` block with a `default` is optional — omit it everywhere and the default applies. A `variable` block without a `default` is required: Terraform must obtain a value from some source before it can evaluate the configuration. There is no implicit empty string, no zero value and no null fallback. ```hcl variable "environment" { type = string # required: no default } variable "instance_count" { type = number default = 1 # optional } ``` ## Interactive behaviour Run `terraform plan` with `environment` unset and Terraform stops before doing any work and prompts on the terminal, one variable at a time, showing the variable's `description` if it has one. Whatever you type is used for that run only — nothing is written to a file. This is a convenience for a human exploring a configuration, and it is a trap as a workflow: the value is unrecorded, unreviewed and different every time somebody's fingers slip. Only root-module variables can be prompted for. A child module's variables are set by arguments in its `module` block, so a missing one there is a configuration error at plan time, not a prompt. ## Non-interactive behaviour `-input=false` tells Terraform never to ask a question. With a required variable unsupplied, the run then fails with an error reporting that the root module input variable is not set and has no default value, and pointing at `-var` or `-var-file` as the way to provide one. The equivalent environment switch is `TF_INPUT=0`, which is handy when the command line is assembled by a wrapper you do not control. Without `-input=false`, a non-interactive runner behaves badly in a way that depends on the runner: some fail immediately because stdin is closed, others sit waiting for input until the job's timeout kills it, burning a runner slot and leaving a state lock held for the duration. Neither is a good outcome, and both are avoidable by making `-input=false` unconditional in automation. ```bash terraform plan -input=false -var-file=envs/prod.tfvars -out=tfplan terraform apply -input=false tfplan ``` ## Designing for the loud failure The interesting judgment is not the mechanics but the choice of which variables get a default: - **No default** for anything whose wrong value is dangerous or unguessable: the environment or account name, the region when the blast radius differs per region, a release identifier the pipeline supplies. If it is missing, the run must stop. - **A default** for genuinely optional knobs where a sensible value exists and a missing one is harmless: log retention, an instance count that a module consumer rarely overrides, a feature toggle that is off. A default on a dangerous variable converts a forgotten `-var-file` into a silent, successful run against the wrong target. A missing default on a harmless knob just makes every caller pass boilerplate. Getting this split right is most of what "good module interface" means in practice. ## Related failure that looks the same If a value *is* supplied but the variable name is misspelled in the tfvars file, Terraform emits a warning about a value for an undeclared variable, ignores it, and then reports the declared variable as unset — so the same error appears even though a value was clearly in the file. Reading the warning above the error is the whole diagnosis. ## What good looks like in a pipeline Every automated Terraform invocation carries `-input=false`, supplies environment values through an explicit `-var-file`, and injects per-run values through `TF_VAR_` or `-var`. Required variables have no defaults, so the pipeline cannot drift into a run with unintended inputs — the worst case is a failed job with an error that names exactly which variable was missing.
- Which variables should deliberately have no default?The ones whose wrong value is dangerous or unguessable — the environment or account name, a release identifier the pipeline supplies, a region when blast radius differs. A default there turns a forgotten `-var-file` into a silent successful run against the wrong target. Optional knobs with a harmless sensible value, such as log retention or a rarely-overridden count, should have defaults so callers are not forced into boilerplate.
- A CI job hangs for its full timeout on terraform apply. How does this explain it?Without `-input=false`, Terraform tries to prompt for a missing required variable and waits on stdin, which on many runners never closes, so the job blocks until the timeout kills it — and the state lock it took is held the whole time. Adding `-input=false` (or `TF_INPUT=0`) converts that into an immediate error naming the missing variable, which is both faster and diagnosable from the log.
- The value is clearly in the tfvars file, yet Terraform still says the variable is not set. What is going on?Almost always a name mismatch. Terraform emits a warning about a value assigned to an undeclared variable, ignores that value, and then reports the declared variable as unset — so the file looks right at a glance. Read the warning immediately above the error: it names the key that nothing declares. The other candidate is that the file is not auto-loaded and was never passed with `-var-file`.
saying these in an interview costs you the question
- Thinks an unset variable defaults to an empty string or null
- Believes -input=false suppresses the error as well as the prompt
- Relies on the interactive prompt as a workflow in automation
- Gives every variable a default so nothing can ever fail
- Expects Terraform to prompt for a child module's variables