Add comprehensive Terraform infrastructure with Firebase automation
- Create Firebase project, web app, and Firestore database - Automate Firebase Authentication with email templates - Configure security rules for user data isolation - Support Cloud Run and App Engine hosting options - Add professional email templates for password reset and verification - Include deployment scripts and comprehensive documentation - Implement service accounts with minimal required permissions - Add Docker configuration for containerized deployment 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
118
terraform/modules/app_engine/main.tf
Normal file
118
terraform/modules/app_engine/main.tf
Normal file
@@ -0,0 +1,118 @@
|
||||
# Enable App Engine Admin API
|
||||
resource "google_project_service" "appengine" {
|
||||
project = var.gcp_project_id
|
||||
service = "appengine.googleapis.com"
|
||||
}
|
||||
|
||||
# App Engine Application
|
||||
resource "google_app_engine_application" "app" {
|
||||
project = var.gcp_project_id
|
||||
location_id = var.gcp_region
|
||||
depends_on = [google_project_service.appengine]
|
||||
}
|
||||
|
||||
# App Engine Service for Flask app
|
||||
resource "google_app_engine_standard_app_version" "flask_app" {
|
||||
project = var.gcp_project_id
|
||||
service = "default"
|
||||
version_id = "${var.app_name}-v1"
|
||||
|
||||
runtime = "python311"
|
||||
|
||||
entrypoint {
|
||||
command = "gunicorn -b :$PORT app:app"
|
||||
}
|
||||
|
||||
deployment {
|
||||
zip {
|
||||
source_url = google_storage_bucket_object.app_source_zip.output_uri
|
||||
}
|
||||
}
|
||||
|
||||
env_variables = {
|
||||
FLASK_SECRET_KEY = var.flask_secret_key
|
||||
FIREBASE_PROJECT_ID = var.firebase_project_id
|
||||
GOOGLE_APPLICATION_CREDENTIALS = "/etc/secrets/service-account.json"
|
||||
FILEVINE_CLIENT_ID = var.filevine_client_id
|
||||
FILEVINE_CLIENT_SECRET = var.filevine_client_secret
|
||||
FILEVINE_PERSONAL_ACCESS_TOKEN = var.filevine_pat
|
||||
FILEVINE_ORG_ID = var.filevine_org_id
|
||||
FILEVINE_USER_ID = var.filevine_user_id
|
||||
}
|
||||
|
||||
# Service account
|
||||
service_account = var.service_account_email
|
||||
|
||||
# Resources
|
||||
resources {
|
||||
cpu = 1
|
||||
memory_gb = 0.5
|
||||
disk_gb = 0.5
|
||||
}
|
||||
|
||||
# Automatic scaling
|
||||
automatic_scaling {
|
||||
min_idle_instances = 0
|
||||
max_idle_instances = 1
|
||||
min_pending_latency = "automatic"
|
||||
max_pending_latency = "automatic"
|
||||
max_concurrent_requests = 80
|
||||
}
|
||||
|
||||
# Health check
|
||||
health_check {
|
||||
enable_health_check = true
|
||||
check_path = "/"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_storage_bucket_object.app_source_zip,
|
||||
google_secret_manager_secret_version.service_account_key
|
||||
]
|
||||
}
|
||||
|
||||
# Make App Engine service publicly accessible
|
||||
resource "google_app_engine_firewall_rule" "allow_all" {
|
||||
project = var.gcp_project_id
|
||||
action = "ALLOW"
|
||||
priority = "1"
|
||||
|
||||
source_range = "*"
|
||||
}
|
||||
|
||||
# Cloud Storage bucket for app source code
|
||||
resource "google_storage_bucket" "app_source" {
|
||||
name = "${var.app_name}-source-${var.gcp_project_id}"
|
||||
location = var.gcp_region
|
||||
force_destroy = true
|
||||
|
||||
uniform_bucket_level_access = true
|
||||
}
|
||||
|
||||
# Upload app source code
|
||||
resource "google_storage_bucket_object" "app_source_zip" {
|
||||
name = "app-source.zip"
|
||||
bucket = google_storage_bucket.app_source.name
|
||||
source = var.app_source_zip_path
|
||||
}
|
||||
|
||||
# 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 = true
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Output the app URL
|
||||
output "app_url" {
|
||||
description = "App Engine application URL"
|
||||
value = "https://${google_app_engine_application.app.default_hostname}"
|
||||
}
|
||||
71
terraform/modules/app_engine/variables.tf
Normal file
71
terraform/modules/app_engine/variables.tf
Normal file
@@ -0,0 +1,71 @@
|
||||
variable "app_name" {
|
||||
description = "Name of the application"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_project_id" {
|
||||
description = "GCP Project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_region" {
|
||||
description = "GCP region"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "app_source_zip_path" {
|
||||
description = "Path to the app source code zip file"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "firebase_project_id" {
|
||||
description = "Firebase project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "flask_secret_key" {
|
||||
description = "Flask secret key"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "service_account_email" {
|
||||
description = "Service account email for the App Engine service"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "service_account_key_data" {
|
||||
description = "Service account key JSON data"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_id" {
|
||||
description = "Filevine client ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_secret" {
|
||||
description = "Filevine client secret"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_pat" {
|
||||
description = "Filevine personal access token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_org_id" {
|
||||
description = "Filevine organization ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_user_id" {
|
||||
description = "Filevine user ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
144
terraform/modules/cloud_run/main.tf
Normal file
144
terraform/modules/cloud_run/main.tf
Normal 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 = true
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
71
terraform/modules/cloud_run/variables.tf
Normal file
71
terraform/modules/cloud_run/variables.tf
Normal file
@@ -0,0 +1,71 @@
|
||||
variable "app_name" {
|
||||
description = "Name of the application"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_project_id" {
|
||||
description = "GCP Project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "gcp_region" {
|
||||
description = "GCP region"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "container_image" {
|
||||
description = "Docker image for the Flask app"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "firebase_project_id" {
|
||||
description = "Firebase project ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "flask_secret_key" {
|
||||
description = "Flask secret key"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "service_account_email" {
|
||||
description = "Service account email for the Cloud Run service"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "service_account_key_data" {
|
||||
description = "Service account key JSON data"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_id" {
|
||||
description = "Filevine client ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_client_secret" {
|
||||
description = "Filevine client secret"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_pat" {
|
||||
description = "Filevine personal access token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_org_id" {
|
||||
description = "Filevine organization ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "filevine_user_id" {
|
||||
description = "Filevine user ID"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
Reference in New Issue
Block a user