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:
# Terraform variable:# variable "environment" { type = string }
export TF_VAR_environment="production"export TF_VAR_instance_count=5export TF_VAR_enable_monitoring=trueterraform applyPrecedence
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:
# Listexport TF_VAR_allowed_cidr_blocks='["10.0.0.0/8","192.168.1.0/24"]'
# Mapexport TF_VAR_tags='{"Environment":"production","Team":"platform"}'
# Objectexport 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:
# Retrieve from AWS Secrets Manager and exportexport TF_VAR_database_password=$(aws secretsmanager get-secret-value \ --secret-id "production/rds/master-password" \ --query SecretString \ --output text)
# From HashiCorp Vaultexport TF_VAR_api_key=$(vault kv get -field=api_key secret/myapp/credentials)
# From 1Password CLIexport TF_VAR_slack_webhook=$(op read "op://Team Vault/Slack Webhook/credential")
terraform applyunset TF_VAR_database_password # Clean up after applyProvider Authentication via Environment Variables
Every major provider reads credentials from environment variables — never put them in .tf files:
AWS
# 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 roleexport AWS_ROLE_ARN="arn:aws:iam::123456789012:role/TerraformRole"
# MFA session (when required)export AWS_SESSION_TOKEN="AQoXnyc4lcK4w..."Azure
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
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"# Or use application default credentials:gcloud auth application-default loginKubernetes
export KUBECONFIG="/path/to/kubeconfig"export KUBE_CONFIG_PATH="/path/to/kubeconfig"TF_* Behavior Variables
These control Terraform’s own runtime behavior:
# Enable detailed logging (DEBUG, INFO, WARN, ERROR, TRACE)export TF_LOG=DEBUGexport TF_LOG_PATH="/tmp/terraform.log"
# Disable color output (useful for CI log parsing)export TF_CLI_ARGS="-no-color"
# Apply to specific commands onlyexport TF_CLI_ARGS_plan="-no-color -compact-warnings"export TF_CLI_ARGS_apply="-no-color -auto-approve"
# Override the working directory for all operationsexport 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 workspaceexport TF_WORKSPACE="production"TF_LOG: Debugging Terraform
When Terraform behaves unexpectedly, enable logging to see exactly what it’s doing:
# Log all Terraform operationsexport TF_LOG=DEBUGterraform plan 2>&1 | head -100
# Log only specific subsystemsexport TF_LOG=provider # Provider-level debug infoexport TF_LOG=core # Terraform core operations
# Save to file (log output goes to stderr, redirect to file)export TF_LOG=DEBUGexport TF_LOG_PATH="terraform-debug.log"terraform apply 2>&1
# Analyze the loggrep "Request:" terraform-debug.log | head -20grep "Error:" terraform-debug.logLog levels (most to least verbose): TRACE, DEBUG, INFO, WARN, ERROR
GitHub Actions: Secure Environment Variables
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)
# .env (add to .gitignore!)TF_VAR_environment=devTF_VAR_database_password=local-dev-passwordAWS_DEFAULT_REGION=us-east-1AWS_PROFILE=dev-account
# Load .env fileset -a && source .env && set +aterraform planUse direnv to automatically load .env when you cd into the project directory.