FigJam Diagram: Terraform & Ansible — Infrastructure as Code (expires 2026-04-13)
All VM infrastructure managed with Terraform. All node configuration managed with Ansible. Nothing is ClickOps.
| Environment | Path | State Key | Purpose |
|---|---|---|---|
homelab-prod |
terraform/environments/homelab-prod/ |
homelab-prod/terraform.tfstate |
Proxmox VMs (k3s nodes) |
aws |
terraform/environments/aws/ |
aws/terraform.tfstate |
AWS resources (ECR, IAM, S3, Route53, CloudFront) |
google |
terraform/environments/google/ |
google/terraform.tfstate |
GCP OAuth 2.0 credentials (K8s secret), Authentik Google social source, per-app access policies |
State Backend: S3 bucket k3s-homelab-tfstate-855878721457 (us-east-1, versioned, encrypted)
Locking: S3 native lock files (use_lockfile = true)
GCP constraint: Standard OAuth 2.0 clients cannot be created via Terraform — create the app in GCP Console (
homelab-oauthproject, personal account, no org), then pass credentials as Terraform vars. Thegoogle-oauth-appmodule only stores them as a K8s secret.
| Provider | Version | Notes |
|---|---|---|
bpg/proxmox |
0.98.1 (~> 0.50) |
VM provisioning, GPU passthrough, network config |
hashicorp/aws |
~> 5.x |
AWS resources |
hashicorp/google |
~> 6.0 |
GCP resources |
goauthentik/authentik |
>= 2024.0 |
Authentik source/provider config |
hashicorp/kubernetes |
~> 2.0 |
K8s secrets (Google OAuth credentials) |
Auth note:
hostpci(GPU passthrough) requiresroot@pamcredentials, not API tokens. All homelab-prod environments useproxmox_username = "root@pam".
| Module | Purpose |
|---|---|
proxmox_vm |
Generic VM provisioning on Proxmox |
k3s_node |
k3s-specific VM config (extends proxmox_vm) |
terraform_state |
S3 + lock file backend setup |
| Module | Purpose |
|---|---|
route53_iam |
IAM for cert-manager DNS-01 (Route53) |
s3_backups |
S3 backup bucket |
ecr |
ECR container repos + push/pull IAM users |
grafana_cloudwatch_iam |
Grafana CloudWatch read-only IAM |
bedrock_budget |
AWS Budgets alarm for Bedrock spend |
cloudfront_waf |
CloudFront + WAF for public endpoints |
cloudtrail |
CloudTrail audit logging |
ses_iam |
SES IAM for email sending |
alert_responder_bedrock_iam |
IAM for Alert Responder Bedrock access |
auto_brand_iam |
IAM for Auto Brand (Bedrock + S3) |
open_webui_bedrock_iam |
IAM for OpenClaw chat gateway Bedrock |
generative_agents_bedrock_iam |
IAM for generative agent workloads |
rag_ingester_iam |
IAM for RAG pipeline ingestion |
security_scanner_bedrock_iam |
IAM for security scanner Bedrock access |
trade_bot_bedrock_iam |
IAM for Trade Bot Bedrock access |
tshirt_cannon_iam |
IAM for Tshirt Cannon AI pipeline |
| Module | Purpose |
|---|---|
google-oauth-app |
Stores Google OAuth 2.0 credentials in a Kubernetes secret (GCP doesn't support creating OAuth apps via Terraform — create in Console, pass creds as vars) |
authentik-google-source |
Configures Authentik's Google OAuth source (authentik_source_oauth) and enrollment flows |
Templates are node-local (not shared storage):
| Host | Template ID | OS |
|---|---|---|
| pve1 | 9000 | Debian 13 (Trixie) cloud image |
| pve2 | 9001 | Debian 13 (Trixie) cloud image |
| pve3 | 9002 | Debian 13 (Trixie) cloud image |
| pve4 | 9003 | Debian 13 (Trixie) cloud image |
All VMs use cloud-init for initial configuration.
| Role | Purpose |
|---|---|
common |
OS hardening, swap disabled, timezone (America/Chicago) |
hardening |
SSH hardening, fail2ban, UFW firewall rules |
k3s_server |
k3s control plane with HA embedded etcd |
k3s_agent |
k3s worker node (sets K3S_TOKEN + K3S_URL in service env) |
longhorn_disk |
Dedicated Longhorn disks if /dev/sdb present |
cluster_services |
MetalLB, Traefik, cert-manager, Longhorn, Prometheus, Loki |
proxmox_nic_fix |
Disable Intel e1000e offloading (prevents NIC hangs on VMs) |
proxmox_network_rescue |
Auto-recover vmbr0 interface on PVE hosts |
gpu-worker |
Install VA-API drivers, apply gpu=intel-uhd-630 node label |
proxmox-gpu-prep |
IOMMU/VFIO setup on PVE hosts for GPU passthrough |
# --- Proxmox VMs ---
cd terraform/environments/homelab-prod
terraform plan
terraform apply
# Target a single agent
terraform plan -target='module.k3s_agents["k3s-agent-4"]'
# --- AWS Resources ---
cd terraform/environments/aws
terraform plan
terraform apply
# Get service credentials
terraform output -raw trade_bot_bedrock_access_key_id
terraform output -raw grafana_cloudwatch_access_key_id
# --- Google / Authentik ---
cd terraform/environments/google
terraform plan
terraform apply
# --- Ansible ---
cd ansible && source .env # loads AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
# Full cluster
ansible-playbook -i inventory/homelab playbooks/site.yml
# Single node
ansible-playbook -i inventory/homelab playbooks/site.yml --limit k3s-agent-1
# GPU node setup
ansible-playbook -i inventory/homelab playbooks/proxmox-gpu-prep.yml --limit pve4
ansible-playbook -i inventory/homelab playbooks/gpu-worker.yml --limit k3s-agent-4
# List state
terraform state list
# Check for stale S3 lock
aws s3 ls s3://k3s-homelab-tfstate-855878721457/ | grep tflock
# Break stale lock (use carefully)
terraform plan -lock=false
# Import existing resource
terraform import 'module.k3s_servers["k3s-server-1"].module.vm.proxmox_virtual_environment_vm.vm' pve1/qemu/110