Terraform Patterns¶
Overview¶
Terraform handles infrastructure provisioning for AWS and GCE instances.
Workstation Credentials (luna)¶
Before running Terraform, ensure cloud credentials are loaded.
AWS¶
AWS CLI uses credential_process for automatic 1Password integration:
See Secrets Management for details.
GCP¶
GCP requires sourcing credentials before running Terraform:
# Load credentials (Touch ID prompt)
source scripts/gcp/gcp-env.sh
# Or use alias
gcp-auth
# Then run Terraform
cd cloud/terraform/environments/production/gce/dumbo
terraform plan
See Secrets Management for details.
Critical Constraint: Static IP Protection¶
NEVER release reserved static IP addresses
These are irreplaceable resources. All static IPs use lifecycle { prevent_destroy = true }.
Protection Strategy¶
- Separate State Files - Static IPs managed in dedicated state, never with instances
- prevent_destroy Lifecycle - All static IP resources use
prevent_destroy = true - Data Sources for References - Instances reference IPs via data sources, not resource dependencies
- No IP Resources in Instance Modules - Instance modules NEVER create/manage IP allocations
Example: AWS Static IP¶
# static-ips/aws/main.tf - SEPARATE STATE FILE
resource "aws_eip" "pluto" {
lifecycle {
prevent_destroy = true # CRITICAL: Cannot be destroyed
}
tags = {
Name = "pluto-public-ip"
managed_by = "terraform"
critical = "true"
}
}
# instances/aws/pluto/main.tf - References IP, doesn't manage it
data "aws_eip" "pluto" {
filter {
name = "tag:Name"
values = ["pluto-public-ip"]
}
}
resource "aws_eip_association" "pluto" {
instance_id = aws_instance.pluto.id
allocation_id = data.aws_eip.pluto.id
}
Reserved Static IPs¶
DO NOT RELEASE THESE IPs
| IP | Cloud | Resource ID | Instance | Allocated |
|---|---|---|---|---|
| 52.32.80.62 | AWS | eipalloc-05fa588c23ff2037e | pluto | 2023-02-28 |
| 35.85.90.224 | AWS | eipalloc-02c6ee27c4a74bdb9 | (redshift) | ? |
| 34.44.33.3 | GCE | threefour (us-central1) | dumbo | ? |
| 35.209.219.216 | GCE | ? | bogart | ? |
| 193.8.172.100 | Meanservers | N/A | rocky | ? |
Directory Structure¶
terraform/
├── modules/
│ ├── aws-instance/ # Reusable AWS EC2 module
│ ├── gce-instance/ # Reusable GCE VM module
│ └── static-ips/ # Static IP management
└── environments/
├── production/
│ ├── aws/
│ │ ├── static-ips/ # Static IP state (PROTECTED)
│ │ ├── pluto/
│ │ └── mickey/
│ └── gce/
│ ├── static-ips/ # Static IP state (PROTECTED)
│ ├── dumbo/
│ └── bogart/
└── test/
AMI/Image Selection¶
AWS - Ubuntu¶
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-*-24.04-amd64-server-*"]
}
}
GCE - Ubuntu¶
data "google_compute_image" "ubuntu" {
family = "ubuntu-2404-lts-amd64"
project = "ubuntu-os-cloud"
}
Commands¶
Planning Changes¶
# Always use -target for instance changes
terraform plan -target=aws_instance.pluto
terraform apply -target=aws_instance.pluto
# NEVER run without -target on instance directories
# (could accidentally affect IP associations)
State Management¶
# List resources in state
terraform state list
# Show specific resource
terraform state show aws_instance.pluto
# Import existing resource
terraform import aws_instance.pluto i-1234567890abcdef0
Best Practices¶
AMI Creation¶
Always stop the instance before creating an AMI:
# Stop instance
aws ec2 stop-instances --instance-ids i-xxx
# Wait for stop
aws ec2 wait instance-stopped --instance-ids i-xxx
# Create AMI
aws ec2 create-image --instance-id i-xxx --name "pluto-backup-$(date +%Y%m%d)"
Consistency
Running instances can have inconsistent filesystem state captured. Use --no-reboot only for quick snapshots where consistency isn't critical.
Instance Recreation¶
To safely recreate an instance:
- Verify static IP is in separate state file
- Backup any local data
- Run
terraform destroy -target=aws_instance.pluto - Run
terraform apply -target=aws_instance.pluto - Static IP association will be recreated automatically
Implementation Status¶
Phase 1: AWS Foundation¶
- Terraformer import of existing resources
- Create aws-instance module
- Create pluto production config
- Create static-ips module with prevent_destroy
- Validate with pluto-dev (complete)
- Validate pluto can be recreated from IaC
Phase 3: GCE Support (Complete)¶
- Create gce-instance module
- Import dumbo into Terraform state
- Enable deletion protection on dumbo
- Validate with dumbo-dev (complete)
- Import bogart (coop-389306)
- Validate GCE instances can be recreated