# Workload Identity Federation: GitHub Actions pushes images without long-lived keys. resource "google_iam_workload_identity_pool" "github" { workload_identity_pool_id = "github" depends_on = [google_project_service.enabled] } resource "google_iam_workload_identity_pool_provider" "github" { workload_identity_pool_id = google_iam_workload_identity_pool.github.workload_identity_pool_id workload_identity_pool_provider_id = "github" attribute_mapping = { "google.subject" = "assertion.sub" "attribute.repository" = "assertion.repository" } attribute_condition = "assertion.repository == \"${var.github_repo}\"" oidc { issuer_uri = "https://token.actions.githubusercontent.com" } } resource "google_service_account" "ci" { account_id = "rarelens-ci" } resource "google_service_account_iam_member" "ci_wif" { service_account_id = google_service_account.ci.name role = "roles/iam.workloadIdentityUser" member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/${var.github_repo}" } resource "google_artifact_registry_repository_iam_member" "ci_push" { repository = google_artifact_registry_repository.images.name location = var.region role = "roles/artifactregistry.writer" member = "serviceAccount:${google_service_account.ci.email}" } # Runtime identities, bound to Kubernetes ServiceAccounts in namespace "rarelens" via GKE # Workload Identity (the gcp overlay annotates the k8s side). resource "google_service_account" "api" { account_id = "rarelens-api" } resource "google_service_account" "pipeline" { account_id = "rarelens-pipeline" } resource "google_service_account_iam_member" "api_workload_identity" { count = var.deploy_kubernetes ? 1 : 0 service_account_id = google_service_account.api.name role = "roles/iam.workloadIdentityUser" member = "serviceAccount:${var.project}.svc.id.goog[rarelens/rarelens-api]" } resource "google_service_account_iam_member" "pipeline_workload_identity" { count = var.deploy_kubernetes ? 1 : 0 service_account_id = google_service_account.pipeline.name role = "roles/iam.workloadIdentityUser" member = "serviceAccount:${var.project}.svc.id.goog[rarelens/rarelens-pipeline]" } resource "google_project_iam_member" "api_sql" { project = var.project role = "roles/cloudsql.client" member = "serviceAccount:${google_service_account.api.email}" } resource "google_pubsub_topic_iam_member" "api_publish" { topic = google_pubsub_topic.vcf_uploaded.name role = "roles/pubsub.publisher" member = "serviceAccount:${google_service_account.api.email}" } resource "google_pubsub_subscription_iam_member" "pipeline_subscribe" { subscription = google_pubsub_subscription.vcf_uploaded_argo.name role = "roles/pubsub.subscriber" member = local.pipeline_sa_member } resource "google_storage_bucket_iam_member" "pipeline_data" { bucket = google_storage_bucket.data.name role = "roles/storage.objectAdmin" member = local.pipeline_sa_member } resource "google_artifact_registry_repository_iam_member" "pipeline_pull" { repository = google_artifact_registry_repository.images.name location = var.region role = "roles/artifactregistry.reader" member = local.pipeline_sa_member } # Nextflow's google-batch executor submits Batch jobs that run as the pipeline SA itself. resource "google_project_iam_member" "pipeline_batch" { for_each = toset(["roles/batch.jobsEditor", "roles/batch.agentReporter", "roles/logging.logWriter"]) project = var.project role = each.value member = local.pipeline_sa_member } resource "google_service_account_iam_member" "pipeline_act_as_self" { service_account_id = google_service_account.pipeline.name role = "roles/iam.serviceAccountUser" member = local.pipeline_sa_member }