Skip to content

Comments

feat: IaC#1

Merged
kedeinroga merged 6 commits intomainfrom
refactor/database-optimization
Jan 22, 2026
Merged

feat: IaC#1
kedeinroga merged 6 commits intomainfrom
refactor/database-optimization

Conversation

@kedeinroga
Copy link
Owner

No description provided.

…ent and configuration

- Updated Dockerfile to enhance runtime configuration and user permissions.
- Modified Makefile to introduce a new alias for production deployment and clarify deployment messages.
- Enhanced cloudrun.yaml with additional environment variables for server configuration, JWT settings, and analytics.
- Refactored main.go and config.go to support raw PEM content for JWT keys from environment variables.
- Improved ad_campaign_test.go and ad_click.go for better readability and consistency.
- Updated ad_impression.go, ad_security.go, and related test files to enhance security and fraud detection logic.
- Refined maintenance.go and monitoring.go for better structure and clarity in maintenance operations.
- Enhanced security.go and token_manager.go to support flexible key loading from environment variables.
- Improved ad_fraud_detector.go and maintenance_service.go for better fraud detection and maintenance operations.
- Updated security_service_test.go and slug_service.go for consistency and improved test coverage.
@github-actions
Copy link

Terraform Format and Style 🖌failure

Terraform Initialization ⚙️success

Terraform Validation 🤖success

Terraform Plan 📖success

Show Plan
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_artifact_registry_repository.repository will be created
  + resource "google_artifact_registry_repository" "repository" {
      + create_time      = (known after apply)
      + description      = "Docker repository for radio-backend container images"
      + effective_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + format           = "DOCKER"
      + id               = (known after apply)
      + labels           = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + location         = "us-central1"
      + mode             = "STANDARD_REPOSITORY"
      + name             = (known after apply)
      + project          = "radio-485022"
      + repository_id    = "radio-backend"
      + terraform_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + update_time      = (known after apply)

      + cleanup_policies {
          + action = "DELETE"
          + id     = "delete-old-untagged"

          + condition {
              + older_than            = "604800s"
              + package_name_prefixes = []
              + tag_prefixes          = []
              + tag_state             = "UNTAGGED"
              + version_name_prefixes = []
            }
        }
      + cleanup_policies {
          + action = "KEEP"
          + id     = "keep-recent"

          + most_recent_versions {
              + keep_count            = 10
              + package_name_prefixes = []
            }
        }
    }

  # google_artifact_registry_repository_iam_member.cloudrun_reader will be created
  + resource "google_artifact_registry_repository_iam_member" "cloudrun_reader" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.reader"
    }

  # google_artifact_registry_repository_iam_member.github_actions_writer will be created
  + resource "google_artifact_registry_repository_iam_member" "github_actions_writer" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.writer"
    }

  # google_cloud_run_v2_service.service will be created
  + resource "google_cloud_run_v2_service" "service" {
      + conditions              = (known after apply)
      + create_time             = (known after apply)
      + creator                 = (known after apply)
      + delete_time             = (known after apply)
      + effective_annotations   = (known after apply)
      + effective_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + etag                    = (known after apply)
      + expire_time             = (known after apply)
      + generation              = (known after apply)
      + id                      = (known after apply)
      + ingress                 = "INGRESS_TRAFFIC_ALL"
      + labels                  = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + last_modifier           = (known after apply)
      + latest_created_revision = (known after apply)
      + latest_ready_revision   = (known after apply)
      + launch_stage            = (known after apply)
      + location                = "us-central1"
      + name                    = "radio-backend"
      + observed_generation     = (known after apply)
      + project                 = "radio-485022"
      + reconciling             = (known after apply)
      + terminal_condition      = (known after apply)
      + terraform_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + traffic_statuses        = (known after apply)
      + uid                     = (known after apply)
      + update_time             = (known after apply)
      + uri                     = (known after apply)

      + template {
          + max_instance_request_concurrency = (known after apply)
          + service_account                  = (known after apply)
          + timeout                          = "300s"

          + containers {
              + image = "gcr.io/radio-485022/radio-backend:latest"

              + env {
                  + name  = "AD_CACHE_TTL"
                  + value = "10m"
                }
              + env {
                  + name  = "AD_FRAUD_SCORE_THRESHOLD"
                  + value = "0.7"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_DAILY"
                  + value = "30"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_HOURLY"
                  + value = "6"
                }
              + env {
                  + name  = "AD_IMPRESSION_TOKEN_MAX_AGE"
                  + value = "5m"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_REQUESTS"
                  + value = "50"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "ANALYTICS_BATCH_SIZE"
                  + value = "100"
                }
              + env {
                  + name  = "ANALYTICS_FLUSH_INTERVAL"
                  + value = "10s"
                }
              + env {
                  + name  = "BCRYPT_COST"
                  + value = "12"
                }
              + env {
                  + name  = "CORS_ALLOWED_HEADERS"
                  + value = "Content-Type,Authorization,X-Language,X-Request-ID"
                }
              + env {
                  + name  = "CORS_ALLOWED_METHODS"
                  + value = "GET,POST,PUT,DELETE,OPTIONS"
                }
              + env {
                  + name  = "CORS_ALLOWED_ORIGINS"
                  + value = "https://your-production-frontend.com"
                }
              + env {
                  + name  = "DEFAULT_LANGUAGE"
                  + value = "en"
                }
              + env {
                  + name  = "FEATURE_ANALYTICS"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_PREMIUM_CONTENT"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_VAULT_INTEGRATION"
                  + value = "false"
                }
              + env {
                  + name  = "JWT_EXPIRATION"
                  + value = "24h"
                }
              + env {
                  + name  = "JWT_REFRESH_EXPIRATION"
                  + value = "168h"
                }
              + env {
                  + name  = "LOG_FORMAT"
                  + value = "json"
                }
              + env {
                  + name  = "LOG_LEVEL"
                  + value = "info"
                }
              + env {
                  + name  = "RADIO_BROWSER_API_URL"
                  + value = "https://de1.api.radio-browser.info"
                }
              + env {
                  + name  = "RATE_LIMIT_REQUESTS"
                  + value = "100"
                }
              + env {
                  + name  = "RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "SERVER_BASE_URL"
                  + value = "https://radio-backend-296736956418.us-central1.run.app"
                }
              + env {
                  + name  = "SERVER_ENV"
                  + value = "production"
                }
              + env {
                  + name  = "SERVER_HOST"
                  + value = "0.0.0.0"
                }
              + env {
                  + name  = "SERVER_PORT"
                  + value = "8080"
                }
              + env {
                  + name  = "SERVER_TIMEOUT"
                  + value = "30s"
                }
              + env {
                  + name  = "SUPPORTED_LANGUAGES"
                  + value = "en,es,fr,de"
                }
              + env {
                  + name = "DATABASE_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "database-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "REDIS_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "redis-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PRIVATE_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-private-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PUBLIC_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-public-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "AD_IMPRESSION_TOKEN_SECRET"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "ad-impression-token-secret"
                          + version = "latest"
                        }
                    }
                }

              + liveness_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 10
                  + period_seconds        = 30
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }

              + ports {
                  + container_port = 8080
                  + name           = "http1"
                }

              + resources {
                  + cpu_idle = true
                  + limits   = {
                      + "cpu"    = "1000m"
                      + "memory" = "512Mi"
                    }
                }

              + startup_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 5
                  + period_seconds        = 10
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }
            }

          + scaling {
              + max_instance_count = 3
              + min_instance_count = 1
            }
        }
    }

  # google_cloud_run_v2_service_iam_member.public_access will be created
  + resource "google_cloud_run_v2_service_iam_member" "public_access" {
      + etag     = (known after apply)
      + id       = (known after apply)
      + location = "us-central1"
      + member   = "allUsers"
      + name     = "radio-backend"
      + project  = (known after apply)
      + role     = "roles/run.invoker"
    }

  # google_iam_workload_identity_pool.github will be created
  + resource "google_iam_workload_identity_pool" "github" {
      + description               = "Workload Identity Pool for GitHub Actions"
      + disabled                  = false
      + display_name              = "GitHub Actions Pool"
      + id                        = (known after apply)
      + name                      = (known after apply)
      + project                   = "radio-485022"
      + state                     = (known after apply)
      + workload_identity_pool_id = "github-actions-pool"
    }

  # google_iam_workload_identity_pool_provider.github will be created
  + resource "google_iam_workload_identity_pool_provider" "github" {
      + attribute_condition                = "assertion.repository == 'kedeinroga/radio-backend'"
      + attribute_mapping                  = {
          + "attribute.actor"      = "assertion.actor"
          + "attribute.aud"        = "assertion.aud"
          + "attribute.repository" = "assertion.repository"
          + "google.subject"       = "assertion.sub"
        }
      + description                        = "OIDC provider for GitHub Actions"
      + disabled                           = false
      + display_name                       = "GitHub Provider"
      + id                                 = (known after apply)
      + name                               = (known after apply)
      + project                            = "radio-485022"
      + state                              = (known after apply)
      + workload_identity_pool_id          = "github-actions-pool"
      + workload_identity_pool_provider_id = "github-provider"

      + oidc {
          + issuer_uri = "https://token.actions.githubusercontent.com"
        }
    }

  # google_project_iam_member.cloudrun_roles["roles/cloudtrace.agent"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/cloudtrace.agent"
    }

  # google_project_iam_member.cloudrun_roles["roles/logging.logWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/logging.logWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/monitoring.metricWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/monitoring.metricWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/run.invoker"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.invoker"
    }

  # google_project_iam_member.github_actions_roles["roles/artifactregistry.writer"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/artifactregistry.writer"
    }

  # google_project_iam_member.github_actions_roles["roles/iam.serviceAccountUser"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/iam.serviceAccountUser"
    }

  # google_project_iam_member.github_actions_roles["roles/run.admin"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.admin"
    }

  # google_project_iam_member.github_actions_roles["roles/secretmanager.secretAccessor"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/secretmanager.secretAccessor"
    }

  # google_project_service.apis["artifactregistry.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "artifactregistry.googleapis.com"
    }

  # google_project_service.apis["cloudresourcemanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "cloudresourcemanager.googleapis.com"
    }

  # google_project_service.apis["compute.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "compute.googleapis.com"
    }

  # google_project_service.apis["iam.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iam.googleapis.com"
    }

  # google_project_service.apis["iamcredentials.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iamcredentials.googleapis.com"
    }

  # google_project_service.apis["run.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "run.googleapis.com"
    }

  # google_project_service.apis["secretmanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "secretmanager.googleapis.com"
    }

  # google_secret_manager_secret.secrets["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "ad-impression-token-secret"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["database_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "database-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_private_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-private-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_public_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-public-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["redis_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "redis-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "ad-impression-token-secret"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["database_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "database-url"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-private-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-public-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["redis_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "redis-url"
    }

  # google_secret_manager_secret_version.secret_versions["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["database_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["redis_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_service_account.cloudrun will be created
  + resource "google_service_account" "cloudrun" {
      + account_id   = "radio-backend-sa"
      + description  = "Service account for running the radio-backend Cloud Run service"
      + disabled     = false
      + display_name = "Radio Backend Cloud Run Service Account"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account.github_actions will be created
  + resource "google_service_account" "github_actions" {
      + account_id   = "radio-backend-sa-github"
      + description  = "Service account for GitHub Actions CI/CD"
      + disabled     = false
      + display_name = "Radio Backend GitHub Actions"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account_iam_member.github_actions_impersonate will be created
  + resource "google_service_account_iam_member" "github_actions_impersonate" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.serviceAccountUser"
      + service_account_id = (known after apply)
    }

  # google_service_account_iam_member.github_workload_identity will be created
  + resource "google_service_account_iam_member" "github_workload_identity" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.workloadIdentityUser"
      + service_account_id = (known after apply)
    }

