This commit is contained in:
2026-01-27 22:06:18 -08:00
parent 6cde9ab75f
commit dc6c24ca6d
8 changed files with 338 additions and 7 deletions

152
terraform/main.tf Normal file
View File

@@ -0,0 +1,152 @@
terraform {
required_providers {
google-beta = {
source = "hashicorp/google-beta"
version = "~> 6.0"
}
}
}
provider "google" {
project = var.gcp_project_id
region = var.gcp_region
}
# Firebase Project Setup
resource "google_firebase_project" "default" {
provider = google-beta
project = var.gcp_project_id
}
# Firebase Web App
resource "google_firebase_web_app" "rothbard_portal" {
provider = google-beta
project = google_firebase_project.default.project
display_name = "Rothbard Client Portal"
app_urls = ["https://${var.domain_name}"]
}
# Firestore Database
resource "google_firestore_database" "default" {
provider = google-beta
project = var.gcp_project_id
name = "(default)"
location_id = var.firestore_location
type = "FIRESTORE_NATIVE"
delete_protection_state = "DELETE_PROTECTION_DISABLED"
}
# Firebase Authentication - Complete Configuration
resource "google_firebase_auth_config" "default" {
provider = google-beta
project = var.gcp_project_id
sign_in_options {
email {
enabled = true
password_required = true
}
# Disable other providers for security
phone {
enabled = false
}
google {
enabled = var.enable_google_signin
}
facebook {
enabled = false
}
apple {
enabled = false
}
}
# Email configuration
email {
reset_password_template {
from_email_address = var.auth_from_email
from_display_name = var.auth_from_name
reply_to = var.auth_reply_to
subject = "Reset your Rothbard Law Group password"
html = file("${path.module}/templates/reset_password.html")
text = file("${path.module}/templates/reset_password.txt")
}
email_verification_template {
from_email_address = var.auth_from_email
from_display_name = var.auth_from_name
reply_to = var.auth_reply_to
subject = "Verify your Rothbard Law Group account"
html = file("${path.module}/templates/email_verification.html")
text = file("${path.module}/templates/email_verification.txt")
}
}
# Security settings
sign_in {
allow_duplicate_emails = false
}
# Multi-factor authentication (disabled for simplicity)
multi_factor_auth {
enabled = false
}
# Anonymous user access (disabled)
anonymous {
enabled = false
}
}
# Service Account for the Flask App
resource "google_service_account" "flask_app" {
account_id = "rothbard-flask-app"
display_name = "Rothbard Flask App Service Account"
}
# IAM permissions for the Flask App
resource "google_project_iam_member" "firestore_access" {
project = var.gcp_project_id
role = "roles/datastore.user"
member = "serviceAccount:${google_service_account.flask_app.email}"
}
resource "google_project_iam_member" "firebase_admin" {
project = var.gcp_project_id
role = "roles/firebase.admin"
member = "serviceAccount:${google_service_account.flask_app.email}"
}
# Firestore Security Rules - Note: Firestore security policies are managed through Firestore rules
# This section is commented out as google_firestore_security_policy is not supported
# Security rules should be managed through firestore.rules file or Firebase console
# Firebase Hosting (optional - for static assets)
resource "google_firebase_hosting_site" "default" {
provider = google-beta
project = var.gcp_project_id
site_id = "rothbard-portal"
}
# Output important values
output "firebase_web_app_id" {
description = "Firebase Web App ID"
value = google_firebase_web_app.rothbard_portal.app_id
}
output "firebase_project_id" {
description = "Firebase Project ID"
value = google_firebase_project.default.project
}
output "service_account_email" {
description = "Service account email for Flask app"
value = google_service_account.flask_app.email
}

View File

@@ -0,0 +1,144 @@
# Cloud Run Service for Flask App
resource "google_cloud_run_service" "flask_app" {
name = "${var.app_name}-service"
location = var.gcp_region
template {
spec {
containers {
image = var.container_image
# Environment variables for the Flask app
env {
name = "FLASK_SECRET_KEY"
value = var.flask_secret_key
}
env {
name = "FIREBASE_PROJECT_ID"
value = var.firebase_project_id
}
env {
name = "GOOGLE_APPLICATION_CREDENTIALS"
value = "/etc/secrets/service-account.json"
}
# Filevine API credentials
env {
name = "FILEVINE_CLIENT_ID"
value = var.filevine_client_id
}
env {
name = "FILEVINE_CLIENT_SECRET"
value = var.filevine_client_secret
}
env {
name = "FILEVINE_PERSONAL_ACCESS_TOKEN"
value = var.filevine_pat
}
env {
name = "FILEVINE_ORG_ID"
value = var.filevine_org_id
}
env {
name = "FILEVINE_USER_ID"
value = var.filevine_user_id
}
# Memory and CPU limits
resources {
limits = {
cpu = "1000m"
memory = "512Mi"
}
}
# Mount service account key
volume_mount {
name = "service-account-key"
mount_path = "/etc/secrets"
read_only = true
}
}
# Service account for the container
service_account_name = var.service_account_email
# Volumes
volumes {
name = "service-account-key"
secret {
secret_name = google_secret_manager_secret.service_account_key.secret_id
items {
key = "latest"
path = "service-account.json"
}
}
}
# Allow unauthenticated access
container_concurrency = 100
timeout_seconds = 300
}
# Traffic settings
metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "10"
"autoscaling.knative.dev/minScale" = "1"
"run.googleapis.com/ingress" = "all"
}
}
}
traffic {
percent = 100
latest_revision = true
}
depends_on = [google_secret_manager_secret_version.service_account_key]
}
# Make Cloud Run service publicly accessible
resource "google_cloud_run_service_iam_member" "public" {
location = google_cloud_run_service.flask_app.location
project = google_cloud_run_service.flask_app.project
service = google_cloud_run_service.flask_app.name
role = "roles/run.invoker"
member = "allUsers"
}
# Store service account key in Secret Manager
resource "google_secret_manager_secret" "service_account_key" {
project = var.gcp_project_id
secret_id = "${var.app_name}-service-account-key"
replication {
automatic {}
}
}
resource "google_secret_manager_secret_version" "service_account_key" {
secret = google_secret_manager_secret.service_account_key.id
secret_data = var.service_account_key_data
}
# Cloud Storage bucket for container storage (if needed)
resource "google_storage_bucket" "app_storage" {
name = "${var.app_name}-storage-${var.gcp_project_id}"
location = var.gcp_region
force_destroy = true
uniform_bucket_level_access = true
}
# Output the service URL
output "service_url" {
description = "Cloud Run service URL"
value = google_cloud_run_service.flask_app.status[0].url
}