data "google_project" "current" { project_id = var.project_id } resource "google_project_service" "enabled" { for_each = local.api_services project = var.project_id service = each.value disable_on_destroy = false disable_dependent_services = false } resource "google_artifact_registry_repository" "containers" { location = local.artifact_location project = var.project_id repository_id = var.artifact_repository_id description = "Container images for household bot" format = "DOCKER" labels = local.common_labels cleanup_policies { id = "keep-last-10" action = "KEEP" most_recent_versions { keep_count = 10 } } cleanup_policies { id = "delete-stale" action = "DELETE" condition { older_than = "1209600s" # 14 days } } lifecycle { ignore_changes = [ labels, effective_labels, terraform_labels, ] } depends_on = [google_project_service.enabled] } resource "google_service_account" "bot_runtime" { project = var.project_id account_id = "${var.environment}-bot-runtime" display_name = "${local.name_prefix} bot runtime" } resource "google_service_account" "mini_runtime" { project = var.project_id account_id = "${var.environment}-mini-runtime" display_name = "${local.name_prefix} mini runtime" } resource "google_cloud_tasks_queue" "scheduled_dispatches" { project = var.project_id location = var.region name = var.scheduled_dispatch_queue_name depends_on = [google_project_service.enabled] } resource "google_project_iam_member" "bot_runtime_cloud_tasks_enqueuer" { project = var.project_id role = "roles/cloudtasks.enqueuer" member = "serviceAccount:${google_service_account.bot_runtime.email}" } resource "google_secret_manager_secret" "runtime" { for_each = var.manage_runtime_secrets ? local.runtime_secret_ids : toset([]) project = var.project_id secret_id = each.value replication { auto {} } labels = local.common_labels depends_on = [google_project_service.enabled] } resource "google_secret_manager_secret_iam_member" "bot_runtime_access" { for_each = local.runtime_secret_ids project = var.project_id secret_id = each.value role = "roles/secretmanager.secretAccessor" member = "serviceAccount:${google_service_account.bot_runtime.email}" } resource "google_secret_manager_secret_iam_member" "github_deployer_bot_token_access" { count = var.create_workload_identity ? 1 : 0 project = var.project_id secret_id = var.telegram_bot_token_secret_id role = "roles/secretmanager.secretAccessor" member = "serviceAccount:${google_service_account.github_deployer[0].email}" } resource "google_secret_manager_secret_iam_member" "github_deployer_webhook_secret_access" { count = var.create_workload_identity ? 1 : 0 project = var.project_id secret_id = var.telegram_webhook_secret_id role = "roles/secretmanager.secretAccessor" member = "serviceAccount:${google_service_account.github_deployer[0].email}" } resource "google_secret_manager_secret_iam_member" "github_deployer_webhook_secret_test_access" { count = var.create_workload_identity ? 1 : 0 project = var.project_id secret_id = "${var.telegram_webhook_secret_id}-test" role = "roles/secretmanager.secretAccessor" member = "serviceAccount:${google_service_account.github_deployer[0].email}" } module "bot_api_service" { source = "./modules/cloud_run_service" project_id = var.project_id region = var.region name = "${local.name_prefix}-bot-api" service_account_email = google_service_account.bot_runtime.email image = var.bot_api_image allow_unauthenticated = true min_instance_count = var.bot_min_instances max_instance_count = var.bot_max_instances labels = local.common_labels env = merge( { NODE_ENV = var.environment DB_SCHEMA = var.db_schema }, var.bot_purchase_parser_model == null ? {} : { PURCHASE_PARSER_MODEL = var.bot_purchase_parser_model }, var.bot_assistant_model == null ? {} : { ASSISTANT_MODEL = var.bot_assistant_model }, var.bot_topic_processor_model == null ? {} : { TOPIC_PROCESSOR_MODEL = var.bot_topic_processor_model }, var.bot_topic_processor_timeout_ms == null ? {} : { TOPIC_PROCESSOR_TIMEOUT_MS = tostring(var.bot_topic_processor_timeout_ms) }, var.bot_assistant_timeout_ms == null ? {} : { ASSISTANT_TIMEOUT_MS = tostring(var.bot_assistant_timeout_ms) }, var.bot_assistant_memory_max_turns == null ? {} : { ASSISTANT_MEMORY_MAX_TURNS = tostring(var.bot_assistant_memory_max_turns) }, var.bot_assistant_rate_limit_burst == null ? {} : { ASSISTANT_RATE_LIMIT_BURST = tostring(var.bot_assistant_rate_limit_burst) }, var.bot_assistant_rate_limit_burst_window_ms == null ? {} : { ASSISTANT_RATE_LIMIT_BURST_WINDOW_MS = tostring(var.bot_assistant_rate_limit_burst_window_ms) }, var.bot_assistant_rate_limit_rolling == null ? {} : { ASSISTANT_RATE_LIMIT_ROLLING = tostring(var.bot_assistant_rate_limit_rolling) }, var.bot_assistant_rate_limit_rolling_window_ms == null ? {} : { ASSISTANT_RATE_LIMIT_ROLLING_WINDOW_MS = tostring(var.bot_assistant_rate_limit_rolling_window_ms) }, length(var.bot_mini_app_allowed_origins) == 0 ? {} : { MINI_APP_ALLOWED_ORIGINS = join(",", var.bot_mini_app_allowed_origins) }, var.bot_mini_app_url == null ? {} : { MINI_APP_URL = var.bot_mini_app_url }, var.scheduled_dispatch_public_base_url == null ? {} : { SCHEDULED_DISPATCH_PROVIDER = "gcp-cloud-tasks" SCHEDULED_DISPATCH_PUBLIC_BASE_URL = var.scheduled_dispatch_public_base_url GCP_SCHEDULED_DISPATCH_PROJECT_ID = var.project_id GCP_SCHEDULED_DISPATCH_LOCATION = var.region GCP_SCHEDULED_DISPATCH_QUEUE = google_cloud_tasks_queue.scheduled_dispatches.name } ) secret_env = merge( { TELEGRAM_WEBHOOK_SECRET = var.telegram_webhook_secret_id SCHEDULER_SHARED_SECRET = var.scheduler_shared_secret_id }, var.database_url_secret_id == null ? {} : { DATABASE_URL = var.database_url_secret_id }, var.telegram_bot_token_secret_id == null ? {} : { TELEGRAM_BOT_TOKEN = var.telegram_bot_token_secret_id }, var.openai_api_key_secret_id == null ? {} : { OPENAI_API_KEY = var.openai_api_key_secret_id } ) depends_on = [ google_project_service.enabled, google_cloud_tasks_queue.scheduled_dispatches, google_project_iam_member.bot_runtime_cloud_tasks_enqueuer, google_secret_manager_secret.runtime, google_secret_manager_secret_iam_member.bot_runtime_access ] } module "mini_app_service" { source = "./modules/cloud_run_service" project_id = var.project_id region = var.region name = "${local.name_prefix}-mini-app" service_account_email = google_service_account.mini_runtime.email image = var.mini_app_image allow_unauthenticated = true min_instance_count = var.mini_min_instances max_instance_count = var.mini_max_instances labels = local.common_labels env = { NODE_ENV = var.environment BOT_API_URL = module.bot_api_service.uri } depends_on = [google_project_service.enabled] } resource "google_service_account" "github_deployer" { count = var.create_workload_identity ? 1 : 0 project = var.project_id account_id = "${var.environment}-${var.github_deploy_service_account_id}" display_name = "${local.name_prefix} GitHub deployer" } resource "google_iam_workload_identity_pool" "github" { count = var.create_workload_identity ? 1 : 0 project = var.project_id workload_identity_pool_id = var.workload_identity_pool_id display_name = "GitHub Actions Pool" } resource "google_iam_workload_identity_pool_provider" "github" { count = var.create_workload_identity ? 1 : 0 project = var.project_id workload_identity_pool_id = google_iam_workload_identity_pool.github[0].workload_identity_pool_id workload_identity_pool_provider_id = var.workload_identity_provider_id display_name = "GitHub Actions Provider" attribute_mapping = { "google.subject" = "assertion.sub" "attribute.actor" = "assertion.actor" "attribute.repository" = "assertion.repository" } attribute_condition = "assertion.repository == \"${var.github_repository}\"" oidc { issuer_uri = "https://token.actions.githubusercontent.com" } } resource "google_service_account_iam_member" "github_oidc" { count = var.create_workload_identity ? 1 : 0 service_account_id = google_service_account.github_deployer[0].name role = "roles/iam.workloadIdentityUser" member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github[0].name}/attribute.repository/${var.github_repository}" } resource "google_project_iam_member" "github_deployer_roles" { for_each = var.create_workload_identity ? local.github_deploy_roles : toset([]) project = var.project_id role = each.value member = "serviceAccount:${google_service_account.github_deployer[0].email}" }