New engagements · 24h
Skip to main content
Home / Docs / Terraform Standards

Terraform

Terraform Standards

1. Module Structure

Every Terraform module follows a consistent file structure. This makes modules predictable — anyone on the team knows exactly where to look for any given concern.

module/
├── main.tf          # Resources
├── variables.tf     # Input variables with descriptions
├── outputs.tf       # Output values
├── versions.tf      # Provider and Terraform version constraints
└── README.md        # Usage, inputs, outputs

Do

One resource type per file for complex modules. Split iam.tf, networking.tf, compute.tf when main.tf exceeds ~150 lines.

Don't

Put multiple unrelated resource types in main.tf. When main.tf exceeds 300 lines, it is a signal that the module has too many responsibilities.

2. Remote State

Always use S3 backend with DynamoDB locking. Local state fails the moment two engineers or two pipeline runs execute simultaneously. State files are encrypted at rest with SSE-S3.

terraform {
  backend "s3" {
    bucket         = "your-org-terraform-state"
    key            = "project/env/component.tfstate"
    region         = "eu-west-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

Do

Use separate state files per environment and component. dev/vpc.tfstate, dev/eks.tfstate, prod/vpc.tfstate

Don't

Use local state in CI/CD. The state file disappears with the runner. Don't commit .tfstate files to version control.

⚠️

Warning

Never commit .tfstate files. They contain plaintext secrets, resource IDs and configuration that should never be in version control. Add *.tfstate and *.tfstate.backup to .gitignore.

3. Variable Naming

Use snake_case throughout. Prefix variables with their resource type for clarity. Every variable must have a description.

variable "rds_instance_class" {
  description = "RDS instance type for the primary database"
  type        = string
  default     = "db.t3.micro"
}

variable "eks_node_count" {
  description = "Desired number of EKS worker nodes"
  type        = number
  default     = 2
}

variable "eks_min_node_count" {
  description = "Minimum number of EKS worker nodes for autoscaling"
  type        = number
  default     = 1
}

✅ Correct prefixes

  • rds_ — database resources
  • eks_ — cluster resources
  • vpc_ — networking resources
  • iam_ — access and roles

❌ Avoid

  • db_class — ambiguous resource type
  • nodeCount — camelCase breaks convention
  • x, val — no descriptive name
  • Variables without description

4. OIDC Authentication

Never use static AWS credentials in CI/CD pipelines. Always configure GitHub Actions OIDC provider. This gives pipelines 15-minute ephemeral tokens scoped to minimum required permissions.

# In your GitHub Actions workflow
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::ACCOUNT_ID:role/github-actions-role
    aws-region: eu-west-1

# Never use this:
# aws-access-key-id: NEVER_USE_STATIC_KEYS
# aws-secret-access-key: NEVER_USE_STATIC_KEYS

The Terraform OIDC provider and the deploy role require separate IAM roles with separate permission sets. The Terraform role needs terraform:* on state resources. The deploy role needs only what the running service requires.

5. Environment Separation

Separate state files per environment using variable files. Use Terraform workspaces only for truly identical infrastructure — different environments typically have different configurations and should use separate state files.

environments/
├── dev/
│   ├── terraform.tfvars    # dev-specific values
│   └── backend.tf          # dev state configuration
└── prod/
    ├── terraform.tfvars    # prod-specific values
    └── backend.tf          # prod state configuration

# Apply
terraform init -backend-config=environments/dev/backend.tf
terraform apply -var-file=environments/dev/terraform.tfvars

Do

Use separate state files and variable files per environment. The differences between environments should be explicit, auditable and version-controlled.

⚠️

Warning

Terraform workspaces share the same backend configuration. If dev and prod differ in more than variable values (topology, enabled modules, resource sizes), use separate state files, not workspaces.