Hardcoding values directly in main.tf works for a quick test but breaks down as soon as a project grows: credentials end up in source control, switching between environments requires editing source files, and configuration becomes hard to review. Terraform input variables solve all three problems by separating values from logic — secrets stay out of source files, environments switch by swapping a single .tfvars file, and the configuration itself stays readable.
Recommended project structure
Instead of one monolithic main.tf, split configuration into purpose-specific files. Terraform loads all .tf files in a directory automatically, so the split is purely organizational.
terraform.tfvars often contains API keys and other secrets. Add it to .gitignore. Commit .terraform.lock.hcl to lock provider versions for the whole team.
Provider configuration
Move the provider configuration out of main.tf into a dedicated providers.tf so it is easy to find and update:
The api_key value now comes from a variable instead of being hardcoded.
Define variables
All variable declarations go in variables.tf. A variable block has four optional fields:
Variable types
Terraform supports simple types and collection types.
Simple types
Collection types
Reference individual elements in configuration:
Sensitive variables
Mark variables that hold secrets with sensitive = true:
Terraform masks the value everywhere — in plan output, apply output, and the interactive console:
The actual value is still stored in terraform.tfstate. Do not commit the state file to version control.
Add a validation block to catch bad values before Terraform makes any API call:
If the value fails the condition, Terraform prints the error message and stops:
Assign values to variables
Terraform resolves variable values in this order (later sources override earlier ones):
Auto-loaded file
Create terraform.tfvars in the project directory. Terraform loads it automatically without any flags:
Run terraform plan — Terraform reads the file automatically:
Files per deployment stage
For separate environments (dev, staging, production), create one .tfvars file per environment:
staging.tfvars:
Pass the file explicitly with -var-file:
Command-line override
Override a single variable without editing any file:
Use multiple -var flags to override several variables at once:
The -var flag takes the highest priority among file-based methods — it overrides values from .tfvars files.
CI/CD injection
Any variable named example can be set via the environment variable TF_VAR_example. This is the preferred method in CI/CD pipelines and secret managers where writing files is impractical.
Terraform picks up the values without any flags or files.
Define outputs
Outputs expose values from the Terraform state — useful for reading resource IDs after apply, or for passing values between configurations. All output declarations go in outputs.tf:
After terraform apply, outputs print automatically:
Print outputs at any time without re-running apply:
For scripting and automation, use JSON format:
Mark an output as sensitive to prevent it from printing in plain text:
Inspect variables interactively
terraform console opens an interactive prompt that evaluates expressions against the current configuration and state. Use it to check variable values and test expressions before using them in resources.
Type exit or press Ctrl+D to close the console.
terraform console loads the configuration once at startup. After editing .tf files, exit and restart the console to see updated values.
.gitignore recommendations
Commit .terraform.lock.hcl — it locks provider versions for the team and does not contain secrets.
Tag configuration versions
Tagging significant commits gives rollbacks a reliable target. Tag each release or major configuration change after applying it:
List all available tags at any time:
Test rollback procedures periodically so the process is already verified before a real incident occurs.
Roll back the configuration
Configuration changes can sometimes cause unexpected behavior. A rollback strategy allows quick reversion to the last known good state, minimizing downtime.
Step 1. Identify the target version
Run git log or git tag to browse available checkpoints and identify the version to restore.
Step 2. Check out the version
Step 3. Apply the rollback
The infrastructure is now running the configuration from the selected version.