Plan: 41 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + artifact_registry_repository         = (known after apply)
  + artifact_registry_url                = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
  + cloudrun_service_account_email       = (known after apply)
  + cloudrun_service_name                = "radio-backend"
  + cloudrun_service_url                 = (known after apply)
  + environment                          = "production"
  + github_actions_secrets               = {
      + ARTIFACT_REGISTRY_URL      = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
      + CLOUDRUN_SERVICE_NAME      = "radio-backend"
      + GCP_PROJECT_ID             = "radio-485022"
      + GCP_REGION                 = "us-central1"
      + GCP_SERVICE_ACCOUNT        = (known after apply)
      + WORKLOAD_IDENTITY_PROVIDER = (known after apply)
    }
  + github_actions_service_account_email = (known after apply)
  + infrastructure_summary               = (known after apply)
  + project_id                           = "radio-485022"
  + region                               = "us-central1"
  + secret_instructions                  = <<-EOT
        Secrets have been created in Secret Manager. Add values using:
        
        # Database URL (Supabase)
        printf "your-database-url" | gcloud secrets versions add database-url --data-file=-
        
        # Redis URL (Upstash)
        printf "your-redis-url" | gcloud secrets versions add redis-url --data-file=-
        
        # JWT Private Key
        cat keys/jwt-private.pem | gcloud secrets versions add jwt-private-key --data-file=-
        
        # JWT Public Key
        cat keys/jwt-public.pem | gcloud secrets versions add jwt-public-key --data-file=-
        
        # Ad Impression Token Secret
        printf "your-ad-token-secret" | gcloud secrets versions add ad-impression-token-secret --data-file=-
    EOT
  + secret_names                         = {
      + ad_impression_token_secret = "ad-impression-token-secret"
      + database_url               = "database-url"
      + jwt_private_key            = "jwt-private-key"
      + jwt_public_key             = "jwt-public-key"
      + redis_url                  = "redis-url"
    }
  + workload_identity_pool               = (known after apply)
  + workload_identity_provider           = (known after apply)

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

