Init, Format, and Validate
Scenario: A pull request looks fine locally, but the CI pipeline fails with "provider not found" because CI can't download the exact provider version the author's laptop happened to select.
Before Terraform can create anything, three commands set the stage, and it's worth knowing exactly what each one does and does NOT do, because conflating them is a common source of false confidence.
terraform fmt rewrites your .tf files into Terraform's canonical style (consistent indentation and spacing) - purely cosmetic, but it keeps diffs clean across a whole team.
terraform init is the command that actually prepares a working directory: it sets up the backend (where state will be stored - covered in Module 5), downloads any provider plugins and modules your configuration references, and writes (or honors) the dependency lock file you met in the previous section. You must rerun init any time you add a provider, add a module, or change backend configuration.
terraform validate checks that your HCL is syntactically correct and internally consistent (for example, that you haven't referenced a variable that doesn't exist). This is a very shallow check: it does not verify your cloud credentials work, that you have permission to create the resource, that you're under your quota, or that the provider's API will actually accept your request at runtime. A configuration can pass validate and still fail at apply for any of those reasons.
Analogy:initis stocking a workshop with the correct tools before work starts;validateis checking that the blueprint is internally coherent (the numbers add up, nothing references a room that doesn't exist). Neither one proves the city will actually grant you a building permit - that only gets tested when you try toapply.
A worked configuration
terraform fmt -recursive
terraform init
terraform validate
terraform providers
terraform version
In CI, pin exact Terraform and provider versions so every run is deterministic, and consider a provider mirror or cache to avoid repeatedly hitting the public registry. Treat a change to the lock file's recorded checksums or versions with the same scrutiny you'd give a dependency bump in application code - review the diff, don't just accept it because init regenerated it. A provider upgrade can silently change plan behavior (new default values, renamed arguments) even when your own .tf files haven't changed at all.
Common mistakes in production
- Treating a clean
terraform validateas proof the apply will succeed - it only checks syntax, not credentials, quotas, or live API behavior. - Forgetting to rerun
initafter adding a new provider or module, then getting confused by an "unsupported block type" error that has nothing to do with the actual typo.
Goal: Treat the plan as a change contract: understand every create, update, replacement, and destroy before approving it. Reinforce this lesson in the tf-version-lockfile Terraform lab. Open/labs/terraformand choose slugtf-version-lockfile; the lab runs real Terraform against the offline FakeCloud provider.