Initial infrastructure mono-repo scaffold

Consolidates nixos, docker, raspi, and debian-configuration into a single
infrastructure-as-code repo. Includes:

- ansible/: full inventory + proxmox-hardening, freeipa, and raspberrypi
  roles (converted from debian-configuration bash scripts)
- terraform/: Proxmox VMs, Dynu DNS, Pi-hole (decommissioned stub),
  Docker container catalog — migrated from docker/infrastructure/terraform/
- stacks/docker/, stacks/raspi/, nixos/: placeholder READMEs pending
  git subtree population (see implementation plan)
- docs/: internal MkDocs site with architecture, network topology, runbooks,
  and drift-detection guide; external sanitized site
- scripts/: drift-detect.sh, docs-build.sh, install-hooks.sh, check-secrets.sh
- CI: secret-scan (push/PR), drift-detect (daily), docs-build (on change)
- Pi-hole removed throughout — DNS is FreeIPA, DHCP is router

See docs/internal/implementation-plan.md for the phased rollout after
pushing to Gitea.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UvNjoxTWEDkhXsd1Dq2ETP
This commit is contained in:
beatzaplentyandClaude Sonnet 4.6 committed 2026-07-30 07:07:47 +10:00
commit f85c65870f
125 files changed
+8186

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
# Terraform Proxmox Inventory Layer
This directory codifies existing Proxmox infrastructure using an import-first reconciliation model.
## Purpose
- Track existing Proxmox VMs in Terraform.
- Reconcile imported VM configuration into maintainable, explicit files.
- Represent physical host metadata as structured Terraform locals/outputs.
- Support documentation inventory and future downstream tooling.
## Current repository status
This directory already contains imported/reconciled VM resources (for example `docker`, `server-nixos`, `nix-cache`, `pbs`, `pihole`) plus host metadata locals/outputs.
This means it is no longer just a scaffold; treat it as active infrastructure inventory code.
## Workflow standard (brownfield)
1. Import one existing VM at a time.
2. Confirm provider-specific import ID format.
3. Inspect state/plan details.
4. Keep hand-maintained `.tf` files focused and readable.
5. Use `ignore_changes` only where drift noise is unavoidable.
6. Stop when plan is sane/no-op for intended scope.
## File organization expectations
- Prefer one-resource-per-file patterns when practical.
- Keep shared metadata in `locals`/outputs with clear descriptions.
- Keep generated comments/config under ongoing cleanup rather than assuming generated output is final.
## Safety notes
- Do not run broad applies casually.
- Do not commit real credentials or `.tfstate*`.
- Keep changes incremental and reviewable.
## Related docs
- [../README.md](../README.md)
- [../../../docs/source-of-truth.md](../../../docs/source-of-truth.md)
- [../../../docs/terraform-workflows.md](../../../docs/terraform-workflows.md)
- [../../../docs/infrastructure-inventory.md](../../../docs/infrastructure-inventory.md)
+76
View File
@@ -0,0 +1,76 @@
# proxmox_virtual_environment_vm.docker:
resource "proxmox_virtual_environment_vm" "docker" {
name = "docker"
node_name = "pve"
scsi_hardware = "virtio-scsi-single"
vm_id = 103
agent {
enabled = true
timeout = "15m"
trim = false
}
cpu {
cores = 4
numa = false
sockets = 1
type = "host"
units = 1024
}
disk {
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi0"
iothread = false
path_in_datastore = "vm-103-disk-0"
replicate = true
size = 120
ssd = false
}
disk {
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi1"
iothread = false
path_in_datastore = "vm-103-disk-1"
replicate = true
size = 250
ssd = false
}
memory {
dedicated = 8192
floating = 4096
keep_hugepages = false
shared = 0
}
network_device {
bridge = "vmbr0"
disconnected = false
enabled = true
firewall = true
}
operating_system {
type = "l26"
}
lifecycle {
ignore_changes = [
vga,
keyboard_layout,
tablet_device,
agent,
]
}
}
+51
View File
@@ -0,0 +1,51 @@
# Proxmox import-first workflow
#
# 1) Add one minimal resource block for one existing VM.
# 2) Add an import block for that VM using the provider's required import ID format.
# 3) Run:
# terraform init
# terraform plan
# or:
# ../scripts/reconcile_from_plan.sh -- -var-file=terraform.tfvars
# 4) Review generated config carefully.
# 5) Move only the useful arguments into a hand-maintained .tf file.
# 6) Repeat until `terraform plan` is a no-op.
# IMPORTANT:
# - Start with exactly ONE existing VM.
# - Do not apply until plan is clean.
# - Confirm the provider's exact import ID format before running import/plan.
# - Do not import your whole environment at once.
# Example placeholder for one existing VM
#resource "proxmox_virtual_environment_vm" "server-nixos" {
# name = "server-nixos"
# node_name = "pve"
#}
# Example import block
# REPLACE the id below with the exact import ID format required by your provider.
# This is provider-specific and must be confirmed before use.
#
# Commonly this will involve the Proxmox node name and VM ID in some form.
#
import {
to = proxmox_virtual_environment_vm.nix-cache
id = "pve/105"
}
import {
to = proxmox_virtual_environment_vm.server-nixos
id = "pve/104"
}
import {
to = proxmox_virtual_environment_vm.pihole
id = "pve/108"
}
import {
to = proxmox_virtual_environment_vm.pbs
id = "pve/106"
}
import {
to = proxmox_virtual_environment_vm.docker
id = "pve/103"
}
+63
View File
@@ -0,0 +1,63 @@
# proxmox_virtual_environment_vm.nix-cache:
resource "proxmox_virtual_environment_vm" "nix-cache" {
name = "nix-cache"
node_name = "pve"
scsi_hardware = "virtio-scsi-single"
vm_id = 105
agent {
enabled = true
timeout = "15m"
trim = false
}
cpu {
cores = 2
numa = false
sockets = 1
type = "x86-64-v2-AES"
units = 1024
}
disk {
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi0"
iothread = false
path_in_datastore = "vm-105-disk-0"
replicate = true
size = 100
ssd = false
}
memory {
dedicated = 2048
floating = 0
keep_hugepages = false
shared = 0
}
network_device {
bridge = "vmbr0"
disconnected = false
enabled = true
firewall = true
}
operating_system {
type = "l26"
}
lifecycle {
ignore_changes = [
vga,
keyboard_layout,
tablet_device,
agent,
]
}
}
+28
View File
@@ -0,0 +1,28 @@
output "proxmox_scaffold_ready" {
description = "Indicates this directory is a placeholder scaffold for future Proxmox adoption."
value = true
}
output "proxmox_endpoint_configured" {
description = "Whether a non-empty endpoint has been provided."
value = var.proxmox_endpoint != ""
}
output "physical_hosts" {
description = "Physical host inventory used for documentation"
value = local.physical_hosts
}
output "virtual_hosts" {
description = "Virtual host/VM inventory used for documentation"
value = local.virtual_hosts
}
output "infrastructure_inventory" {
description = "Combined infrastructure inventory"
value = {
physical_hosts = local.physical_hosts
virtual_hosts = local.virtual_hosts
}
}
+72
View File
@@ -0,0 +1,72 @@
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform
resource "proxmox_virtual_environment_vm" "pbs" {
name = "pbs"
node_name = "pve"
scsi_hardware = "virtio-scsi-single"
vm_id = 106
agent {
enabled = true
timeout = "15m"
trim = false
}
cpu {
cores = 4
numa = false
sockets = 1
type = "x86-64-v2-AES"
units = 1024
}
disk {
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi0"
iothread = false
path_in_datastore = "vm-106-disk-0"
replicate = true
size = 100
ssd = false
}
disk {
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi1"
iothread = false
path_in_datastore = "vm-106-disk-1"
replicate = true
size = 700
ssd = false
}
memory {
dedicated = 8192
floating = 4096
keep_hugepages = false
shared = 0
}
network_device {
bridge = "vmbr0"
disconnected = false
enabled = true
firewall = true
}
operating_system {
type = "l26"
}
lifecycle {
ignore_changes = [
vga,
keyboard_layout,
tablet_device,
agent,
]
}
}
+14
View File
@@ -0,0 +1,14 @@
# Pi-hole VM — DECOMMISSIONED
# Pi-hole was decommissioned. DNS is now handled by FreeIPA (domain-controller.sweet.home).
# DHCP is on the router. PXE DHCP is handled by the pxe-boot LXC.
#
# This resource block is kept as historical documentation of VMID 108.
# If the VM has been destroyed on Proxmox, remove this file entirely.
# If it still exists and needs to be removed via Terraform:
# terraform destroy -target=proxmox_virtual_environment_vm.pihole
# resource "proxmox_virtual_environment_vm" "pihole" {
# name = "pihole"
# node_name = "pve"
# vm_id = 108
# }
+11
View File
@@ -0,0 +1,11 @@
provider "proxmox" {
# Placeholder-only scaffold.
# Confirm exact provider auth mode and endpoint details before real use.
endpoint = var.proxmox_endpoint
insecure = var.proxmox_insecure
# username = var.proxmox_username
# password = var.proxmox_password
api_token = "${var.proxmox_api_token_id}=${var.proxmox_api_token_secret}"
}
+79
View File
@@ -0,0 +1,79 @@
locals {
physical_hosts = {
pve = {
hostname = "pve"
type = "physical"
role = "proxmox"
management_ip = "pve.sweet.home"
os_family = "debian"
hypervisor = "proxmox"
location = "home"
notes = "Primary Proxmox VE host"
}
raspberrypi = {
hostname = "raspberrypi"
type = "physical"
role = "edge"
management_ip = "raspberrypi.tail13f623.ts.net"
os_family = "debian"
hypervisor = null
location = "riverglades"
notes = "Raspberry Pi host"
}
}
# Virtual host inventory for documentation output. This is intentionally
# concise and shaped for docs tooling (not a full provider object dump).
virtual_hosts = {
docker = {
name = "docker"
type = "virtual"
role = "docker-host"
proxmox_node = "pve"
vm_id = 103
management_ip = ""
os_family = "linux"
notes = "Primary Docker VM"
}
server_nixos = {
name = "server-nixos"
type = "virtual"
role = "nixos-server"
proxmox_node = "pve"
vm_id = 104
management_ip = ""
os_family = "nixos"
notes = "General-purpose NixOS VM"
}
nix_cache = {
name = "nix-cache"
type = "virtual"
role = "cache"
proxmox_node = "pve"
vm_id = 105
management_ip = ""
os_family = "linux"
notes = "Nix binary cache VM"
}
pbs = {
name = "pbs"
type = "virtual"
role = "backup"
proxmox_node = "pve"
vm_id = 106
management_ip = ""
os_family = "linux"
notes = "Proxmox Backup Server VM"
}
pihole = {
name = "pihole"
type = "virtual"
role = "dns"
proxmox_node = "pve"
vm_id = 108
management_ip = ""
os_family = "linux"
notes = "DNS filtering VM"
}
}
}
+88
View File
@@ -0,0 +1,88 @@
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform
resource "proxmox_virtual_environment_vm" "server-nixos" {
name = "server-nixos"
node_name = "pve"
scsi_hardware = "virtio-scsi-single"
vm_id = 104
agent {
enabled = true
timeout = "15m"
trim = false
}
cpu {
cores = 4
numa = false
sockets = 1
type = "x86-64-v2-AES"
units = 1024
}
disk {
aio = "io_uring"
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi0"
iothread = false
path_in_datastore = "vm-104-disk-0"
replicate = true
size = 32
ssd = false
}
disk {
aio = "io_uring"
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi1"
iothread = false
path_in_datastore = "vm-104-disk-1"
replicate = true
size = 200
ssd = false
}
disk {
aio = "io_uring"
backup = true
cache = "none"
datastore_id = "local-lvm"
discard = "ignore"
file_format = "raw"
interface = "scsi2"
iothread = false
path_in_datastore = "vm-104-disk-2"
replicate = true
size = 200
ssd = false
}
memory {
dedicated = 4096
floating = 2048
keep_hugepages = false
shared = 0
}
network_device {
bridge = "vmbr0"
disconnected = false
enabled = true
firewall = true
}
operating_system {
type = "l26"
}
lifecycle {
ignore_changes = [
vga,
keyboard_layout,
tablet_device,
agent,
]
}
}
@@ -0,0 +1,9 @@
# Example placeholders only. Do not commit real credentials.
proxmox_endpoint = "https://pve.example.local:8006/api2/json"
proxmox_insecure = false
# Use either username/password or API token based on your chosen auth flow.
proxmox_username = "root@pam"
proxmox_password = "REPLACE_ME"
proxmox_api_token = "REPLACE_ME"
+37
View File
@@ -0,0 +1,37 @@
variable "proxmox_endpoint" {
description = "Proxmox API endpoint URL, for example https://pve.example.local:8006/api2/json"
type = string
default = ""
}
variable "proxmox_insecure" {
description = "Set true only for local testing with self-signed TLS; prefer false in stable environments."
type = bool
default = false
}
#variable "proxmox_username" {
# description = "Username for password-based auth (placeholder; optional if token auth is used)."
# type = string
# default = ""
#}
#variable "proxmox_password" {
# description = "Password for password-based auth (placeholder; optional if token auth is used)."
# type = string
# default = ""
# sensitive = true
#}
variable "proxmox_api_token_id" {
type = string
description = "Proxmox API token ID, e.g. terraform@pve!tf"
sensitive = true
}
variable "proxmox_api_token_secret" {
description = "API token for token-based auth (placeholder; optional if username/password is used)."
type = string
default = ""
sensitive = true
}
+10
View File
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.6.0"
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.68.0"
}
}
}