Pusher: @kedeinroga, Action: pull_request

@github-actions
Copy link

Terraform Format and Style 🖌failure

Terraform Initialization ⚙️success

Terraform Validation 🤖success

Terraform Plan 📖success

Show Plan
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_artifact_registry_repository.repository will be created
  + resource "google_artifact_registry_repository" "repository" {
      + create_time      = (known after apply)
      + description      = "Docker repository for radio-backend container images"
      + effective_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + format           = "DOCKER"
      + id               = (known after apply)
      + labels           = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + location         = "us-central1"
      + mode             = "STANDARD_REPOSITORY"
      + name             = (known after apply)
      + project          = "radio-485022"
      + repository_id    = "radio-backend"
      + terraform_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + update_time      = (known after apply)

      + cleanup_policies {
          + action = "DELETE"
          + id     = "delete-old-untagged"

          + condition {
              + older_than            = "604800s"
              + package_name_prefixes = []
              + tag_prefixes          = []
              + tag_state             = "UNTAGGED"
              + version_name_prefixes = []
            }
        }
      + cleanup_policies {
          + action = "KEEP"
          + id     = "keep-recent"

          + most_recent_versions {
              + keep_count            = 10
              + package_name_prefixes = []
            }
        }
    }

  # google_artifact_registry_repository_iam_member.cloudrun_reader will be created
  + resource "google_artifact_registry_repository_iam_member" "cloudrun_reader" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.reader"
    }

  # google_artifact_registry_repository_iam_member.github_actions_writer will be created
  + resource "google_artifact_registry_repository_iam_member" "github_actions_writer" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.writer"
    }

  # google_cloud_run_v2_service.service will be created
  + resource "google_cloud_run_v2_service" "service" {
      + conditions              = (known after apply)
      + create_time             = (known after apply)
      + creator                 = (known after apply)
      + delete_time             = (known after apply)
      + effective_annotations   = (known after apply)
      + effective_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + etag                    = (known after apply)
      + expire_time             = (known after apply)
      + generation              = (known after apply)
      + id                      = (known after apply)
      + ingress                 = "INGRESS_TRAFFIC_ALL"
      + labels                  = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + last_modifier           = (known after apply)
      + latest_created_revision = (known after apply)
      + latest_ready_revision   = (known after apply)
      + launch_stage            = (known after apply)
      + location                = "us-central1"
      + name                    = "radio-backend"
      + observed_generation     = (known after apply)
      + project                 = "radio-485022"
      + reconciling             = (known after apply)
      + terminal_condition      = (known after apply)
      + terraform_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + traffic_statuses        = (known after apply)
      + uid                     = (known after apply)
      + update_time             = (known after apply)
      + uri                     = (known after apply)

      + template {
          + max_instance_request_concurrency = (known after apply)
          + service_account                  = (known after apply)
          + timeout                          = "300s"

          + containers {
              + image = "gcr.io/radio-485022/radio-backend:latest"

              + env {
                  + name  = "AD_CACHE_TTL"
                  + value = "10m"
                }
              + env {
                  + name  = "AD_FRAUD_SCORE_THRESHOLD"
                  + value = "0.7"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_DAILY"
                  + value = "30"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_HOURLY"
                  + value = "6"
                }
              + env {
                  + name  = "AD_IMPRESSION_TOKEN_MAX_AGE"
                  + value = "5m"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_REQUESTS"
                  + value = "50"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "ANALYTICS_BATCH_SIZE"
                  + value = "100"
                }
              + env {
                  + name  = "ANALYTICS_FLUSH_INTERVAL"
                  + value = "10s"
                }
              + env {
                  + name  = "BCRYPT_COST"
                  + value = "12"
                }
              + env {
                  + name  = "CORS_ALLOWED_HEADERS"
                  + value = "Content-Type,Authorization,X-Language,X-Request-ID"
                }
              + env {
                  + name  = "CORS_ALLOWED_METHODS"
                  + value = "GET,POST,PUT,DELETE,OPTIONS"
                }
              + env {
                  + name  = "CORS_ALLOWED_ORIGINS"
                  + value = "https://your-production-frontend.com"
                }
              + env {
                  + name  = "DEFAULT_LANGUAGE"
                  + value = "en"
                }
              + env {
                  + name  = "FEATURE_ANALYTICS"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_PREMIUM_CONTENT"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_VAULT_INTEGRATION"
                  + value = "false"
                }
              + env {
                  + name  = "JWT_EXPIRATION"
                  + value = "24h"
                }
              + env {
                  + name  = "JWT_REFRESH_EXPIRATION"
                  + value = "168h"
                }
              + env {
                  + name  = "LOG_FORMAT"
                  + value = "json"
                }
              + env {
                  + name  = "LOG_LEVEL"
                  + value = "info"
                }
              + env {
                  + name  = "RADIO_BROWSER_API_URL"
                  + value = "https://de1.api.radio-browser.info"
                }
              + env {
                  + name  = "RATE_LIMIT_REQUESTS"
                  + value = "100"
                }
              + env {
                  + name  = "RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "SERVER_BASE_URL"
                  + value = "https://radio-backend-296736956418.us-central1.run.app"
                }
              + env {
                  + name  = "SERVER_ENV"
                  + value = "production"
                }
              + env {
                  + name  = "SERVER_HOST"
                  + value = "0.0.0.0"
                }
              + env {
                  + name  = "SERVER_PORT"
                  + value = "8080"
                }
              + env {
                  + name  = "SERVER_TIMEOUT"
                  + value = "30s"
                }
              + env {
                  + name  = "SUPPORTED_LANGUAGES"
                  + value = "en,es,fr,de"
                }
              + env {
                  + name = "DATABASE_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "database-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "REDIS_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "redis-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PRIVATE_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-private-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PUBLIC_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-public-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "AD_IMPRESSION_TOKEN_SECRET"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "ad-impression-token-secret"
                          + version = "latest"
                        }
                    }
                }

              + liveness_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 10
                  + period_seconds        = 30
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }

              + ports {
                  + container_port = 8080
                  + name           = "http1"
                }

              + resources {
                  + cpu_idle = true
                  + limits   = {
                      + "cpu"    = "1000m"
                      + "memory" = "512Mi"
                    }
                }

              + startup_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 5
                  + period_seconds        = 10
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }
            }

          + scaling {
              + max_instance_count = 3
              + min_instance_count = 1
            }
        }
    }

  # google_cloud_run_v2_service_iam_member.public_access will be created
  + resource "google_cloud_run_v2_service_iam_member" "public_access" {
      + etag     = (known after apply)
      + id       = (known after apply)
      + location = "us-central1"
      + member   = "allUsers"
      + name     = "radio-backend"
      + project  = (known after apply)
      + role     = "roles/run.invoker"
    }

  # google_iam_workload_identity_pool.github will be created
  + resource "google_iam_workload_identity_pool" "github" {
      + description               = "Workload Identity Pool for GitHub Actions"
      + disabled                  = false
      + display_name              = "GitHub Actions Pool"
      + id                        = (known after apply)
      + name                      = (known after apply)
      + project                   = "radio-485022"
      + state                     = (known after apply)
      + workload_identity_pool_id = "github-actions-pool"
    }

  # google_iam_workload_identity_pool_provider.github will be created
  + resource "google_iam_workload_identity_pool_provider" "github" {
      + attribute_condition                = "assertion.repository == 'kedeinroga/radio-backend'"
      + attribute_mapping                  = {
          + "attribute.actor"      = "assertion.actor"
          + "attribute.aud"        = "assertion.aud"
          + "attribute.repository" = "assertion.repository"
          + "google.subject"       = "assertion.sub"
        }
      + description                        = "OIDC provider for GitHub Actions"
      + disabled                           = false
      + display_name                       = "GitHub Provider"
      + id                                 = (known after apply)
      + name                               = (known after apply)
      + project                            = "radio-485022"
      + state                              = (known after apply)
      + workload_identity_pool_id          = "github-actions-pool"
      + workload_identity_pool_provider_id = "github-provider"

      + oidc {
          + issuer_uri = "https://token.actions.githubusercontent.com"
        }
    }

  # google_project_iam_member.cloudrun_roles["roles/cloudtrace.agent"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/cloudtrace.agent"
    }

  # google_project_iam_member.cloudrun_roles["roles/logging.logWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/logging.logWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/monitoring.metricWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/monitoring.metricWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/run.invoker"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.invoker"
    }

  # google_project_iam_member.github_actions_roles["roles/artifactregistry.writer"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/artifactregistry.writer"
    }

  # google_project_iam_member.github_actions_roles["roles/iam.serviceAccountUser"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/iam.serviceAccountUser"
    }

  # google_project_iam_member.github_actions_roles["roles/run.admin"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.admin"
    }

  # google_project_iam_member.github_actions_roles["roles/secretmanager.secretAccessor"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/secretmanager.secretAccessor"
    }

  # google_project_service.apis["artifactregistry.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "artifactregistry.googleapis.com"
    }

  # google_project_service.apis["cloudresourcemanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "cloudresourcemanager.googleapis.com"
    }

  # google_project_service.apis["compute.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "compute.googleapis.com"
    }

  # google_project_service.apis["iam.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iam.googleapis.com"
    }

  # google_project_service.apis["iamcredentials.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iamcredentials.googleapis.com"
    }

  # google_project_service.apis["run.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "run.googleapis.com"
    }

  # google_project_service.apis["secretmanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "secretmanager.googleapis.com"
    }

  # google_secret_manager_secret.secrets["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "ad-impression-token-secret"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["database_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "database-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_private_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-private-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_public_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-public-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["redis_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "redis-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "ad-impression-token-secret"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["database_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "database-url"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-private-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-public-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["redis_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "redis-url"
    }

  # google_secret_manager_secret_version.secret_versions["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["database_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["redis_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_service_account.cloudrun will be created
  + resource "google_service_account" "cloudrun" {
      + account_id   = "radio-backend-sa"
      + description  = "Service account for running the radio-backend Cloud Run service"
      + disabled     = false
      + display_name = "Radio Backend Cloud Run Service Account"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account.github_actions will be created
  + resource "google_service_account" "github_actions" {
      + account_id   = "radio-backend-sa-github"
      + description  = "Service account for GitHub Actions CI/CD"
      + disabled     = false
      + display_name = "Radio Backend GitHub Actions"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account_iam_member.github_actions_impersonate will be created
  + resource "google_service_account_iam_member" "github_actions_impersonate" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.serviceAccountUser"
      + service_account_id = (known after apply)
    }

  # google_service_account_iam_member.github_workload_identity will be created
  + resource "google_service_account_iam_member" "github_workload_identity" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.workloadIdentityUser"
      + service_account_id = (known after apply)
    }

