ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

Infrastructure as Code with Terraform

Manage infrastructure as code. Covers Terraform modules, state management, workspace patterns, drift detection, testing IaC, and enterprise-scale Terraform architecture.

Infrastructure as Code (IaC) means managing servers, networks, and services through version-controlled code instead of clicking through cloud consoles. Terraform is the dominant multi-cloud IaC tool, but using it well at enterprise scale requires patterns for state management, module design, and team collaboration that aren’t obvious from the documentation.


Terraform Architecture

├── modules/                     # Reusable, versioned modules
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── compute/
│   └── database/

├── environments/                # Environment-specific configs
│   ├── dev/
│   │   ├── main.tf             # References modules
│   │   ├── terraform.tfvars    # Dev-specific values
│   │   └── backend.tf          # Dev state backend
│   ├── staging/
│   └── production/

└── .github/
    └── workflows/
        └── terraform.yml       # CI/CD pipeline

State Management

ApproachBest ForRisk
S3 + DynamoDB lockAWS teamsLow (standard)
Terraform CloudMulti-cloud teamsLow (managed)
GCS + lockGCP teamsLow (standard)
Local stateLearning onlyHigh (never in production)
# Remote state with S3 + DynamoDB locking
terraform {
  backend "s3" {
    bucket         = "company-terraform-state"
    key            = "production/networking/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

Module Design

# modules/rds/main.tf
resource "aws_db_instance" "main" {
  identifier     = "${var.environment}-${var.name}"
  engine         = var.engine
  engine_version = var.engine_version
  instance_class = var.instance_class
  
  allocated_storage     = var.allocated_storage
  max_allocated_storage = var.max_allocated_storage
  storage_encrypted     = true
  
  multi_az               = var.environment == "production"
  backup_retention_period = var.environment == "production" ? 30 : 7
  
  deletion_protection = var.environment == "production"
  
  vpc_security_group_ids = [aws_security_group.db.id]
  db_subnet_group_name   = aws_db_subnet_group.main.name
  
  tags = merge(var.tags, {
    Environment = var.environment
    ManagedBy   = "terraform"
  })
}

CI/CD Pipeline

# terraform-ci.yml
on:
  pull_request:
    paths: ['environments/**', 'modules/**']

jobs:
  plan:
    steps:
      - name: Terraform Format Check
        run: terraform fmt -check -recursive
      
      - name: Terraform Init
        run: terraform init
      
      - name: Terraform Validate
        run: terraform validate
      
      - name: Terraform Plan
        run: terraform plan -out=tfplan
      
      - name: Post plan to PR comment
        run: |
          terraform show -no-color tfplan > plan.txt
          gh pr comment $PR_NUMBER --body "$(cat plan.txt)"

  # Apply only on merge to main
  apply:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - run: terraform apply -auto-approve tfplan

Anti-Patterns

Anti-PatternProblemFix
Clicking in consoleDrift, no audit trail, unreproducibleAll changes through Terraform
Monolithic stateSlow plans, blast radius = everythingSplit state per environment/service
No module versioningBreaking changes affect all environmentsTag and version modules, pin in consumers
Local stateState on developer laptop → data lossRemote backend with locking
terraform apply locallyNo review, no CI checksApply only through CI/CD pipeline
Hardcoded valuesCan’t reuse across environmentsVariables with .tfvars per environment

Checklist

  • Remote state backend with locking
  • State split by environment and service
  • Modules versioned and reusable
  • CI/CD pipeline: format → validate → plan → apply
  • Plan output posted to PR for review
  • Apply only through CI/CD (never local)
  • Drift detection: scheduled plan to detect manual changes
  • Tagging policy: all resources tagged (environment, team, managed-by)
  • State encryption at rest
  • import existing resources before managing them

:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For IaC consulting, visit garnetgrid.com. :::

Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →