Files
DevOps-Security-Agent-Skills/infrastructure/cloud-aws/terraform-aws/references/best-practices.md
T
2026-01-27 17:35:45 -05:00

3.5 KiB

Terraform AWS Best Practices

Project Structure

project/
├── main.tf           # Main configuration
├── variables.tf      # Input variables
├── outputs.tf        # Output values
├── locals.tf         # Local values
├── data.tf           # Data sources
├── versions.tf       # Provider versions
├── terraform.tfvars  # Variable values (git-ignored)
├── modules/          # Local modules
│   └── vpc/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── environments/     # Environment configs
    ├── dev/
    ├── staging/
    └── prod/

State Management

Remote State with S3

terraform {
  backend "s3" {
    bucket         = "company-terraform-state"
    key            = "project/env/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

State Locking

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

Security Best Practices

Use IAM Roles, Not Credentials

provider "aws" {
  region = "us-east-1"
  # No access_key or secret_key - use IAM role or env vars
}

Enable Encryption Everywhere

resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
  bucket = aws_s3_bucket.example.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.example.arn
    }
  }
}

Use Sensitive Variables

variable "database_password" {
  type      = string
  sensitive = true
}

Tagging Strategy

locals {
  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
    Owner       = var.team
    CostCenter  = var.cost_center
  }
}

resource "aws_instance" "example" {
  # ... configuration ...
  
  tags = merge(local.common_tags, {
    Name = "example-instance"
    Role = "web"
  })
}

Module Best Practices

Version Pinning

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.0.0"  # Pin specific version
  
  # ... configuration ...
}

Variable Validation

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

Workflow

Plan Before Apply

terraform plan -out=tfplan
terraform apply tfplan

Use Workspaces or Directories for Environments

# Workspaces
terraform workspace new prod
terraform workspace select prod

# Or separate directories (recommended)
cd environments/prod
terraform apply

Common Patterns

Data Sources for Existing Resources

data "aws_vpc" "existing" {
  filter {
    name   = "tag:Name"
    values = ["main-vpc"]
  }
}

resource "aws_subnet" "new" {
  vpc_id = data.aws_vpc.existing.id
  # ...
}

Dynamic Blocks

resource "aws_security_group" "example" {
  # ...
  
  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}