Plan: 41 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + artifact_registry_repository         = (known after apply)
  + artifact_registry_url                = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
  + cloudrun_service_account_email       = (known after apply)
  + cloudrun_service_name                = "radio-backend"
  + cloudrun_service_url                 = (known after apply)
  + environment                          = "production"
  + github_actions_secrets               = {
      + ARTIFACT_REGISTRY_URL      = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
      + CLOUDRUN_SERVICE_NAME      = "radio-backend"
      + GCP_PROJECT_ID             = "radio-485022"
      + GCP_REGION                 = "us-central1"
      + GCP_SERVICE_ACCOUNT        = (known after apply)
      + WORKLOAD_IDENTITY_PROVIDER = (known after apply)
    }
  + github_actions_service_account_email = (known after apply)
  + infrastructure_summary               = (known after apply)
  + project_id                           = "radio-485022"
  + region                               = "us-central1"
  + secret_instructions                  = <<-EOT
        Secrets have been created in Secret Manager. Add values using:
        
        # Database URL (Supabase)
        printf "your-database-url" | gcloud secrets versions add database-url --data-file=-
        
        # Redis URL (Upstash)
        printf "your-redis-url" | gcloud secrets versions add redis-url --data-file=-
        
        # JWT Private Key
        cat keys/jwt-private.pem | gcloud secrets versions add jwt-private-key --data-file=-
        
        # JWT Public Key
        cat keys/jwt-public.pem | gcloud secrets versions add jwt-public-key --data-file=-
        
        # Ad Impression Token Secret
        printf "your-ad-token-secret" | gcloud secrets versions add ad-impression-token-secret --data-file=-
    EOT
  + secret_names                         = {
      + ad_impression_token_secret = "ad-impression-token-secret"
      + database_url               = "database-url"
      + jwt_private_key            = "jwt-private-key"
      + jwt_public_key             = "jwt-public-key"
      + redis_url                  = "redis-url"
    }
  + workload_identity_pool               = (known after apply)
  + workload_identity_provider           = (known after apply)

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

Pusher: @kedeinroga, Action: pull_request

@github-actions
Copy link

Terraform Format and Style 🖌failure

Terraform Initialization ⚙️success

Terraform Validation 🤖success

Terraform Plan 📖success

