4.3 Validate a Configuration
Key Takeaways
- terraform validate checks that configuration is syntactically valid and internally consistent: attributes, required arguments, and types it can see from provider schemas
- validate does not call cloud APIs and does not check whether a VPC ID, AMI, or other remote object actually exists
- terraform fmt is style only; terraform plan refreshes remote objects and proposes actions — neither is a substitute for the other
- Validation requires an initialized working directory (providers and modules installed); terraform init -backend=false is the official way to init for validate-only CI
- Custom conditions (objective 4g) can make validate fail; treat that as a mention, not the whole condition system
4.3 Validate a Configuration
Quick Answer:
terraform validatechecks that the configuration is syntactically valid and internally consistent. It uses installed provider schemas to catch bad argument names, missing required arguments, and type errors it can see without credentials. It does not call AWS or Azure to ask whethervpc-0abc123exists. That check isplan.fmtonly rewrites style.
Objective 3c on Terraform Associate (004) is terraform validate on Terraform 1.12. HashiCorp's command reference opens with the sentence you should be able to repeat: the command validates configuration files in a directory and does not validate remote services, such as remote state or provider APIs.
Official references: terraform validate command, terraform fmt command, and the language style guide (run fmt and validate before you commit).
Why this objective appears on 004
Candidates collapse three commands into one vague "check my code." 004 separates them on purpose:
- Style is
fmt. - Internal consistency is
validate. - What would change in the real account is
plan.
If you claim validate proves the VPC ID is real, you are describing plan (and even plan only proves the provider's read succeeded at that moment). If you claim fmt catches a missing ami argument, you are describing validate.
What terraform validate checks
Usage: terraform validate [options].
After a successful init, validate loads the root module, the installed child modules, and the provider schemas those plugins expose. It then asks: is this configuration a legal Terraform program?
Typical failures validate can see:
- HCL syntax errors (unclosed brace, bad interpolation).
- A resource type the installed provider does not implement.
- An argument name that is not in the schema (
ami_idonaws_instanceinstead ofami). - A required argument the schema says is missing.
- An obvious type problem (
count = "three"when a number is required). - References to names that do not exist in this configuration (
var.missing,aws_instance.nope.id). - Some author-written custom conditions, when Terraform can evaluate them from configuration alone (objective 4g — mentioned below, not taught here).
Typical questions validate cannot answer:
- Does
vpc-0abc123exist in this AWS account? - Are these credentials valid?
- Is the remote state file reachable?
- Will the provider API accept this combination of arguments at apply time?
- Is the live infrastructure drifted from state?
HashiCorp's introduction is explicit: validate verifies that a configuration is syntactically valid and internally consistent, regardless of any provided variables or existing state. It is aimed at reusable modules and editor/CI checks. To verify a configuration in the context of a particular run (this workspace, these variable values, this state), use terraform plan, which includes an implied validation check and then talks to the world.
resource "aws_instance" "web" {
# validate fails: "ami_id" is not an aws_instance argument
ami_id = "ami-0123456789abcdef0"
instance_type = "t3.micro"
subnet_id = "subnet-0abc123" # validate accepts the type; it does not look up the subnet
}
Fix the argument name and validate can succeed even if that AMI and subnet were deleted yesterday. Only plan (refresh + provider reads) discovers the missing remote objects.
$ terraform validate
Success! The configuration is valid.
You normally run it after init
Validation requires an initialized working directory with referenced plugins and modules installed. Provider schemas live in those plugins. Without init, validate cannot know whether ami is required on aws_instance.
HashiCorp's documented pattern for CI that should validate without touching remote state:
terraform init -backend=false
terraform validate
-backend=false still installs providers and modules. Use it when the working directory was already designed to init, or when you only need schemas. Do not treat it as a substitute for a real init on the apply path.
The official init tutorial places validate immediately after the first successful init, before you inspect .terraform/. That is the habit 004 wants: Write → Init → Validate → Plan → Apply.
Contrast: fmt, validate, and plan
| Command | Question it answers | Talks to cloud APIs? | Needs init? | Changes files or infrastructure? |
|---|---|---|---|---|
terraform fmt | Does this HCL match canonical style? | No | No | Rewrites .tf files (unless -check / -write=false) |
terraform validate | Is this configuration internally consistent? | No | Yes (plugins + modules) | No |
terraform plan | What would change if we applied, given current state and remote objects? | Yes (refresh / reads) | Yes | Updates state only if you later apply; plan itself proposes |
fmt is style only
terraform fmt rewrites configuration to HashiCorp's canonical format: indentation, argument alignment, a subset of the language style guide. It is intentionally opinionated and has no style-rule flags. Useful flags on 004:
-check— exit non-zero if files are not formatted (CI).-diff— show the formatting diff.-recursive— include subdirectories (modules).-write=false— do not overwrite.
fmt will not tell you that ami is missing. A perfectly formatted file can still be invalid. Objective 3g covers formatting in more depth; here you only need the contrast: fmt does not validate.
plan is refresh plus proposed actions
terraform plan performs the default planning operations from 4.1: read remote objects, compare configuration to state, propose create/update/destroy. It also runs an implied validate first. A configuration that fails validate never becomes a meaningful plan. The reverse is not true: validate can pass and plan can still fail on a missing VPC, bad credentials, or a custom condition that needs remote data.
Use this decision list on the exam:
- Whitespace and argument alignment →
fmt - Wrong argument name or missing required argument →
validate - "Will this create two instances or destroy the bucket?" →
plan - "Does this ID exist in the account?" →
plan(notvalidate)
Machine-readable output: -json
terraform validate -json prints a JSON object for editors and CI. Official fields include:
valid(boolean)error_count/warning_countdiagnostics— each item hasseverity(errororwarning),summary, optionaldetail, and optional sourcerange
Warnings do not make valid false. Errors do. External tools must tolerate non-JSON on stdout if Terraform fails before validation starts. You do not need to memorize the snippet schema for 004; you do need to know -json exists and that it is for automation, not for humans at a terminal.
Custom conditions can make validate fail (objective 4g is later)
Terraform 1.12 includes author-written checks: variable validation blocks, precondition / postcondition on resources, data sources, and outputs, and check blocks. Objective 4g teaches that system. For 3c, remember one sentence: a custom condition that validate can evaluate from configuration may cause terraform validate to fail, even though the HCL is otherwise well-formed.
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "environment must be dev, staging, or prod."
}
}
If that condition is evaluable during validate and it fails, validate reports an error. That still is not a cloud lookup. A precondition that reads data.aws_ami.example.architecture needs a plan-time read; it is not a reason to say "validate talks to AWS." check blocks warn during plan/apply and do not turn validate into a monitoring tool.
Leave the evaluation-order table (variable validation → preconditions → postconditions → checks) for 4g. Here, do not confuse "a condition failed" with "the VPC exists."
HCP Terraform and CI
HCP Terraform remote runs validate as part of planning on the workspace VM. You still want fmt + validate in pull-request CI so broken HCL never opens a run. A common 1.12 pipeline is:
terraform fmt -check -recursive
terraform init -backend=false
terraform validate -json
# later, on the apply workspace: terraform plan -out=tfplan
That pipeline never asks whether the VPC is still there. The HCP Terraform plan run does.
Scenario: the "valid" module that still cannot apply
Priya publishes an aws_instance module. terraform init -backend=false && terraform validate succeeds in CI: every argument is in schema, var.subnet_id is a string, no missing required fields. A consumer sets subnet_id = "subnet-deadbeef" and runs plan. The AWS provider's read fails; the subnet is gone. Validate was not wrong. It never promised a live lookup. Priya adds a note in the module README: consumers must plan against a real account. She does not replace plan with validate in the apply pipeline.
004 traps for objective 3c
validatedoes not call provider APIs or check that a VPC/AMI/subnet ID exists.fmtis not validation; it is canonical style.planis the command that refreshes and proposes actions.validateneedsinit(orinit -backend=false) so provider schemas are present.- Custom conditions can fail validate; they do not turn validate into a cloud inventory tool.
- A successful validate is not an apply and does not write state.
A junior engineer runs terraform validate to confirm that vpc-0abc123 still exists in the AWS account. What should you tell them?
On Terraform 1.12, how do terraform fmt, terraform validate, and terraform plan differ?
Which statement about terraform validate on Terraform 1.12 is correct?