Cloud  /  Terraform

IaC Terraform 50 guides · updated 2026

Infrastructure as code done right — providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

Terraform Environment Variables

Terraform supports two categories of environment variables: TF_VAR_* variables for supplying Terraform variable values, and TF_* variables that control Terraform’s own behavior. Together, they enable secure, configurable automation without committing sensitive values to files.


TF_VAR_* — Supply Variable Values

Any Terraform variable can be set via an environment variable prefixed with TF_VAR_ followed by the variable name:

Terminal window
# Terraform variable:
# variable "environment" { type = string }
export TF_VAR_environment="production"
export TF_VAR_instance_count=5
export TF_VAR_enable_monitoring=true
terraform apply

Precedence

Environment variables have lower priority than -var and -var-file flags but higher than the default value in the variable block:

-var / -var-file (highest)
↓ *.auto.tfvars files
↓ terraform.tfvars
↓ TF_VAR_* environment variables
↓ default in variable block (lowest)

Complex Variable Types

For lists, maps, and objects, use HCL or JSON syntax in the environment variable value:

Terminal window
# List
export TF_VAR_allowed_cidr_blocks='["10.0.0.0/8","192.168.1.0/24"]'
# Map
export TF_VAR_tags='{"Environment":"production","Team":"platform"}'
# Object
export TF_VAR_database_config='{"engine":"postgres","instance_class":"db.t3.small","allocated_storage":50,"multi_az":true,"backup_days":7}'

Secrets via Environment Variables

The safest way to handle secrets — no file is written, nothing appears in git:

Terminal window
# Retrieve from AWS Secrets Manager and export
export TF_VAR_database_password=$(aws secretsmanager get-secret-value \
--secret-id "production/rds/master-password" \
--query SecretString \
--output text)
# From HashiCorp Vault
export TF_VAR_api_key=$(vault kv get -field=api_key secret/myapp/credentials)
# From 1Password CLI
export TF_VAR_slack_webhook=$(op read "op://Team Vault/Slack Webhook/credential")
terraform apply
unset TF_VAR_database_password # Clean up after apply

Provider Authentication via Environment Variables

Every major provider reads credentials from environment variables — never put them in .tf files:

AWS

Terminal window
# Long-term credentials (dev only)
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="us-east-1"
# Assume a role
export AWS_ROLE_ARN="arn:aws:iam::123456789012:role/TerraformRole"
# MFA session (when required)
export AWS_SESSION_TOKEN="AQoXnyc4lcK4w..."

Azure

Terminal window
export ARM_SUBSCRIPTION_ID="your-subscription-id"
export ARM_TENANT_ID="your-tenant-id"
export ARM_CLIENT_ID="your-service-principal-app-id"
export ARM_CLIENT_SECRET="your-service-principal-secret"

GCP

Terminal window
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"
# Or use application default credentials:
gcloud auth application-default login

Kubernetes

Terminal window
export KUBECONFIG="/path/to/kubeconfig"
export KUBE_CONFIG_PATH="/path/to/kubeconfig"

TF_* Behavior Variables

These control Terraform’s own runtime behavior:

Terminal window
# Enable detailed logging (DEBUG, INFO, WARN, ERROR, TRACE)
export TF_LOG=DEBUG
export TF_LOG_PATH="/tmp/terraform.log"
# Disable color output (useful for CI log parsing)
export TF_CLI_ARGS="-no-color"
# Apply to specific commands only
export TF_CLI_ARGS_plan="-no-color -compact-warnings"
export TF_CLI_ARGS_apply="-no-color -auto-approve"
# Override the working directory for all operations
export TF_DATA_DIR="/tmp/terraform-data" # Where .terraform/ is stored
# Disable remote state locking (rarely needed)
export TF_CLI_ARGS_apply="-lock=false"
# Skip interactive prompts (non-interactive mode)
export TF_INPUT=false
# Set a specific workspace
export TF_WORKSPACE="production"

TF_LOG: Debugging Terraform

When Terraform behaves unexpectedly, enable logging to see exactly what it’s doing:

Terminal window
# Log all Terraform operations
export TF_LOG=DEBUG
terraform plan 2>&1 | head -100
# Log only specific subsystems
export TF_LOG=provider # Provider-level debug info
export TF_LOG=core # Terraform core operations
# Save to file (log output goes to stderr, redirect to file)
export TF_LOG=DEBUG
export TF_LOG_PATH="terraform-debug.log"
terraform apply 2>&1
# Analyze the log
grep "Request:" terraform-debug.log | head -20
grep "Error:" terraform-debug.log

Log levels (most to least verbose): TRACE, DEBUG, INFO, WARN, ERROR


GitHub Actions: Secure Environment Variables

.github/workflows/terraform.yml
jobs:
terraform-apply:
runs-on: ubuntu-latest
env:
# Provider auth via OIDC (no static keys)
AWS_DEFAULT_REGION: us-east-1
# Non-sensitive Terraform variables
TF_VAR_environment: production
TF_VAR_region: us-east-1
# Suppress color codes in CI logs
TF_CLI_ARGS: "-no-color"
TF_INPUT: "false"
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Set sensitive vars from secrets
run: |
echo "TF_VAR_database_password=${{ secrets.DB_PASSWORD }}" >> $GITHUB_ENV
echo "TF_VAR_api_key=${{ secrets.API_KEY }}" >> $GITHUB_ENV
# Secrets are masked in GitHub Actions logs automatically
- run: terraform init -input=false
- run: terraform apply -input=false -auto-approve

.env File Pattern (Local Development)

Terminal window
# .env (add to .gitignore!)
TF_VAR_environment=dev
TF_VAR_database_password=local-dev-password
AWS_DEFAULT_REGION=us-east-1
AWS_PROFILE=dev-account
# Load .env file
set -a && source .env && set +a
terraform plan

Use direnv to automatically load .env when you cd into the project directory.