Show Plan
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_artifact_registry_repository.repository will be created
  + resource "google_artifact_registry_repository" "repository" {
      + create_time      = (known after apply)
      + description      = "Docker repository for radio-backend container images"
      + effective_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + format           = "DOCKER"
      + id               = (known after apply)
      + labels           = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + location         = "us-central1"
      + mode             = "STANDARD_REPOSITORY"
      + name             = (known after apply)
      + project          = "radio-485022"
      + repository_id    = "radio-backend"
      + terraform_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + update_time      = (known after apply)

      + cleanup_policies {
          + action = "DELETE"
          + id     = "delete-old-untagged"

          + condition {
              + older_than            = "604800s"
              + package_name_prefixes = []
              + tag_prefixes          = []
              + tag_state             = "UNTAGGED"
              + version_name_prefixes = []
            }
        }
      + cleanup_policies {
          + action = "KEEP"
          + id     = "keep-recent"

          + most_recent_versions {
              + keep_count            = 10
              + package_name_prefixes = []
            }
        }
    }

  # google_artifact_registry_repository_iam_member.cloudrun_reader will be created
  + resource "google_artifact_registry_repository_iam_member" "cloudrun_reader" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.reader"
    }

  # google_artifact_registry_repository_iam_member.github_actions_writer will be created
  + resource "google_artifact_registry_repository_iam_member" "github_actions_writer" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.writer"
    }

  # google_cloud_run_v2_service.service will be created
  + resource "google_cloud_run_v2_service" "service" {
      + conditions              = (known after apply)
      + create_time             = (known after apply)
      + creator                 = (known after apply)
      + delete_time             = (known after apply)
      + effective_annotations   = (known after apply)
      + effective_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + etag                    = (known after apply)
      + expire_time             = (known after apply)
      + generation              = (known after apply)
      + id                      = (known after apply)
      + ingress                 = "INGRESS_TRAFFIC_ALL"
      + labels                  = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + last_modifier           = (known after apply)
      + latest_created_revision = (known after apply)
      + latest_ready_revision   = (known after apply)
      + launch_stage            = (known after apply)
      + location                = "us-central1"
      + name                    = "radio-backend"
      + observed_generation     = (known after apply)
      + project                 = "radio-485022"
      + reconciling             = (known after apply)
      + terminal_condition      = (known after apply)
      + terraform_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + traffic_statuses        = (known after apply)
      + uid                     = (known after apply)
      + update_time             = (known after apply)
      + uri                     = (known after apply)

      + template {
          + max_instance_request_concurrency = (known after apply)
          + service_account                  = (known after apply)
          + timeout                          = "300s"

          + containers {
              + image = "gcr.io/radio-485022/radio-backend:latest"

              + env {
                  + name  = "AD_CACHE_TTL"
                  + value = "10m"
                }
              + env {
                  + name  = "AD_FRAUD_SCORE_THRESHOLD"
                  + value = "0.7"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_DAILY"
                  + value = "30"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_HOURLY"
                  + value = "6"
                }
              + env {
                  + name  = "AD_IMPRESSION_TOKEN_MAX_AGE"
                  + value = "5m"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_REQUESTS"
                  + value = "50"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "ANALYTICS_BATCH_SIZE"
                  + value = "100"
                }
              + env {
                  + name  = "ANALYTICS_FLUSH_INTERVAL"
                  + value = "10s"
                }
              + env {
                  + name  = "BCRYPT_COST"
                  + value = "12"
                }
              + env {
                  + name  = "CORS_ALLOWED_HEADERS"
                  + value = "Content-Type,Authorization,X-Language,X-Request-ID"
                }
              + env {
                  + name  = "CORS_ALLOWED_METHODS"
                  + value = "GET,POST,PUT,DELETE,OPTIONS"
                }
              + env {
                  + name  = "CORS_ALLOWED_ORIGINS"
                  + value = "https://your-production-frontend.com"
                }
              + env {
                  + name  = "DEFAULT_LANGUAGE"
                  + value = "en"
                }
              + env {
                  + name  = "FEATURE_ANALYTICS"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_PREMIUM_CONTENT"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_VAULT_INTEGRATION"
                  + value = "false"
                }
              + env {
                  + name  = "JWT_EXPIRATION"
                  + value = "24h"
                }
              + env {
                  + name  = "JWT_REFRESH_EXPIRATION"
                  + value = "168h"
                }
              + env {
                  + name  = "LOG_FORMAT"
                  + value = "json"
                }
              + env {
                  + name  = "LOG_LEVEL"
                  + value = "info"
                }
              + env {
                  + name  = "RADIO_BROWSER_API_URL"
                  + value = "https://de1.api.radio-browser.info"
                }
              + env {
                  + name  = "RATE_LIMIT_REQUESTS"
                  + value = "100"
                }
              + env {
                  + name  = "RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "SERVER_BASE_URL"
                  + value = "https://radio-backend-296736956418.us-central1.run.app"
                }
              + env {
                  + name  = "SERVER_ENV"
                  + value = "production"
                }
              + env {
                  + name  = "SERVER_HOST"
                  + value = "0.0.0.0"
                }
              + env {
                  + name  = "SERVER_PORT"
                  + value = "8080"
                }
              + env {
                  + name  = "SERVER_TIMEOUT"
                  + value = "30s"
                }
              + env {
                  + name  = "SUPPORTED_LANGUAGES"
                  + value = "en,es,fr,de"
                }
              + env {
                  + name = "DATABASE_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "database-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "REDIS_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "redis-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PRIVATE_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-private-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PUBLIC_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-public-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "AD_IMPRESSION_TOKEN_SECRET"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "ad-impression-token-secret"
                          + version = "latest"
                        }
                    }
                }

              + liveness_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 10
                  + period_seconds        = 30
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }

              + ports {
                  + container_port = 8080
                  + name           = "http1"
                }

              + resources {
                  + cpu_idle = true
                  + limits   = {
                      + "cpu"    = "1000m"
                      + "memory" = "512Mi"
                    }
                }

              + startup_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 5
                  + period_seconds        = 10
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }
            }

          + scaling {
              + max_instance_count = 3
              + min_instance_count = 1
            }
        }
    }

  # google_cloud_run_v2_service_iam_member.public_access will be created
  + resource "google_cloud_run_v2_service_iam_member" "public_access" {
      + etag     = (known after apply)
      + id       = (known after apply)
      + location = "us-central1"
      + member   = "allUsers"
      + name     = "radio-backend"
      + project  = (known after apply)
      + role     = "roles/run.invoker"
    }

  # google_iam_workload_identity_pool.github will be created
  + resource "google_iam_workload_identity_pool" "github" {
      + description               = "Workload Identity Pool for GitHub Actions"
      + disabled                  = false
      + display_name              = "GitHub Actions Pool"
      + id                        = (known after apply)
      + name                      = (known after apply)
      + project                   = "radio-485022"
      + state                     = (known after apply)
      + workload_identity_pool_id = "github-actions-pool"
    }

  # google_iam_workload_identity_pool_provider.github will be created
  + resource "google_iam_workload_identity_pool_provider" "github" {
      + attribute_condition                = "assertion.repository == 'kedeinroga/radio-backend'"
      + attribute_mapping                  = {
          + "attribute.actor"      = "assertion.actor"
          + "attribute.aud"        = "assertion.aud"
          + "attribute.repository" = "assertion.repository"
          + "google.subject"       = "assertion.sub"
        }
      + description                        = "OIDC provider for GitHub Actions"
      + disabled                           = false
      + display_name                       = "GitHub Provider"
      + id                                 = (known after apply)
      + name                               = (known after apply)
      + project                            = "radio-485022"
      + state                              = (known after apply)
      + workload_identity_pool_id          = "github-actions-pool"
      + workload_identity_pool_provider_id = "github-provider"

      + oidc {
          + issuer_uri = "https://token.actions.githubusercontent.com"
        }
    }

  # google_project_iam_member.cloudrun_roles["roles/cloudtrace.agent"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/cloudtrace.agent"
    }

  # google_project_iam_member.cloudrun_roles["roles/logging.logWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/logging.logWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/monitoring.metricWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/monitoring.metricWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/run.invoker"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.invoker"
    }

  # google_project_iam_member.github_actions_roles["roles/artifactregistry.writer"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/artifactregistry.writer"
    }

  # google_project_iam_member.github_actions_roles["roles/iam.serviceAccountUser"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/iam.serviceAccountUser"
    }

  # google_project_iam_member.github_actions_roles["roles/run.admin"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.admin"
    }

  # google_project_iam_member.github_actions_roles["roles/secretmanager.secretAccessor"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/secretmanager.secretAccessor"
    }

  # google_project_service.apis["artifactregistry.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "artifactregistry.googleapis.com"
    }

  # google_project_service.apis["cloudresourcemanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "cloudresourcemanager.googleapis.com"
    }

  # google_project_service.apis["compute.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "compute.googleapis.com"
    }

  # google_project_service.apis["iam.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iam.googleapis.com"
    }

  # google_project_service.apis["iamcredentials.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iamcredentials.googleapis.com"
    }

  # google_project_service.apis["run.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "run.googleapis.com"
    }

  # google_project_service.apis["secretmanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "secretmanager.googleapis.com"
    }

  # google_secret_manager_secret.secrets["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "ad-impression-token-secret"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["database_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "database-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_private_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-private-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_public_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-public-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["redis_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "redis-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "ad-impression-token-secret"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["database_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "database-url"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-private-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-public-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["redis_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "redis-url"
    }

  # google_secret_manager_secret_version.secret_versions["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["database_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["redis_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_service_account.cloudrun will be created
  + resource "google_service_account" "cloudrun" {
      + account_id   = "radio-backend-sa"
      + description  = "Service account for running the radio-backend Cloud Run service"
      + disabled     = false
      + display_name = "Radio Backend Cloud Run Service Account"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account.github_actions will be created
  + resource "google_service_account" "github_actions" {
      + account_id   = "radio-backend-sa-github"
      + description  = "Service account for GitHub Actions CI/CD"
      + disabled     = false
      + display_name = "Radio Backend GitHub Actions"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account_iam_member.github_actions_impersonate will be created
  + resource "google_service_account_iam_member" "github_actions_impersonate" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.serviceAccountUser"
      + service_account_id = (known after apply)
    }

  # google_service_account_iam_member.github_workload_identity will be created
  + resource "google_service_account_iam_member" "github_workload_identity" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.workloadIdentityUser"
      + service_account_id = (known after apply)
    }

