Editor & Dev Setup
A good Terraform workflow is more than the CLI: it’s editor feedback as you type, automatic formatting so diffs stay clean, a linter that catches mistakes before plan, and a .gitignore that keeps secrets and bulky cache files out of version control. Investing an hour in this setup pays off on every change you make afterward, and it standardizes the experience across your whole team. This page configures the HashiCorp Terraform VS Code extension, format-on-save, tflint, and pre-commit hooks. Everything here applies equally to OpenTofu — the file format and tooling are shared.
Installing the VS Code extension
The official HashiCorp Terraform extension (hashicorp.terraform) is the single biggest productivity boost. It bundles the terraform-ls language server, giving you syntax highlighting, semantic autocompletion for resource arguments, hover documentation pulled from the provider schema, go-to-definition across modules, and inline diagnostics. Install it from the marketplace or the command line.
code --install-extension hashicorp.terraform
Once installed, open any directory containing .tf files and the language server initializes automatically. After your first terraform init, completion becomes schema-aware: typing inside an aws_instance block suggests real arguments like ami, instance_type, and tags, and flags unknown ones immediately.
Tip: The language server reads provider schemas from the
.terraformdirectory created byterraform init. If autocompletion seems incomplete, runterraform initin the workspace and reload the window.
Format on save with terraform fmt
terraform fmt rewrites configuration to the canonical HCL style — consistent indentation, aligned = signs, and ordered arguments. Running it automatically on every save means formatting never shows up as noise in code review. Configure VS Code to use the extension as the default formatter for Terraform files and format on save.
{
"editor.formatOnSave": true,
"[terraform]": {
"editor.defaultFormatter": "hashicorp.terraform",
"editor.formatOnSave": true
},
"[terraform-vars]": {
"editor.defaultFormatter": "hashicorp.terraform",
"editor.formatOnSave": true
}
}
Drop this into your repository’s .vscode/settings.json so the convention travels with the project. To check formatting from the CLI or in CI, use the recursive check mode, which exits non-zero if anything is unformatted:
terraform fmt -recursive -check -diff
Output:
main.tf
--- old/main.tf
+++ new/main.tf
@@ -1,4 +1,4 @@
resource "aws_s3_bucket" "logs" {
- bucket = "my-app-logs"
+ bucket = "my-app-logs"
}
Linting with tflint
terraform validate only checks that your configuration is internally consistent. tflint goes further: it catches provider-specific errors such as invalid instance types, deprecated syntax, unused declarations, and naming-convention violations — often before you ever run a plan. Install it and add the AWS ruleset for deep cloud-aware checks.
brew install tflint # macOS; see releases for Linux/Windows
tflint --init # downloads configured plugins
Configure it with a .tflint.hcl file at the repository root. The AWS plugin validates resource arguments against the real AWS API surface.
plugin "aws" {
enabled = true
version = "0.35.0"
source = "github.com/terraform-linters/tflint-ruleset-aws"
}
rule "terraform_naming_convention" {
enabled = true
format = "snake_case"
}
rule "terraform_unused_declarations" {
enabled = true
}
Run it against your code from the project directory:
tflint --recursive
Output:
2 issue(s) found:
Warning: variable "region" is declared but not used (terraform_unused_declarations)
on variables.tf line 3:
3: variable "region" {
Error: "t2.tiny" is an invalid instance type (aws_instance_invalid_type)
on main.tf line 12:
12: instance_type = "t2.tiny"
Automating checks with pre-commit hooks
Linters only help if they run consistently. pre-commit runs formatting and validation automatically every time you commit, so broken or unformatted code never reaches the repository. Add a .pre-commit-config.yaml using the community Terraform hooks.
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.96.1
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_tflint
- id: terraform_docs
Install the framework and wire it into your git hooks once per clone:
pip install pre-commit
pre-commit install
From now on, git commit runs every hook and blocks the commit if any fail. You can also run the full suite manually against all files — handy in CI:
pre-commit run --all-files
Output:
Terraform fmt............................................................Passed
Terraform validate.......................................................Passed
Terraform tflint.........................................................Passed
Terraform docs...........................................................Passed
A Terraform .gitignore
Two categories of files must never be committed. The .terraform/ directory holds downloaded providers and modules (large, machine-specific) and state files can contain plaintext secrets like database passwords. Add a .gitignore at the repository root.
# Local .terraform directories and downloaded plugins
**/.terraform/*
# State files — may contain sensitive values
*.tfstate
*.tfstate.*
# Crash logs and plan output
crash.log
*.tfplan
# Variable files that hold secrets (commit *.tfvars.example instead)
*.auto.tfvars
secrets.tfvars
# Keep lock file in version control — do NOT ignore .terraform.lock.hcl
Warning: Always commit
.terraform.lock.hcl. It pins exact provider versions and checksums so every machine and CI run resolves identical providers — the same rolepackage-lock.jsonplays in Node projects.
Best Practices
- Install the official
hashicorp.terraformextension and runterraform initso the language server provides schema-aware completion and diagnostics. - Commit a
.vscode/settings.jsonwith format-on-save so HCL style is enforced for everyone without manual effort. - Run
terraform fmt -checkandtflintin CI to reject unformatted or invalid code at the pull-request stage. - Use
pre-committo runfmt,validate, andtflintlocally, catching problems before they ever leave your machine. - Ignore
.terraform/,*.tfstate, and secret*.tfvars, but always version.terraform.lock.hclfor reproducible provider resolution. - Pin tool and plugin versions (extension,
tflintruleset, pre-commitrev) so the toolchain is reproducible across the team and CI.