Skip to content

Terraform Overview

This section covers using Terraform to provision AWS infrastructure for Rack Gateway, including RDS, S3 WORM buckets, KMS keys, and networking.

Managing Rack Gateway infrastructure with Terraform provides:

  • Reproducibility - Same infrastructure across environments
  • Version control - Track infrastructure changes in git
  • Automation - CI/CD for infrastructure deployments
  • Documentation - Code is documentation
terraform/
├── modules/
│ ├── rack_gateway/ # Main gateway module
│ │ ├── main.tf # RDS, S3, KMS resources
│ │ ├── variables.tf # Input variables
│ │ ├── outputs.tf # Output values
│ │ └── iam.tf # IAM roles and policies
│ └── networking/ # VPC and subnets
│ ├── main.tf
│ └── variables.tf
├── environments/
│ ├── staging/
│ │ ├── main.tf # Staging configuration
│ │ └── terraform.tfvars # Staging values
│ └── production/
│ ├── main.tf # Production configuration
│ └── terraform.tfvars # Production values
└── global/
└── shared/ # Cross-environment resources
  1. Initialize Terraform

    Terminal window
    cd terraform/environments/production
    terraform init
  2. Review the plan

    Terminal window
    terraform plan -out=plan.tfplan
  3. Apply changes

    Terminal window
    terraform apply plan.tfplan
  4. Retrieve outputs

    Terminal window
    terraform output database_url
    terraform output audit_anchor_bucket
main.tf
module "rack_gateway" {
source = "../../modules/rack_gateway"
environment = "production"
vpc_id = var.vpc_id
subnet_ids = var.private_subnet_ids
# Database
db_instance_class = "db.t3.medium"
db_password = var.db_password
# S3 WORM
enable_audit_anchoring = true
worm_retention_days = 400
# Tags
tags = {
Environment = "production"
Project = "rack-gateway"
}
}
VariableTypeRequiredDescription
environmentstringYesEnvironment name (staging, production)
vpc_idstringYesVPC ID for resources
subnet_idslistYesPrivate subnet IDs
db_instance_classstringNoRDS instance type (default: db.t3.small)
db_passwordstringYesDatabase password (sensitive)
enable_audit_anchoringboolNoEnable S3 WORM bucket (default: true)
worm_retention_daysnumberNoObject Lock retention (default: 400)
OutputDescription
database_urlPostgreSQL connection string
database_hostRDS endpoint hostname
audit_anchor_bucketS3 bucket name for anchors
kms_key_arnKMS key ARN for encryption
iam_role_arnIAM role for S3 access

Store Terraform state in S3 with locking:

backend.tf
terraform {
backend "s3" {
bucket = "myorg-terraform-state"
key = "rack-gateway/production/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}

Create the DynamoDB table for state locking:

resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}

Never commit secrets to git. Use:

  • Terraform Cloud/Enterprise variables
  • AWS Secrets Manager
  • Environment variables
# terraform.tfvars (add to .gitignore)
db_password = "secure-password-here"

Or use environment variables:

Terminal window
export TF_VAR_db_password="secure-password-here"
terraform apply

Terraform needs these AWS permissions:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds:*",
"s3:*",
"kms:*",
"iam:*",
"ec2:Describe*",
"ec2:CreateSecurityGroup",
"ec2:DeleteSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:RevokeSecurityGroupIngress"
],
"Resource": "*"
}
]
}
name: Terraform
on:
push:
branches: [main]
paths: ['terraform/**']
pull_request:
paths: ['terraform/**']
jobs:
terraform:
runs-on: ubuntu-latest
defaults:
run:
working-directory: terraform/environments/production
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6
- name: Terraform Init
run: terraform init
- name: Terraform Plan
run: terraform plan -no-color
env:
TF_VAR_db_password: ${{ secrets.DB_PASSWORD }}
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve
env:
TF_VAR_db_password: ${{ secrets.DB_PASSWORD }}

If you have existing infrastructure:

Terminal window
# Import existing RDS instance
terraform import module.rack_gateway.aws_db_instance.main rack-gateway
# Import existing S3 bucket
terraform import module.rack_gateway.aws_s3_bucket.audit_anchors audit-anchors-prod
Terminal window
# List state resources
terraform state list
# Show resource details
terraform state show module.rack_gateway.aws_db_instance.main
# Remove resource from state (careful!)
terraform state rm module.rack_gateway.aws_db_instance.main

If Terraform shows unexpected changes:

  1. Check for manual changes in AWS console
  2. Verify variable values match current state
  3. Run terraform refresh to sync state

If resources fail to create in order:

# Explicit dependency
resource "aws_db_instance" "main" {
depends_on = [aws_security_group.rds]
# ...
}
  • Separate state files per environment
  • Different AWS accounts for prod/non-prod
  • Consistent naming conventions
  • Always review plans before applying
  • Use workspaces or directories for environments
  • Tag resources for cost tracking
  • Document module inputs/outputs
  • Include architecture diagrams
  • Maintain runbooks for common operations