Plan: 41 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + artifact_registry_repository         = (known after apply)
  + artifact_registry_url                = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
  + cloudrun_service_account_email       = (known after apply)
  + cloudrun_service_name                = "radio-backend"
  + cloudrun_service_url                 = (known after apply)
  + environment                          = "production"
  + github_actions_secrets               = {
      + ARTIFACT_REGISTRY_URL      = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
      + CLOUDRUN_SERVICE_NAME      = "radio-backend"
      + GCP_PROJECT_ID             = "radio-485022"
      + GCP_REGION                 = "us-central1"
      + GCP_SERVICE_ACCOUNT        = (known after apply)
      + WORKLOAD_IDENTITY_PROVIDER = (known after apply)
    }
  + github_actions_service_account_email = (known after apply)
  + infrastructure_summary               = (known after apply)
  + project_id                           = "radio-485022"
  + region                               = "us-central1"
  + secret_instructions                  = <<-EOT
        Secrets have been created in Secret Manager. Add values using:
        
        # Database URL (Supabase)
        printf "your-database-url" | gcloud secrets versions add database-url --data-file=-
        
        # Redis URL (Upstash)
        printf "your-redis-url" | gcloud secrets versions add redis-url --data-file=-
        
        # JWT Private Key
        cat keys/jwt-private.pem | gcloud secrets versions add jwt-private-key --data-file=-
        
        # JWT Public Key
        cat keys/jwt-public.pem | gcloud secrets versions add jwt-public-key --data-file=-
        
        # Ad Impression Token Secret
        printf "your-ad-token-secret" | gcloud secrets versions add ad-impression-token-secret --data-file=-
    EOT
  + secret_names                         = {
      + ad_impression_token_secret = "ad-impression-token-secret"
      + database_url               = "database-url"
      + jwt_private_key            = "jwt-private-key"
      + jwt_public_key             = "jwt-public-key"
      + redis_url                  = "redis-url"
    }
  + workload_identity_pool               = (known after apply)
  + workload_identity_provider           = (known after apply)

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

Pusher: @kedeinroga, Action: pull_request

@github-actions
Copy link

Terraform Format and Style 🖌failure

Terraform Initialization ⚙️success

Terraform Validation 🤖success

Terraform Plan 📖success

