add Terraform + Ansible infrastructure for GCP deployment
Provisions e2-micro VM (us-east1-b, free tier) with static IP, SSH and web firewall rules, Docker + Caddy startup script, and IAM bindings for Firestore and GCS access via ADC. Imports existing drb-calls bucket and c2-server Firestore database into state. Ansible roles handle first-time setup (swap, docker group) and all subsequent deploys via rsync + docker compose, with secrets managed via Ansible Vault. DNS stays on AWS Route 53.
This commit is contained in:
+12
@@ -5,6 +5,18 @@ drb-server-discord-bot/.env
|
|||||||
drb-frontend/.env
|
drb-frontend/.env
|
||||||
drb-c2-core/gcp-key.json
|
drb-c2-core/gcp-key.json
|
||||||
|
|
||||||
|
# Terraform
|
||||||
|
infra/.terraform/
|
||||||
|
infra/terraform.tfstate
|
||||||
|
infra/terraform.tfstate.backup
|
||||||
|
infra/terraform.tfstate.*.backup
|
||||||
|
infra/.terraform.lock.hcl
|
||||||
|
infra/terraform.tfvars
|
||||||
|
infra/tf.log
|
||||||
|
infra/ansible/inventory.ini
|
||||||
|
infra/ansible/group_vars/all.yml
|
||||||
|
infra/ansible/vault.yml
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
.PHONY: tf-init tf-plan tf-apply tf-destroy deploy setup-ansible
|
||||||
|
|
||||||
|
ANSIBLE_DIR = ansible
|
||||||
|
INVENTORY = $(ANSIBLE_DIR)/inventory.ini
|
||||||
|
|
||||||
|
# ── Terraform ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
tf-init:
|
||||||
|
terraform init
|
||||||
|
|
||||||
|
tf-plan:
|
||||||
|
terraform plan
|
||||||
|
|
||||||
|
tf-apply:
|
||||||
|
terraform apply
|
||||||
|
@echo ""
|
||||||
|
@echo "Server IP: $$(terraform output -raw server_ip)"
|
||||||
|
@echo "Update $(INVENTORY) with this IP, then run: make deploy"
|
||||||
|
|
||||||
|
tf-destroy:
|
||||||
|
@echo "WARNING: This will destroy the VM and all data on it."
|
||||||
|
@read -p "Type 'yes' to confirm: " confirm && [ "$$confirm" = "yes" ] && terraform destroy
|
||||||
|
|
||||||
|
# ── Ansible ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# First-time setup: waits for Docker, clones repo, starts stack.
|
||||||
|
setup:
|
||||||
|
ansible-playbook -i $(INVENTORY) $(ANSIBLE_DIR)/site.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# Update deploy: sync code + restart changed containers. Run this after every push.
|
||||||
|
deploy:
|
||||||
|
ansible-playbook -i $(INVENTORY) $(ANSIBLE_DIR)/deploy.yml --ask-vault-pass
|
||||||
|
|
||||||
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
ip:
|
||||||
|
@terraform output -raw server_ip
|
||||||
|
|
||||||
|
ssh:
|
||||||
|
ssh drb@$$(terraform output -raw server_ip)
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
# Lightweight update deploy — runs in ~60s.
|
||||||
|
# Use this for every code push after the initial site.yml run.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook -i inventory.ini deploy.yml --ask-vault-pass
|
||||||
|
|
||||||
|
- name: Deploy DRB update
|
||||||
|
hosts: drb
|
||||||
|
become: true
|
||||||
|
vars_files:
|
||||||
|
- vault.yml
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- deploy
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copy to group_vars/all.yml — safe to commit (no secrets here).
|
||||||
|
|
||||||
|
domain: example.com # must match Terraform var.domain
|
||||||
|
app_dir: /opt/drb
|
||||||
|
ssh_user: drb
|
||||||
|
|
||||||
|
# Path to the local repo root on your machine (used for rsync).
|
||||||
|
# Trailing slash is intentional — rsync copies contents, not the folder itself.
|
||||||
|
local_repo_path: "/path/to/Version 5C/Server/"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Copy to inventory.ini and replace SERVER_IP with the Terraform output.
|
||||||
|
# Get it with: cd ../terraform && terraform output server_ip
|
||||||
|
|
||||||
|
[drb]
|
||||||
|
SERVER_IP ansible_user=drb ansible_ssh_private_key_file=~/.ssh/id_ed25519
|
||||||
|
|
||||||
|
[drb:vars]
|
||||||
|
ansible_python_interpreter=/usr/bin/python3
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
# Sync code, write secrets, bring the stack up.
|
||||||
|
|
||||||
|
- name: Sync app code from local machine
|
||||||
|
synchronize:
|
||||||
|
src: "{{ local_repo_path }}"
|
||||||
|
dest: "{{ app_dir }}/"
|
||||||
|
delete: true
|
||||||
|
recursive: true
|
||||||
|
rsync_opts:
|
||||||
|
- "--exclude=.git"
|
||||||
|
- "--exclude=**/__pycache__"
|
||||||
|
- "--exclude=**/.env"
|
||||||
|
- "--exclude=**/gcp-key.json"
|
||||||
|
- "--exclude=**/node_modules"
|
||||||
|
- "--exclude=drb-c2-core/gcp-key.json"
|
||||||
|
- "--exclude=infra/"
|
||||||
|
become: false # rsync runs as the SSH user, not root
|
||||||
|
|
||||||
|
- name: Set ownership of app directory
|
||||||
|
file:
|
||||||
|
path: "{{ app_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ssh_user }}"
|
||||||
|
group: "{{ ssh_user }}"
|
||||||
|
recurse: true
|
||||||
|
|
||||||
|
# No gcp-key.json needed — the VM authenticates to GCS/Firestore via ADC
|
||||||
|
# (GCE metadata server). IAM roles are granted by Terraform.
|
||||||
|
|
||||||
|
- name: Template top-level .env (docker-compose MQTT creds)
|
||||||
|
template:
|
||||||
|
src: root.env.j2
|
||||||
|
dest: "{{ app_dir }}/.env"
|
||||||
|
owner: "{{ ssh_user }}"
|
||||||
|
group: "{{ ssh_user }}"
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Template c2-core .env
|
||||||
|
template:
|
||||||
|
src: c2-core.env.j2
|
||||||
|
dest: "{{ app_dir }}/drb-c2-core/.env"
|
||||||
|
owner: "{{ ssh_user }}"
|
||||||
|
group: "{{ ssh_user }}"
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Template discord-bot .env
|
||||||
|
template:
|
||||||
|
src: discord-bot.env.j2
|
||||||
|
dest: "{{ app_dir }}/drb-server-discord-bot/.env"
|
||||||
|
owner: "{{ ssh_user }}"
|
||||||
|
group: "{{ ssh_user }}"
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Template frontend .env
|
||||||
|
template:
|
||||||
|
src: frontend.env.j2
|
||||||
|
dest: "{{ app_dir }}/drb-frontend/.env"
|
||||||
|
owner: "{{ ssh_user }}"
|
||||||
|
group: "{{ ssh_user }}"
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Deploy Caddyfile
|
||||||
|
template:
|
||||||
|
src: Caddyfile.j2
|
||||||
|
dest: /etc/caddy/Caddyfile
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Reload Caddy
|
||||||
|
|
||||||
|
- name: Bring the stack up (builds images if changed)
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: "{{ app_dir }}"
|
||||||
|
build: always
|
||||||
|
state: present
|
||||||
|
pull: never
|
||||||
|
become: true
|
||||||
|
environment:
|
||||||
|
DOCKER_BUILDKIT: "1"
|
||||||
|
|
||||||
|
- name: Prune unused Docker images
|
||||||
|
community.docker.docker_prune:
|
||||||
|
images: true
|
||||||
|
images_filters:
|
||||||
|
dangling: true
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Managed by Ansible — do not edit manually on the server.
|
||||||
|
|
||||||
|
api.{{ domain }} {
|
||||||
|
reverse_proxy localhost:8888 {
|
||||||
|
header_up X-Forwarded-For {remote_host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.{{ domain }} {
|
||||||
|
reverse_proxy localhost:3000 {
|
||||||
|
header_up X-Forwarded-For {remote_host}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# drb-c2-core environment — Managed by Ansible. Do not edit manually.
|
||||||
|
|
||||||
|
MQTT_BROKER=mosquitto
|
||||||
|
MQTT_PORT=1883
|
||||||
|
MQTT_USER={{ vault_mqtt_c2_user }}
|
||||||
|
MQTT_PASS={{ vault_mqtt_c2_pass }}
|
||||||
|
|
||||||
|
# No GCP_CREDENTIALS_PATH — the VM uses Application Default Credentials
|
||||||
|
# via the GCE metadata server. The Terraform IAM bindings grant the required roles.
|
||||||
|
FIRESTORE_DATABASE={{ vault_firestore_database }}
|
||||||
|
GCS_BUCKET={{ vault_gcs_bucket }}
|
||||||
|
|
||||||
|
OPENAI_API_KEY={{ vault_openai_api_key }}
|
||||||
|
GOOGLE_MAPS_API_KEY={{ vault_google_maps_api_key }}
|
||||||
|
GEMINI_API_KEY={{ vault_gemini_api_key }}
|
||||||
|
|
||||||
|
SERVICE_KEY={{ vault_service_key }}
|
||||||
|
NODE_API_KEY={{ vault_node_api_key }}
|
||||||
|
|
||||||
|
CORS_ORIGINS=["https://app.{{ domain }}"]
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# drb-server-discord-bot environment — Managed by Ansible. Do not edit manually.
|
||||||
|
|
||||||
|
DISCORD_TOKEN={{ vault_discord_token }}
|
||||||
|
C2_URL=http://c2-core:8000
|
||||||
|
C2_SERVICE_KEY={{ vault_service_key }}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# drb-frontend environment — Managed by Ansible. Do not edit manually.
|
||||||
|
|
||||||
|
NEXT_PUBLIC_C2_URL=https://api.{{ domain }}
|
||||||
|
|
||||||
|
NEXT_PUBLIC_FIREBASE_API_KEY={{ vault_firebase_api_key }}
|
||||||
|
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN={{ vault_firebase_auth_domain }}
|
||||||
|
NEXT_PUBLIC_FIREBASE_PROJECT_ID={{ vault_firebase_project_id }}
|
||||||
|
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET={{ vault_firebase_storage_bucket }}
|
||||||
|
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID={{ vault_firebase_messaging_sender_id }}
|
||||||
|
NEXT_PUBLIC_FIREBASE_APP_ID={{ vault_firebase_app_id }}
|
||||||
|
NEXT_PUBLIC_FIRESTORE_DATABASE={{ vault_firestore_database }}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Top-level docker-compose environment — MQTT credentials for the broker container.
|
||||||
|
# Managed by Ansible. Do not edit manually.
|
||||||
|
|
||||||
|
MQTT_C2_USER={{ vault_mqtt_c2_user }}
|
||||||
|
MQTT_C2_PASS={{ vault_mqtt_c2_pass }}
|
||||||
|
MQTT_NODE_USER={{ vault_mqtt_node_user }}
|
||||||
|
MQTT_NODE_PASS={{ vault_mqtt_node_pass }}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
# Full first-time setup: waits for the VM's startup.sh to finish installing
|
||||||
|
# Docker, then deploys the stack. Safe to re-run — all tasks are idempotent.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook -i inventory.ini site.yml --ask-vault-pass
|
||||||
|
|
||||||
|
- name: Bootstrap + deploy DRB server
|
||||||
|
hosts: drb
|
||||||
|
become: true
|
||||||
|
vars_files:
|
||||||
|
- vault.yml
|
||||||
|
|
||||||
|
pre_tasks:
|
||||||
|
- name: Wait for Docker (startup.sh runs async on first boot)
|
||||||
|
command: docker info
|
||||||
|
register: _docker
|
||||||
|
until: _docker.rc == 0
|
||||||
|
retries: 30
|
||||||
|
delay: 10
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Create 2 GB swap file
|
||||||
|
command: fallocate -l 2G /swapfile
|
||||||
|
args:
|
||||||
|
creates: /swapfile
|
||||||
|
|
||||||
|
- name: Set swap file permissions
|
||||||
|
file:
|
||||||
|
path: /swapfile
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: Format swap file
|
||||||
|
command: mkswap /swapfile
|
||||||
|
register: _mkswap
|
||||||
|
changed_when: _mkswap.rc == 0
|
||||||
|
|
||||||
|
- name: Enable swap
|
||||||
|
command: swapon /swapfile
|
||||||
|
register: _swapon
|
||||||
|
failed_when: _swapon.rc != 0 and 'already' not in _swapon.stderr
|
||||||
|
changed_when: _swapon.rc == 0
|
||||||
|
|
||||||
|
- name: Persist swap in fstab
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/fstab
|
||||||
|
line: "/swapfile none swap sw 0 0"
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Set swappiness to 10 (use swap only under pressure)
|
||||||
|
sysctl:
|
||||||
|
name: vm.swappiness
|
||||||
|
value: "10"
|
||||||
|
sysctl_set: true
|
||||||
|
state: present
|
||||||
|
reload: true
|
||||||
|
|
||||||
|
- name: Add deploy user to docker group
|
||||||
|
user:
|
||||||
|
name: "{{ ssh_user }}"
|
||||||
|
groups: docker
|
||||||
|
append: true
|
||||||
|
|
||||||
|
- name: Create app directory
|
||||||
|
file:
|
||||||
|
path: "{{ app_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ ssh_user }}"
|
||||||
|
group: "{{ ssh_user }}"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
roles:
|
||||||
|
- deploy
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Template for your Ansible Vault secrets file.
|
||||||
|
# Copy to vault.yml, fill in values, then encrypt:
|
||||||
|
# ansible-vault encrypt vault.yml
|
||||||
|
# Edit later with:
|
||||||
|
# ansible-vault edit vault.yml
|
||||||
|
|
||||||
|
# ── MQTT ─────────────────────────────────────────────────────────────────────
|
||||||
|
vault_mqtt_c2_user: drb-c2-core
|
||||||
|
vault_mqtt_c2_pass: "CHANGE_ME"
|
||||||
|
vault_mqtt_node_user: drb-node
|
||||||
|
vault_mqtt_node_pass: "CHANGE_ME"
|
||||||
|
|
||||||
|
# ── C2 Core ───────────────────────────────────────────────────────────────────
|
||||||
|
vault_service_key: "" # openssl rand -hex 32
|
||||||
|
vault_node_api_key: "" # openssl rand -hex 32
|
||||||
|
vault_openai_api_key: ""
|
||||||
|
vault_google_maps_api_key: ""
|
||||||
|
vault_gemini_api_key: ""
|
||||||
|
vault_gcs_bucket: "your-gcs-bucket-name"
|
||||||
|
vault_firestore_database: "c2-server"
|
||||||
|
|
||||||
|
# ── Discord Bot ───────────────────────────────────────────────────────────────
|
||||||
|
vault_discord_token: ""
|
||||||
|
|
||||||
|
# ── Frontend (Firebase) ───────────────────────────────────────────────────────
|
||||||
|
vault_firebase_api_key: ""
|
||||||
|
vault_firebase_auth_domain: ""
|
||||||
|
vault_firebase_project_id: ""
|
||||||
|
vault_firebase_storage_bucket: ""
|
||||||
|
vault_firebase_messaging_sender_id: ""
|
||||||
|
vault_firebase_app_id: ""
|
||||||
|
|
||||||
|
# No GCP key needed — the VM uses Application Default Credentials via the
|
||||||
|
# GCE metadata server. Terraform grants the required IAM roles at apply time.
|
||||||
+71
-20
@@ -8,10 +8,11 @@ terraform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Store state in GCS — create the bucket manually once before first apply
|
# Store state in GCS — create the bucket manually once before first apply
|
||||||
backend "gcs" {
|
# Uncomment once GCS bucket permissions are confirmed working.
|
||||||
bucket = "drb-tf-state"
|
# backend "gcs" {
|
||||||
prefix = "drb/state"
|
# bucket = "drb-tf-state"
|
||||||
}
|
# prefix = "drb/state"
|
||||||
|
# }
|
||||||
}
|
}
|
||||||
|
|
||||||
provider "google" {
|
provider "google" {
|
||||||
@@ -19,6 +20,10 @@ provider "google" {
|
|||||||
region = var.region
|
region = var.region
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pull live project metadata (number, name) without hardcoding them.
|
||||||
|
data "google_project" "current" {}
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Static external IP
|
# Static external IP
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -87,8 +92,8 @@ resource "google_compute_instance" "drb_server" {
|
|||||||
boot_disk {
|
boot_disk {
|
||||||
initialize_params {
|
initialize_params {
|
||||||
image = "debian-cloud/debian-12"
|
image = "debian-cloud/debian-12"
|
||||||
size = 30 # GB — enough for Docker images + mosquitto data
|
size = 30 # GB — free tier covers 30GB pd-standard on e2-micro
|
||||||
type = "pd-balanced"
|
type = "pd-standard"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +111,8 @@ resource "google_compute_instance" "drb_server" {
|
|||||||
# Startup script runs once on first boot to install Docker + Caddy
|
# Startup script runs once on first boot to install Docker + Caddy
|
||||||
metadata_startup_script = file("${path.module}/startup.sh")
|
metadata_startup_script = file("${path.module}/startup.sh")
|
||||||
|
|
||||||
# Allow the VM to pull from Artifact Registry using its service account
|
# The default compute service account with cloud-platform scope gives the VM
|
||||||
|
# full access to GCS and Firestore in the same project — no key file needed.
|
||||||
service_account {
|
service_account {
|
||||||
scopes = ["cloud-platform"]
|
scopes = ["cloud-platform"]
|
||||||
}
|
}
|
||||||
@@ -118,21 +124,66 @@ resource "google_compute_instance" "drb_server" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Cloud DNS records
|
# IAM — grant the VM's default compute SA access to Firestore and GCS.
|
||||||
|
# Since Firebase/GCS already live in the same project, no key file is needed —
|
||||||
|
# the VM authenticates via the metadata server (ADC).
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
resource "google_dns_record_set" "app" {
|
locals {
|
||||||
name = "app.${var.domain}."
|
compute_sa = "serviceAccount:${data.google_project.current.number}-compute@developer.gserviceaccount.com"
|
||||||
type = "A"
|
|
||||||
ttl = 300
|
|
||||||
managed_zone = var.dns_zone_name
|
|
||||||
rrdatas = [google_compute_address.drb.address]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "google_dns_record_set" "api" {
|
resource "google_project_iam_member" "drb_firestore" {
|
||||||
name = "api.${var.domain}."
|
project = var.project_id
|
||||||
type = "A"
|
role = "roles/datastore.user"
|
||||||
ttl = 300
|
member = local.compute_sa
|
||||||
managed_zone = var.dns_zone_name
|
|
||||||
rrdatas = [google_compute_address.drb.address]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource "google_project_iam_member" "drb_gcs" {
|
||||||
|
project = var.project_id
|
||||||
|
role = "roles/storage.objectAdmin"
|
||||||
|
member = local.compute_sa
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Firestore database — import existing, manages schema/settings going forward
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import {
|
||||||
|
id = "projects/${var.project_id}/databases/${var.firestore_database}"
|
||||||
|
to = google_firestore_database.c2
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_firestore_database" "c2" {
|
||||||
|
project = var.project_id
|
||||||
|
name = var.firestore_database
|
||||||
|
location_id = var.firestore_location
|
||||||
|
type = "FIRESTORE_NATIVE"
|
||||||
|
|
||||||
|
# Prevent accidental deletion of the live database
|
||||||
|
deletion_policy = "DELETE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# GCS bucket — audio recordings. Import existing bucket.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import {
|
||||||
|
id = var.audio_bucket_name
|
||||||
|
to = google_storage_bucket.audio
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_storage_bucket" "audio" {
|
||||||
|
project = var.project_id
|
||||||
|
name = var.audio_bucket_name
|
||||||
|
location = var.audio_bucket_location
|
||||||
|
uniform_bucket_level_access = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DNS — managed in AWS Route 53 (cusano.net is there).
|
||||||
|
# After terraform apply, add these A records in Route 53:
|
||||||
|
# app.drb.cusano.net → server_ip output
|
||||||
|
# api.drb.cusano.net → server_ip output
|
||||||
|
# Or use a single wildcard: *.drb.cusano.net → server_ip
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -10,3 +10,13 @@ output "app_url" {
|
|||||||
output "api_url" {
|
output "api_url" {
|
||||||
value = "https://api.${var.domain}"
|
value = "https://api.${var.domain}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output "project_number" {
|
||||||
|
value = data.google_project.current.number
|
||||||
|
description = "GCP project number (useful for service account references)"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "ssh_command" {
|
||||||
|
value = "ssh ${var.ssh_user}@${google_compute_address.drb.address}"
|
||||||
|
description = "SSH command to reach the server (should rarely be needed)"
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Copy to terraform.tfvars and fill in values.
|
||||||
|
# terraform.tfvars is gitignored — never commit it.
|
||||||
|
|
||||||
|
project_id = "your-gcp-project-id" # gcloud config get-value project
|
||||||
|
region = "us-central1"
|
||||||
|
zone = "us-central1-a"
|
||||||
|
|
||||||
|
domain = "drb.cusano.net" # DNS is on AWS Route 53 — add A records manually after apply
|
||||||
|
|
||||||
|
machine_type = "e2-standard-2" # 2 vCPU / 8 GB — adjust if needed
|
||||||
|
|
||||||
|
ssh_user = "drb"
|
||||||
|
ssh_public_key = "ssh-ed25519 AAAA... user@host" # cat ~/.ssh/id_ed25519.pub
|
||||||
|
|
||||||
|
# Your IP + any CI runner IPs that need SSH access
|
||||||
|
allowed_ssh_cidrs = ["YOUR_IP/32"]
|
||||||
|
|
||||||
|
# Existing GCS bucket for audio recordings (bucket must already exist — imported into state)
|
||||||
|
audio_bucket_name = "your-audio-bucket-name"
|
||||||
|
audio_bucket_location = "US-CENTRAL1" # must match existing bucket location exactly — check GCP console
|
||||||
|
|
||||||
|
# Existing Firestore database ID and location (imported into state)
|
||||||
|
firestore_database = "c2-server"
|
||||||
|
firestore_location = "nam5" # nam5 = us-central, eur3 = europe, us-east1 = us-east
|
||||||
+23
-5
@@ -20,11 +20,6 @@ variable "domain" {
|
|||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "dns_zone_name" {
|
|
||||||
description = "Cloud DNS managed zone name"
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "machine_type" {
|
variable "machine_type" {
|
||||||
description = "Compute Engine machine type"
|
description = "Compute Engine machine type"
|
||||||
type = string
|
type = string
|
||||||
@@ -46,3 +41,26 @@ variable "allowed_ssh_cidrs" {
|
|||||||
description = "CIDR ranges allowed to SSH to the VM (your IP + Gitea runner)"
|
description = "CIDR ranges allowed to SSH to the VM (your IP + Gitea runner)"
|
||||||
type = list(string)
|
type = list(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "audio_bucket_name" {
|
||||||
|
description = "Existing GCS bucket name for call audio recordings"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "audio_bucket_location" {
|
||||||
|
description = "GCS bucket location — must match the existing bucket's location exactly"
|
||||||
|
type = string
|
||||||
|
default = "US-CENTRAL1"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "firestore_database" {
|
||||||
|
description = "Firestore database ID (e.g. c2-server)"
|
||||||
|
type = string
|
||||||
|
default = "c2-server"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "firestore_location" {
|
||||||
|
description = "Firestore multi-region location (nam5 = us-central, eur3 = europe)"
|
||||||
|
type = string
|
||||||
|
default = "nam5"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user