terraform { required_version = ">= 1.6" required_providers { google = { source = "hashicorp/google" version = "~> 5.0" } } # Store state in GCS — create the bucket manually once before first apply # Uncomment once GCS bucket permissions are confirmed working. # backend "gcs" { # bucket = "drb-tf-state" # prefix = "drb/state" # } } provider "google" { project = var.project_id region = var.region } # Pull live project metadata (number, name) without hardcoding them. data "google_project" "current" {} # --------------------------------------------------------------------------- # Static external IP # --------------------------------------------------------------------------- resource "google_compute_address" "drb" { name = "drb-server-ip" region = var.region } # --------------------------------------------------------------------------- # Firewall rules # --------------------------------------------------------------------------- resource "google_compute_firewall" "allow_web" { name = "drb-allow-web" network = "default" allow { protocol = "tcp" ports = ["80", "443"] } source_ranges = ["0.0.0.0/0"] target_tags = ["drb-server"] } resource "google_compute_firewall" "allow_ssh" { name = "drb-allow-ssh" network = "default" allow { protocol = "tcp" ports = ["22"] } # Restrict SSH to your IP(s) and Gitea runner IP source_ranges = var.allowed_ssh_cidrs target_tags = ["drb-server"] } # MQTT is NOT exposed externally — edge nodes connect via WireGuard (see below) # If you need to temporarily allow direct MQTT access for testing, uncomment and # restrict source_ranges to your node IPs. # # resource "google_compute_firewall" "allow_mqtt" { # name = "drb-allow-mqtt" # network = "default" # allow { # protocol = "tcp" # ports = ["8883"] # TLS MQTT, not 1883 # } # source_ranges = ["YOUR_NODE_CIDR"] # target_tags = ["drb-server"] # } # --------------------------------------------------------------------------- # Compute Engine VM # --------------------------------------------------------------------------- resource "google_compute_instance" "drb_server" { name = "drb-server" machine_type = var.machine_type zone = var.zone tags = ["drb-server"] boot_disk { initialize_params { image = "debian-cloud/debian-12" size = 30 # GB — free tier covers 30GB pd-standard on e2-micro type = "pd-standard" } } network_interface { network = "default" access_config { nat_ip = google_compute_address.drb.address } } metadata = { ssh-keys = "${var.ssh_user}:${var.ssh_public_key}" } # Startup script runs once on first boot to install Docker + Caddy metadata_startup_script = file("${path.module}/startup.sh") # The default compute service account with cloud-platform scope gives the VM # full access to GCS and Firestore in the same project — no key file needed. service_account { scopes = ["cloud-platform"] } lifecycle { # Prevent Terraform from destroying + recreating on metadata changes ignore_changes = [metadata_startup_script] } } # --------------------------------------------------------------------------- # IAM — grant the VM's default compute SA access to Firestore and GCS. # Since Firebase/GCS already live in the same project, no key file is needed — # the VM authenticates via the metadata server (ADC). # --------------------------------------------------------------------------- locals { compute_sa = "serviceAccount:${data.google_project.current.number}-compute@developer.gserviceaccount.com" } resource "google_project_iam_member" "drb_firestore" { project = var.project_id role = "roles/datastore.user" member = local.compute_sa } resource "google_project_iam_member" "drb_gcs" { project = var.project_id role = "roles/storage.objectAdmin" member = local.compute_sa } # --------------------------------------------------------------------------- # Firestore database — import existing, manages schema/settings going forward # --------------------------------------------------------------------------- import { id = "projects/${var.project_id}/databases/${var.firestore_database}" to = google_firestore_database.c2 } resource "google_firestore_database" "c2" { project = var.project_id name = var.firestore_database location_id = var.firestore_location type = "FIRESTORE_NATIVE" # Prevent accidental deletion of the live database deletion_policy = "DELETE" } # --------------------------------------------------------------------------- # GCS bucket — audio recordings. Import existing bucket. # --------------------------------------------------------------------------- import { id = var.audio_bucket_name to = google_storage_bucket.audio } resource "google_storage_bucket" "audio" { project = var.project_id name = var.audio_bucket_name location = var.audio_bucket_location uniform_bucket_level_access = true } # --------------------------------------------------------------------------- # DNS — managed in AWS Route 53 (cusano.net is there). # After terraform apply, add these A records in Route 53: # app.drb.cusano.net → server_ip output # api.drb.cusano.net → server_ip output # Or use a single wildcard: *.drb.cusano.net → server_ip # ---------------------------------------------------------------------------