Show Plan
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_artifact_registry_repository.repository will be created
  + resource "google_artifact_registry_repository" "repository" {
      + create_time      = (known after apply)
      + description      = "Docker repository for radio-backend container images"
      + effective_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + format           = "DOCKER"
      + id               = (known after apply)
      + labels           = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + location         = "us-central1"
      + mode             = "STANDARD_REPOSITORY"
      + name             = (known after apply)
      + project          = "radio-485022"
      + repository_id    = "radio-backend"
      + terraform_labels = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + update_time      = (known after apply)

      + cleanup_policies {
          + action = "DELETE"
          + id     = "delete-old-untagged"

          + condition {
              + older_than            = "604800s"
              + package_name_prefixes = []
              + tag_prefixes          = []
              + tag_state             = "UNTAGGED"
              + version_name_prefixes = []
            }
        }
      + cleanup_policies {
          + action = "KEEP"
          + id     = "keep-recent"

          + most_recent_versions {
              + keep_count            = 10
              + package_name_prefixes = []
            }
        }
    }

  # google_artifact_registry_repository_iam_member.cloudrun_reader will be created
  + resource "google_artifact_registry_repository_iam_member" "cloudrun_reader" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.reader"
    }

  # google_artifact_registry_repository_iam_member.github_actions_writer will be created
  + resource "google_artifact_registry_repository_iam_member" "github_actions_writer" {
      + etag       = (known after apply)
      + id         = (known after apply)
      + location   = "us-central1"
      + member     = (known after apply)
      + project    = (known after apply)
      + repository = (known after apply)
      + role       = "roles/artifactregistry.writer"
    }

  # google_cloud_run_v2_service.service will be created
  + resource "google_cloud_run_v2_service" "service" {
      + conditions              = (known after apply)
      + create_time             = (known after apply)
      + creator                 = (known after apply)
      + delete_time             = (known after apply)
      + effective_annotations   = (known after apply)
      + effective_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + etag                    = (known after apply)
      + expire_time             = (known after apply)
      + generation              = (known after apply)
      + id                      = (known after apply)
      + ingress                 = "INGRESS_TRAFFIC_ALL"
      + labels                  = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + last_modifier           = (known after apply)
      + latest_created_revision = (known after apply)
      + latest_ready_revision   = (known after apply)
      + launch_stage            = (known after apply)
      + location                = "us-central1"
      + name                    = "radio-backend"
      + observed_generation     = (known after apply)
      + project                 = "radio-485022"
      + reconciling             = (known after apply)
      + terminal_condition      = (known after apply)
      + terraform_labels        = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + traffic_statuses        = (known after apply)
      + uid                     = (known after apply)
      + update_time             = (known after apply)
      + uri                     = (known after apply)

      + template {
          + max_instance_request_concurrency = (known after apply)
          + service_account                  = (known after apply)
          + timeout                          = "300s"

          + containers {
              + image = "gcr.io/radio-485022/radio-backend:latest"

              + env {
                  + name  = "AD_CACHE_TTL"
                  + value = "10m"
                }
              + env {
                  + name  = "AD_FRAUD_SCORE_THRESHOLD"
                  + value = "0.7"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_DAILY"
                  + value = "30"
                }
              + env {
                  + name  = "AD_FREQUENCY_CAP_HOURLY"
                  + value = "6"
                }
              + env {
                  + name  = "AD_IMPRESSION_TOKEN_MAX_AGE"
                  + value = "5m"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_REQUESTS"
                  + value = "50"
                }
              + env {
                  + name  = "AD_RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "ANALYTICS_BATCH_SIZE"
                  + value = "100"
                }
              + env {
                  + name  = "ANALYTICS_FLUSH_INTERVAL"
                  + value = "10s"
                }
              + env {
                  + name  = "BCRYPT_COST"
                  + value = "12"
                }
              + env {
                  + name  = "CORS_ALLOWED_HEADERS"
                  + value = "Content-Type,Authorization,X-Language,X-Request-ID"
                }
              + env {
                  + name  = "CORS_ALLOWED_METHODS"
                  + value = "GET,POST,PUT,DELETE,OPTIONS"
                }
              + env {
                  + name  = "CORS_ALLOWED_ORIGINS"
                  + value = "https://your-production-frontend.com"
                }
              + env {
                  + name  = "DEFAULT_LANGUAGE"
                  + value = "en"
                }
              + env {
                  + name  = "FEATURE_ANALYTICS"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_PREMIUM_CONTENT"
                  + value = "true"
                }
              + env {
                  + name  = "FEATURE_VAULT_INTEGRATION"
                  + value = "false"
                }
              + env {
                  + name  = "JWT_EXPIRATION"
                  + value = "24h"
                }
              + env {
                  + name  = "JWT_REFRESH_EXPIRATION"
                  + value = "168h"
                }
              + env {
                  + name  = "LOG_FORMAT"
                  + value = "json"
                }
              + env {
                  + name  = "LOG_LEVEL"
                  + value = "info"
                }
              + env {
                  + name  = "RADIO_BROWSER_API_URL"
                  + value = "https://de1.api.radio-browser.info"
                }
              + env {
                  + name  = "RATE_LIMIT_REQUESTS"
                  + value = "100"
                }
              + env {
                  + name  = "RATE_LIMIT_WINDOW"
                  + value = "1m"
                }
              + env {
                  + name  = "SERVER_BASE_URL"
                  + value = "https://radio-backend-296736956418.us-central1.run.app"
                }
              + env {
                  + name  = "SERVER_ENV"
                  + value = "production"
                }
              + env {
                  + name  = "SERVER_HOST"
                  + value = "0.0.0.0"
                }
              + env {
                  + name  = "SERVER_PORT"
                  + value = "8080"
                }
              + env {
                  + name  = "SERVER_TIMEOUT"
                  + value = "30s"
                }
              + env {
                  + name  = "SUPPORTED_LANGUAGES"
                  + value = "en,es,fr,de"
                }
              + env {
                  + name = "DATABASE_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "database-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "REDIS_URL"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "redis-url"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PRIVATE_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-private-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "JWT_PUBLIC_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "jwt-public-key"
                          + version = "latest"
                        }
                    }
                }
              + env {
                  + name = "AD_IMPRESSION_TOKEN_SECRET"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "ad-impression-token-secret"
                          + version = "latest"
                        }
                    }
                }

              + liveness_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 10
                  + period_seconds        = 30
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }

              + ports {
                  + container_port = 8080
                  + name           = "http1"
                }

              + resources {
                  + cpu_idle = true
                  + limits   = {
                      + "cpu"    = "1000m"
                      + "memory" = "512Mi"
                    }
                }

              + startup_probe {
                  + failure_threshold     = 3
                  + initial_delay_seconds = 5
                  + period_seconds        = 10
                  + timeout_seconds       = 3

                  + http_get {
                      + path = "/health"
                      + port = 8080
                    }
                }
            }

          + scaling {
              + max_instance_count = 3
              + min_instance_count = 1
            }
        }
    }

  # google_cloud_run_v2_service_iam_member.public_access will be created
  + resource "google_cloud_run_v2_service_iam_member" "public_access" {
      + etag     = (known after apply)
      + id       = (known after apply)
      + location = "us-central1"
      + member   = "allUsers"
      + name     = "radio-backend"
      + project  = (known after apply)
      + role     = "roles/run.invoker"
    }

  # google_iam_workload_identity_pool.github will be created
  + resource "google_iam_workload_identity_pool" "github" {
      + description               = "Workload Identity Pool for GitHub Actions"
      + disabled                  = false
      + display_name              = "GitHub Actions Pool"
      + id                        = (known after apply)
      + name                      = (known after apply)
      + project                   = "radio-485022"
      + state                     = (known after apply)
      + workload_identity_pool_id = "github-actions-pool"
    }

  # google_iam_workload_identity_pool_provider.github will be created
  + resource "google_iam_workload_identity_pool_provider" "github" {
      + attribute_condition                = "assertion.repository == 'kedeinroga/radio-backend'"
      + attribute_mapping                  = {
          + "attribute.actor"      = "assertion.actor"
          + "attribute.aud"        = "assertion.aud"
          + "attribute.repository" = "assertion.repository"
          + "google.subject"       = "assertion.sub"
        }
      + description                        = "OIDC provider for GitHub Actions"
      + disabled                           = false
      + display_name                       = "GitHub Provider"
      + id                                 = (known after apply)
      + name                               = (known after apply)
      + project                            = "radio-485022"
      + state                              = (known after apply)
      + workload_identity_pool_id          = "github-actions-pool"
      + workload_identity_pool_provider_id = "github-provider"

      + oidc {
          + issuer_uri = "https://token.actions.githubusercontent.com"
        }
    }

  # google_project_iam_member.cloudrun_roles["roles/cloudtrace.agent"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/cloudtrace.agent"
    }

  # google_project_iam_member.cloudrun_roles["roles/logging.logWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/logging.logWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/monitoring.metricWriter"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/monitoring.metricWriter"
    }

  # google_project_iam_member.cloudrun_roles["roles/run.invoker"] will be created
  + resource "google_project_iam_member" "cloudrun_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.invoker"
    }

  # google_project_iam_member.github_actions_roles["roles/artifactregistry.writer"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/artifactregistry.writer"
    }

  # google_project_iam_member.github_actions_roles["roles/iam.serviceAccountUser"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/iam.serviceAccountUser"
    }

  # google_project_iam_member.github_actions_roles["roles/run.admin"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/run.admin"
    }

  # google_project_iam_member.github_actions_roles["roles/secretmanager.secretAccessor"] will be created
  + resource "google_project_iam_member" "github_actions_roles" {
      + etag    = (known after apply)
      + id      = (known after apply)
      + member  = (known after apply)
      + project = "radio-485022"
      + role    = "roles/secretmanager.secretAccessor"
    }

  # google_project_service.apis["artifactregistry.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "artifactregistry.googleapis.com"
    }

  # google_project_service.apis["cloudresourcemanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "cloudresourcemanager.googleapis.com"
    }

  # google_project_service.apis["compute.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "compute.googleapis.com"
    }

  # google_project_service.apis["iam.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iam.googleapis.com"
    }

  # google_project_service.apis["iamcredentials.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "iamcredentials.googleapis.com"
    }

  # google_project_service.apis["run.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "run.googleapis.com"
    }

  # google_project_service.apis["secretmanager.googleapis.com"] will be created
  + resource "google_project_service" "apis" {
      + disable_on_destroy = false
      + id                 = (known after apply)
      + project            = "radio-485022"
      + service            = "secretmanager.googleapis.com"
    }

  # google_secret_manager_secret.secrets["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "ad-impression-token-secret"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["database_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "database-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_private_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-private-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["jwt_public_key"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "jwt-public-key"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret.secrets["redis_url"] will be created
  + resource "google_secret_manager_secret" "secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + labels                = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }
      + name                  = (known after apply)
      + project               = "radio-485022"
      + secret_id             = "redis-url"
      + terraform_labels      = {
          + "environment" = "production"
          + "managed_by"  = "terraform"
          + "service"     = "radio-backend"
        }

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "ad-impression-token-secret"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["database_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "database-url"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-private-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "jwt-public-key"
    }

  # google_secret_manager_secret_iam_member.cloudrun_secret_access["redis_url"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloudrun_secret_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = (known after apply)
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = "redis-url"
    }

  # google_secret_manager_secret_version.secret_versions["ad_impression_token_secret"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["database_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_private_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["jwt_public_key"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_secret_manager_secret_version.secret_versions["redis_url"] will be created
  + resource "google_secret_manager_secret_version" "secret_versions" {
      + create_time           = (known after apply)
      + deletion_policy       = "DELETE"
      + destroy_time          = (known after apply)
      + enabled               = true
      + id                    = (known after apply)
      + is_secret_data_base64 = false
      + name                  = (known after apply)
      + secret                = (known after apply)
      + secret_data           = (sensitive value)
      + version               = (known after apply)
    }

  # google_service_account.cloudrun will be created
  + resource "google_service_account" "cloudrun" {
      + account_id   = "radio-backend-sa"
      + description  = "Service account for running the radio-backend Cloud Run service"
      + disabled     = false
      + display_name = "Radio Backend Cloud Run Service Account"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account.github_actions will be created
  + resource "google_service_account" "github_actions" {
      + account_id   = "radio-backend-sa-github"
      + description  = "Service account for GitHub Actions CI/CD"
      + disabled     = false
      + display_name = "Radio Backend GitHub Actions"
      + email        = (known after apply)
      + id           = (known after apply)
      + member       = (known after apply)
      + name         = (known after apply)
      + project      = "radio-485022"
      + unique_id    = (known after apply)
    }

  # google_service_account_iam_member.github_actions_impersonate will be created
  + resource "google_service_account_iam_member" "github_actions_impersonate" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.serviceAccountUser"
      + service_account_id = (known after apply)
    }

  # google_service_account_iam_member.github_workload_identity will be created
  + resource "google_service_account_iam_member" "github_workload_identity" {
      + etag               = (known after apply)
      + id                 = (known after apply)
      + member             = (known after apply)
      + role               = "roles/iam.workloadIdentityUser"
      + service_account_id = (known after apply)
    }

