213 lines
No EOL
5.3 KiB
HCL
213 lines
No EOL
5.3 KiB
HCL
terraform {
|
|
required_version = ">= 1.0"
|
|
required_providers {
|
|
google = {
|
|
source = "hashicorp/google"
|
|
version = "~> 5.0"
|
|
}
|
|
}
|
|
}
|
|
|
|
provider "google" {
|
|
project = var.project_id
|
|
region = var.region
|
|
}
|
|
|
|
# Variables
|
|
variable "project_id" {
|
|
description = "Google Cloud Project ID"
|
|
type = string
|
|
}
|
|
|
|
variable "region" {
|
|
description = "Google Cloud Region"
|
|
type = string
|
|
default = "us-central1"
|
|
}
|
|
|
|
variable "domain_name" {
|
|
description = "Custom domain for the application"
|
|
type = string
|
|
default = ""
|
|
}
|
|
|
|
variable "environment" {
|
|
description = "Environment (dev, staging, prod)"
|
|
type = string
|
|
default = "prod"
|
|
}
|
|
|
|
# Enable required APIs
|
|
resource "google_project_service" "required_apis" {
|
|
for_each = toset([
|
|
"compute.googleapis.com",
|
|
"storage.googleapis.com",
|
|
"dns.googleapis.com",
|
|
"certificatemanager.googleapis.com"
|
|
])
|
|
|
|
service = each.value
|
|
disable_on_destroy = false
|
|
}
|
|
|
|
# GCS bucket for hosting the SPA
|
|
resource "google_storage_bucket" "spa_hosting" {
|
|
name = "accessible-video-spa-${var.project_id}"
|
|
location = "US" # Multi-regional for global CDN
|
|
storage_class = "STANDARD"
|
|
|
|
uniform_bucket_level_access = true
|
|
|
|
website {
|
|
main_page_suffix = "index.html"
|
|
not_found_page = "index.html" # SPA routing fallback
|
|
}
|
|
|
|
cors {
|
|
origin = ["*"]
|
|
method = ["GET", "HEAD", "OPTIONS"]
|
|
response_header = ["Content-Type", "Cache-Control"]
|
|
max_age_seconds = 3600
|
|
}
|
|
|
|
# Cache control for static assets
|
|
lifecycle_rule {
|
|
condition {
|
|
age = 365
|
|
}
|
|
action {
|
|
type = "Delete"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Make bucket public for website hosting
|
|
resource "google_storage_bucket_iam_member" "spa_public_access" {
|
|
bucket = google_storage_bucket.spa_hosting.name
|
|
role = "roles/storage.objectViewer"
|
|
member = "allUsers"
|
|
}
|
|
|
|
# Reserve external IP address
|
|
resource "google_compute_global_address" "spa_ip" {
|
|
name = "accessible-video-spa-ip"
|
|
}
|
|
|
|
# Backend service pointing to the storage bucket
|
|
resource "google_compute_backend_bucket" "spa_backend" {
|
|
name = "accessible-video-spa-backend"
|
|
bucket_name = google_storage_bucket.spa_hosting.name
|
|
enable_cdn = true
|
|
|
|
cdn_policy {
|
|
cache_mode = "CACHE_ALL_STATIC"
|
|
default_ttl = 3600
|
|
max_ttl = 86400
|
|
client_ttl = 7200
|
|
negative_caching = true
|
|
|
|
cache_key_policy {
|
|
include_host = true
|
|
include_protocol = true
|
|
include_query_string = false
|
|
}
|
|
}
|
|
}
|
|
|
|
# URL map for routing
|
|
resource "google_compute_url_map" "spa_url_map" {
|
|
name = "accessible-video-spa-url-map"
|
|
|
|
default_service = google_compute_backend_bucket.spa_backend.id
|
|
|
|
# API requests go to Cloud Run
|
|
host_rule {
|
|
hosts = [var.domain_name != "" ? var.domain_name : "*.googleapis.com"]
|
|
path_matcher = "api-matcher"
|
|
}
|
|
|
|
path_matcher {
|
|
name = "api-matcher"
|
|
default_service = google_compute_backend_bucket.spa_backend.id
|
|
|
|
path_rule {
|
|
paths = ["/api/*"]
|
|
service = "https://accessible-video-api-${random_id.suffix.hex}-uc.a.run.app"
|
|
}
|
|
}
|
|
}
|
|
|
|
# SSL certificate (managed)
|
|
resource "google_compute_managed_ssl_certificate" "spa_cert" {
|
|
count = var.domain_name != "" ? 1 : 0
|
|
name = "accessible-video-spa-cert"
|
|
|
|
managed {
|
|
domains = [var.domain_name]
|
|
}
|
|
}
|
|
|
|
# HTTPS proxy
|
|
resource "google_compute_target_https_proxy" "spa_https_proxy" {
|
|
name = "accessible-video-spa-https-proxy"
|
|
url_map = google_compute_url_map.spa_url_map.id
|
|
|
|
ssl_certificates = var.domain_name != "" ? [google_compute_managed_ssl_certificate.spa_cert[0].id] : []
|
|
}
|
|
|
|
# HTTP proxy for redirect to HTTPS
|
|
resource "google_compute_target_http_proxy" "spa_http_proxy" {
|
|
name = "accessible-video-spa-http-proxy"
|
|
url_map = google_compute_url_map.spa_redirect.id
|
|
}
|
|
|
|
# URL map for HTTP to HTTPS redirect
|
|
resource "google_compute_url_map" "spa_redirect" {
|
|
name = "accessible-video-spa-redirect"
|
|
|
|
default_url_redirect {
|
|
https_redirect = true
|
|
strip_query = false
|
|
}
|
|
}
|
|
|
|
# Global forwarding rules
|
|
resource "google_compute_global_forwarding_rule" "spa_https" {
|
|
name = "accessible-video-spa-https"
|
|
target = google_compute_target_https_proxy.spa_https_proxy.id
|
|
port_range = "443"
|
|
ip_address = google_compute_global_address.spa_ip.address
|
|
}
|
|
|
|
resource "google_compute_global_forwarding_rule" "spa_http" {
|
|
name = "accessible-video-spa-http"
|
|
target = google_compute_target_http_proxy.spa_http_proxy.id
|
|
port_range = "80"
|
|
ip_address = google_compute_global_address.spa_ip.address
|
|
}
|
|
|
|
# Random suffix for unique naming
|
|
resource "random_id" "suffix" {
|
|
byte_length = 8
|
|
}
|
|
|
|
# Outputs
|
|
output "spa_bucket_name" {
|
|
description = "Name of the GCS bucket hosting the SPA"
|
|
value = google_storage_bucket.spa_hosting.name
|
|
}
|
|
|
|
output "spa_url" {
|
|
description = "URL of the deployed SPA"
|
|
value = var.domain_name != "" ? "https://${var.domain_name}" : "https://${google_compute_global_address.spa_ip.address}"
|
|
}
|
|
|
|
output "spa_ip_address" {
|
|
description = "External IP address for the SPA"
|
|
value = google_compute_global_address.spa_ip.address
|
|
}
|
|
|
|
output "spa_bucket_url" {
|
|
description = "Direct GCS bucket URL"
|
|
value = "https://storage.googleapis.com/${google_storage_bucket.spa_hosting.name}"
|
|
} |