Terraform and GCP infrastructure conventions for The Curated Knot. Use when writing or modifying Terraform files, creating new modules, or adding GCP resources. Covers module design, naming, labeling, environment management, and GCP-specific patterns.
curated-knot-infrastructure/
├── modules/ # Reusable Terraform modules
│ ├── cloud-run/ # Cloud Run service deployment
│ ├── cloud-sql/ # PostgreSQL database
│ ├── networking/ # VPC, NAT, Router
│ ├── service-account/ # IAM service accounts
│ ├── storage/ # Cloud Storage buckets
│ ├── secrets/ # Secret Manager
│ └── workload-identity/ # GitHub OIDC federation
├── environments/
│ ├── development/ # curated-knot-develop project
│ └── production/ # curated-knot-prod project
└── scripts/ # Helper automation
| Environment | Project ID | Region | State Bucket |
|---|---|---|---|
| Development |
curated-knot-developasia-south1 |
curated-knot-development-tf-state |
| Production | curated-knot-prod | asia-south1 | curated-knot-production-tf-state |
Pattern: curated-knot-{resource}[-{variant}]
Examples:
curated-knot-api — Cloud Run servicecurated-knot-db — Cloud SQL instancecurated-knot-dev-static-assets — Dev storage bucketcurated-knot-static-assets — Prod storage bucketcurated-knot-images — Artifact Registrycurated-knot-nat-ip — NAT static IPvpc-connector — VPC Access ConnectorUse local.name_prefix = "curated-knot" in environment configs.
All resources must have:
labels = {
environment = var.environment # "development" or "production"
managed-by = "terraform"
}
Each module has a single main.tf with variables, resources, and outputs inline.
Variable rules:
descriptiontype constraintsOutput rules:
description to outputsLifecycle:
ignore_changes for immutable fields (e.g., Cloud Run image tags)prevent_destroy for critical resources (databases, state buckets)depends_on explicit for API enablement before resource creationEach environment directory has:
backend.tf — GCS state backendmain.tf — Module calls with environment-specific valuesvariables.tf — Variable declarationsoutputs.tf — Output declarationsDev vs Prod differences:
| Setting | Development | Production |
|---|---|---|
| Cloud Run min_instances | 0 (scale to zero) | 1 (always warm) |
| Cloud Run max_instances | 5 | 20 |
| Cloud SQL tier | db-f1-micro | db-f1-micro (cost-optimized) |
| Backup retention | 3 snapshots | 7 snapshots |
| Storage lifecycle | 90-day auto-delete | No lifecycle |
| VPC CIDR | 10.9.0.0/28 | 10.8.0.0/28 |
| Apply method | Auto on push to main | Requires /apply-prod |
roles/editor or roles/ownerThe terraform.yml workflow supports:
/plan, /plan-dev, /plan-prod, /apply, /apply-dev, /apply-prod/apply-prod (never auto-applies)terraform {
required_version = ">= 1.5.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
resource "google_project_service" "apis" {
for_each = toset([
"run.googleapis.com",
"sqladmin.googleapis.com",
"secretmanager.googleapis.com",
# ...
])
project = var.project_id
service = each.value
}
dynamic "env" {
for_each = var.secrets
content {
name = env.value.env_name
value_source {
secret_key_ref {
secret = env.value.secret_name
version = "latest"
}
}
}
}