Plan: 41 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + artifact_registry_repository         = (known after apply)
  + artifact_registry_url                = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
  + cloudrun_service_account_email       = (known after apply)
  + cloudrun_service_name                = "radio-backend"
  + cloudrun_service_url                 = (known after apply)
  + environment                          = "production"
  + github_actions_secrets               = {
      + ARTIFACT_REGISTRY_URL      = "us-central1-docker.pkg.dev/radio-485022/radio-backend"
      + CLOUDRUN_SERVICE_NAME      = "radio-backend"
      + GCP_PROJECT_ID             = "radio-485022"
      + GCP_REGION                 = "us-central1"
      + GCP_SERVICE_ACCOUNT        = (known after apply)
      + WORKLOAD_IDENTITY_PROVIDER = (known after apply)
    }
  + github_actions_service_account_email = (known after apply)
  + infrastructure_summary               = (known after apply)
  + project_id                           = "radio-485022"
  + region                               = "us-central1"
  + secret_instructions                  = <<-EOT
        Secrets have been created in Secret Manager. Add values using:
        
        # Database URL (Supabase)
        printf "your-database-url" | gcloud secrets versions add database-url --data-file=-
        
        # Redis URL (Upstash)
        printf "your-redis-url" | gcloud secrets versions add redis-url --data-file=-
        
        # JWT Private Key
        cat keys/jwt-private.pem | gcloud secrets versions add jwt-private-key --data-file=-
        
        # JWT Public Key
        cat keys/jwt-public.pem | gcloud secrets versions add jwt-public-key --data-file=-
        
        # Ad Impression Token Secret
        printf "your-ad-token-secret" | gcloud secrets versions add ad-impression-token-secret --data-file=-
    EOT
  + secret_names                         = {
      + ad_impression_token_secret = "ad-impression-token-secret"
      + database_url               = "database-url"
      + jwt_private_key            = "jwt-private-key"
      + jwt_public_key             = "jwt-public-key"
      + redis_url                  = "redis-url"
    }
  + workload_identity_pool               = (known after apply)
  + workload_identity_provider           = (known after apply)

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't
guarantee to take exactly these actions if you run "terraform apply" now.

Pusher: @kedeinroga, Action: pull_request

@kedeinroga kedeinroga merged commit 731da09 into main Jan 22, 2026
10 checks passed
@kedeinroga kedeinroga deleted the refactor/database-optimization branch January 22, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant