Skip to content

Commit 58645cb

Browse files
committed
feat: Terraform — image buckets, IAM, thumbnail-worker SA + outputs
1 parent 13fc8fe commit 58645cb

3 files changed

Lines changed: 87 additions & 0 deletions

File tree

terraform/iam.tf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ resource "google_project_iam_member" "github_deployer_service_account_admin_scop
155155
resource.name.endsWith("/serviceAccounts/audio-generator@${var.project_id}.iam.gserviceaccount.com") ||
156156
resource.name.endsWith("/serviceAccounts/audio-scheduler@${var.project_id}.iam.gserviceaccount.com") ||
157157
resource.name.endsWith("/serviceAccounts/github-deployer@${var.project_id}.iam.gserviceaccount.com") ||
158+
resource.name.endsWith("/serviceAccounts/thumbnail-worker@${var.project_id}.iam.gserviceaccount.com") ||
158159
resource.name.endsWith("/serviceAccounts/${local.appspot_email}")
159160
EOT
160161
}

terraform/images_storage.tf

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Originals: full-resolution user-uploaded images. App Engine runtime
2+
# writes; thumbnail worker reads.
3+
resource "google_storage_bucket" "images_originals" {
4+
name = "${var.project_id}-images-originals"
5+
location = local.app_engine_region
6+
project = var.project_id
7+
force_destroy = false
8+
uniform_bucket_level_access = true
9+
10+
versioning { enabled = true }
11+
12+
lifecycle_rule {
13+
condition { with_state = "ARCHIVED", age = 30 }
14+
action { type = "Delete" }
15+
}
16+
lifecycle_rule {
17+
condition { age = 7 }
18+
action { type = "AbortIncompleteMultipartUpload" }
19+
}
20+
}
21+
22+
# Thumbnails: written later by an async worker. Empty in this
23+
# iteration, but provisioned now so IAM is in place.
24+
resource "google_storage_bucket" "images_thumbnails" {
25+
name = "${var.project_id}-images-thumbnails"
26+
location = local.app_engine_region
27+
project = var.project_id
28+
force_destroy = false
29+
uniform_bucket_level_access = true
30+
31+
lifecycle_rule {
32+
condition { age = 7 }
33+
action { type = "AbortIncompleteMultipartUpload" }
34+
}
35+
}
36+
37+
# App Engine runtime — write originals, read thumbnails (to sign URLs
38+
# for clients).
39+
resource "google_storage_bucket_iam_member" "appspot_originals_admin" {
40+
bucket = google_storage_bucket.images_originals.name
41+
role = "roles/storage.objectAdmin"
42+
member = "serviceAccount:${local.appspot_email}"
43+
}
44+
resource "google_storage_bucket_iam_member" "appspot_thumbnails_reader" {
45+
bucket = google_storage_bucket.images_thumbnails.name
46+
role = "roles/storage.objectViewer"
47+
member = "serviceAccount:${local.appspot_email}"
48+
}
49+
50+
# Future thumbnail worker — service account created now so IAM
51+
# bindings already exist when the Cloud Function lands. No deploy
52+
# follows from this alone; the worker is a later PR.
53+
resource "google_service_account" "thumbnail_worker" {
54+
account_id = "thumbnail-worker"
55+
display_name = "thumbnail-worker"
56+
description = "Async worker that generates image thumbnails."
57+
project = var.project_id
58+
59+
depends_on = [google_project_service.enabled]
60+
}
61+
62+
resource "google_storage_bucket_iam_member" "thumbnail_worker_originals_reader" {
63+
bucket = google_storage_bucket.images_originals.name
64+
role = "roles/storage.objectViewer"
65+
member = "serviceAccount:${google_service_account.thumbnail_worker.email}"
66+
}
67+
resource "google_storage_bucket_iam_member" "thumbnail_worker_thumbnails_admin" {
68+
bucket = google_storage_bucket.images_thumbnails.name
69+
role = "roles/storage.objectAdmin"
70+
member = "serviceAccount:${google_service_account.thumbnail_worker.email}"
71+
}

terraform/outputs.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,18 @@ output "audio_scheduler_job_name" {
5959
output "audio_generator_image_base" {
6060
value = "${google_artifact_registry_repository.audio_generator.location}-docker.pkg.dev/${var.project_id}/${google_artifact_registry_repository.audio_generator.repository_id}/audio-generator"
6161
}
62+
63+
output "images_originals_bucket" {
64+
description = "Name of the GCS bucket holding original image uploads."
65+
value = google_storage_bucket.images_originals.name
66+
}
67+
68+
output "images_thumbnails_bucket" {
69+
description = "Name of the GCS bucket reserved for async-generated thumbnails."
70+
value = google_storage_bucket.images_thumbnails.name
71+
}
72+
73+
output "thumbnail_worker_sa_email" {
74+
description = "Service account email for the future thumbnail worker."
75+
value = google_service_account.thumbnail_worker.email
76+
}

0 commit comments

Comments
 (0)