Infrastructure

πŸ—οΈ Terraform Infrastructure as Code

Terraform by HashiCorp is the industry-standard Infrastructure as Code (IaC) tool. It lets you define cloud infrastructure in a declarative configuration language (HCL), plan changes before applying them, and maintain state to track what has been provisioned.

Core Concepts

  • Providers β€” Plugins that interface with APIs (AWS, GCP, Azure, Kubernetes, GitHub, etc.).
  • Resources β€” The fundamental unit. Each resource maps to a real infrastructure object (e.g., aws_instance).
  • Data Sources β€” Query existing infrastructure managed outside Terraform.
  • State β€” A JSON file (local or remote) that maps your configuration to real-world resources.
  • Modules β€” Reusable, parameterized collections of resources.
  • Workspaces β€” Isolated state environments for the same configuration (dev/staging/prod).

HCL Syntax

HashiCorp Configuration Language (HCL) is Terraform's declarative syntax. It is designed to be human-readable and machine-writable.

hcl
# variables.tf
variable "environment" {
  description = "Deployment environment (dev, staging, prod)"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

variable "instance_count" {
  description = "Number of EC2 instances"
  type        = number
  default     = 2
}

variable "allowed_cidrs" {
  description = "CIDR blocks allowed to reach the load balancer"
  type        = list(string)
  default     = ["0.0.0.0/0"]
}

# locals.tf
locals {
  common_tags = {
    Project     = "mc-platform"
    Environment = var.environment
    ManagedBy   = "terraform"
    Owner       = "platform-team"
  }
  name_prefix = "mc-${var.environment}"
}

Providers & Backend Configuration

hcl
# versions.tf
terraform {
  required_version = ">= 1.7.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.25"
    }
  }

  # Remote state in S3 with DynamoDB locking
  backend "s3" {
    bucket         = "mc-terraform-state-prod"
    key            = "platform/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "mc-terraform-locks"
    kms_key_id     = "alias/terraform-state"
  }
}

provider "aws" {
  region = "us-east-1"

  default_tags {
    tags = local.common_tags
  }
}

provider "aws" {
  alias  = "us-west-2"
  region = "us-west-2"
}

State Management

⚠️

Never commit state files Always use a remote backend (S3, Terraform Cloud, GCS) with state locking. Local state files can cause irreversible infrastructure drift when shared on teams.

bash
# State operations
terraform state list                             # list all managed resources
terraform state show aws_instance.web_server     # inspect a specific resource
terraform state mv aws_instance.old aws_instance.new  # rename resource
terraform state rm aws_instance.imported         # stop managing (does NOT destroy)

# Import existing infrastructure
terraform import aws_s3_bucket.assets mc-assets-prod

# Refresh state to match real-world (use carefully)
terraform apply -refresh-only

Modules

Modules encapsulate reusable infrastructure patterns. Maxi's Computers maintains an internal module registry for common components.

hcl
# main.tf β€” using internal modules
module "vpc" {
  source  = "git::https://github.com/maxiscomputers/terraform-modules.git//vpc?ref=v3.2.0"

  name             = "${local.name_prefix}-vpc"
  cidr_block       = "10.0.0.0/16"
  azs              = ["us-east-1a", "us-east-1b", "us-east-1c"]
  private_subnets  = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets   = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
  enable_nat_gateway = true
  single_nat_gateway = var.environment != "prod"
  tags = local.common_tags
}

module "eks" {
  source  = "git::https://github.com/maxiscomputers/terraform-modules.git//eks?ref=v2.1.0"

  cluster_name    = "${local.name_prefix}-eks"
  cluster_version = "1.29"
  vpc_id          = module.vpc.vpc_id
  subnet_ids      = module.vpc.private_subnet_ids

  node_groups = {
    general = {
      instance_types = ["m6i.xlarge"]
      min_size       = 2
      max_size       = 10
      desired_size   = 3
    }
  }
}

# outputs.tf
output "vpc_id"          { value = module.vpc.vpc_id }
output "eks_endpoint"    { value = module.eks.cluster_endpoint }
output "eks_ca_data"     { value = module.eks.cluster_ca_certificate }

Workspaces

bash
# Create and switch workspaces
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
terraform workspace list
terraform workspace select prod

# Use workspace name in configuration
resource "aws_s3_bucket" "app_data" {
  bucket = "mc-app-${terraform.workspace}-data"
  # ...
}

Standard CLI Workflow

bash
# 1. Initialize β€” download providers and modules
terraform init -upgrade

# 2. Format and validate
terraform fmt -recursive
terraform validate

# 3. Plan β€” preview changes (always review before applying!)
terraform plan -var-file="environments/prod.tfvars" -out=tfplan

# 4. Apply β€” execute the plan
terraform apply tfplan

# 5. Targeted apply (use sparingly)
terraform apply -target=module.eks -var-file="environments/prod.tfvars"

# 6. Destroy (DANGER β€” requires explicit approval)
terraform destroy -var-file="environments/prod.tfvars"

Best Practices at MC

  • Pin provider versions with ~> constraints to avoid unexpected breaking changes.
  • Use terraform plan in CI β€” post the plan output as a pull request comment before merging.
  • Store .tfvars securely β€” never commit sensitive values; use CI secrets or Vault integration.
  • Tag every resource with local.common_tags for cost allocation and compliance.
  • Run terraform fmt as a pre-commit hook to enforce consistent formatting.
  • Use -out flag so the plan applied is exactly what was reviewed.
  • Separate state per environment β€” never share a single state file across prod/staging/dev.
πŸ“± Install MC Wiki
Add to home screen for offline access.