Files
homelab/scripts/crawlers-exporters/collect-homelab-config.sh

1024 lines
32 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
################################################################################
# Homelab Infrastructure Collection Script
# Version: 1.0.0
# Purpose: Collects Proxmox VE configurations, system information, and exports
# infrastructure state in an organized, documented format
#
# Usage: ./collect-homelab-config.sh [OPTIONS]
#
# This script performs READ-ONLY operations and makes no modifications to your
# Proxmox environment. It is designed to be run directly on the Proxmox host
# or remotely via SSH.
################################################################################
set -euo pipefail # Exit on error, undefined variables, and pipe failures
# Script metadata
SCRIPT_VERSION="1.0.0"
SCRIPT_NAME="$(basename "$0")"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
# Color codes for output (disabled if not a TTY)
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color
else
RED='' GREEN='' YELLOW='' BLUE='' MAGENTA='' CYAN='' BOLD='' NC=''
fi
################################################################################
# Configuration Variables
################################################################################
# Default collection level: basic, standard, full, paranoid
COLLECTION_LEVEL="${COLLECTION_LEVEL:-standard}"
# Sanitization options
SANITIZE_IPS="${SANITIZE_IPS:-false}"
SANITIZE_PASSWORDS="${SANITIZE_PASSWORDS:-true}"
SANITIZE_TOKENS="${SANITIZE_TOKENS:-true}"
# Output configuration
OUTPUT_BASE_DIR="${OUTPUT_DIR:-./homelab-export-${TIMESTAMP}}"
COMPRESS_OUTPUT="${COMPRESS_OUTPUT:-true}"
# Logging
LOG_FILE="${OUTPUT_BASE_DIR}/collection.log"
VERBOSE="${VERBOSE:-false}"
# Summary tracking
COLLECTED_ITEMS=()
SKIPPED_ITEMS=()
ERROR_ITEMS=()
################################################################################
# Utility Functions
################################################################################
log() {
local level="$1"
shift
local message="$*"
local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[${timestamp}] [${level}] ${message}" >> "${LOG_FILE}" 2>/dev/null || true
case "${level}" in
INFO)
echo -e "${BLUE}[INFO]${NC} ${message}"
;;
SUCCESS)
echo -e "${GREEN}[✓]${NC} ${message}"
;;
WARN)
echo -e "${YELLOW}[WARN]${NC} ${message}"
;;
ERROR)
echo -e "${RED}[ERROR]${NC} ${message}" >&2
;;
DEBUG)
if [[ "${VERBOSE}" == "true" ]]; then
echo -e "${MAGENTA}[DEBUG]${NC} ${message}"
fi
;;
esac
}
banner() {
local text="$1"
local width=80
echo ""
echo -e "${BOLD}${CYAN}$(printf '=%.0s' $(seq 1 ${width}))${NC}"
echo -e "${BOLD}${CYAN} ${text}${NC}"
echo -e "${BOLD}${CYAN}$(printf '=%.0s' $(seq 1 ${width}))${NC}"
echo ""
}
check_command() {
local cmd="$1"
if command -v "${cmd}" &> /dev/null; then
log DEBUG "Command '${cmd}' is available"
return 0
else
log DEBUG "Command '${cmd}' is NOT available"
return 1
fi
}
check_proxmox() {
if [[ ! -f /etc/pve/.version ]]; then
log ERROR "This does not appear to be a Proxmox VE host"
log ERROR "/etc/pve/.version not found"
return 1
fi
return 0
}
safe_copy() {
local source="$1"
local dest="$2"
local description="${3:-file}"
if [[ -f "${source}" ]]; then
mkdir -p "$(dirname "${dest}")"
cp "${source}" "${dest}" 2>/dev/null && {
log SUCCESS "Collected ${description}"
COLLECTED_ITEMS+=("${description}")
return 0
} || {
log WARN "Failed to copy ${description} from ${source}"
ERROR_ITEMS+=("${description}")
return 1
}
elif [[ -d "${source}" ]]; then
mkdir -p "${dest}"
cp -r "${source}"/* "${dest}/" 2>/dev/null && {
log SUCCESS "Collected ${description}"
COLLECTED_ITEMS+=("${description}")
return 0
} || {
log WARN "Failed to copy directory ${description} from ${source}"
ERROR_ITEMS+=("${description}")
return 1
}
else
log DEBUG "Source does not exist: ${source} (${description})"
SKIPPED_ITEMS+=("${description}")
return 1
fi
}
safe_command() {
local output_file="$1"
local description="$2"
shift 2
local cmd=("$@")
mkdir -p "$(dirname "${output_file}")"
if "${cmd[@]}" > "${output_file}" 2>/dev/null; then
log SUCCESS "Collected ${description}"
COLLECTED_ITEMS+=("${description}")
return 0
else
log WARN "Failed to execute: ${cmd[*]} (${description})"
ERROR_ITEMS+=("${description}")
rm -f "${output_file}"
return 1
fi
}
sanitize_file() {
local file="$1"
[[ ! -f "${file}" ]] && return 0
# Sanitize passwords
if [[ "${SANITIZE_PASSWORDS}" == "true" ]]; then
sed -i 's/password=.*/password=<REDACTED>/g' "${file}" 2>/dev/null || true
sed -i 's/passwd=.*/passwd=<REDACTED>/g' "${file}" 2>/dev/null || true
sed -i 's/"password"[[:space:]]*:[[:space:]]*"[^"]*"/"password": "<REDACTED>"/g' "${file}" 2>/dev/null || true
fi
# Sanitize tokens and keys
if [[ "${SANITIZE_TOKENS}" == "true" ]]; then
sed -i 's/token=.*/token=<REDACTED>/g' "${file}" 2>/dev/null || true
sed -i 's/api[_-]key=.*/api_key=<REDACTED>/g' "${file}" 2>/dev/null || true
sed -i 's/secret=.*/secret=<REDACTED>/g' "${file}" 2>/dev/null || true
fi
# Sanitize IP addresses (if requested)
if [[ "${SANITIZE_IPS}" == "true" ]]; then
# Replace IPv4 with 10.x.x.x equivalents
sed -i 's/\b\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}\b/10.X.X.X/g' "${file}" 2>/dev/null || true
fi
}
################################################################################
# Directory Structure Creation
################################################################################
create_directory_structure() {
banner "Creating Directory Structure"
local dirs=(
"${OUTPUT_BASE_DIR}"
"${OUTPUT_BASE_DIR}/docs"
"${OUTPUT_BASE_DIR}/configs/proxmox"
"${OUTPUT_BASE_DIR}/configs/vms"
"${OUTPUT_BASE_DIR}/configs/lxc"
"${OUTPUT_BASE_DIR}/configs/storage"
"${OUTPUT_BASE_DIR}/configs/network"
"${OUTPUT_BASE_DIR}/configs/backup"
"${OUTPUT_BASE_DIR}/exports/system"
"${OUTPUT_BASE_DIR}/exports/cluster"
"${OUTPUT_BASE_DIR}/exports/guests"
"${OUTPUT_BASE_DIR}/scripts"
"${OUTPUT_BASE_DIR}/diagrams"
)
for dir in "${dirs[@]}"; do
mkdir -p "${dir}"
log DEBUG "Created directory: ${dir}"
done
# Initialize log file
mkdir -p "$(dirname "${LOG_FILE}")"
touch "${LOG_FILE}"
log SUCCESS "Directory structure created at: ${OUTPUT_BASE_DIR}"
}
################################################################################
# Collection Functions
################################################################################
collect_system_information() {
banner "Collecting System Information"
local sys_dir="${OUTPUT_BASE_DIR}/exports/system"
# Proxmox version
safe_command "${sys_dir}/pve-version.txt" "Proxmox VE version" pveversion -v || true
# System information
safe_command "${sys_dir}/hostname.txt" "Hostname" hostname || true
safe_command "${sys_dir}/uname.txt" "Kernel information" uname -a || true
safe_command "${sys_dir}/uptime.txt" "System uptime" uptime || true
safe_command "${sys_dir}/date.txt" "System date/time" date || true
# CPU information
safe_command "${sys_dir}/cpuinfo.txt" "CPU information" lscpu || true
safe_copy "/proc/cpuinfo" "${sys_dir}/proc-cpuinfo.txt" "Detailed CPU info" || true
# Memory information
safe_command "${sys_dir}/meminfo.txt" "Memory information" free -h || true
safe_copy "/proc/meminfo" "${sys_dir}/proc-meminfo.txt" "Detailed memory info" || true
# Disk information
safe_command "${sys_dir}/df.txt" "Filesystem usage" df -h || true
safe_command "${sys_dir}/lsblk.txt" "Block devices" lsblk || true
if check_command "pvdisplay"; then
safe_command "${sys_dir}/pvdisplay.txt" "LVM physical volumes" pvdisplay || true
safe_command "${sys_dir}/vgdisplay.txt" "LVM volume groups" vgdisplay || true
safe_command "${sys_dir}/lvdisplay.txt" "LVM logical volumes" lvdisplay || true
fi
# Network information
safe_command "${sys_dir}/ip-addr.txt" "IP addresses" ip addr show || true
safe_command "${sys_dir}/ip-route.txt" "Routing table" ip route show || true
safe_command "${sys_dir}/ss-listening.txt" "Listening sockets" ss -tulpn || true
# Installed packages
if check_command "dpkg"; then
safe_command "${sys_dir}/dpkg-list.txt" "Installed packages" dpkg -l || true
fi
}
collect_proxmox_configs() {
banner "Collecting Proxmox Configurations"
local pve_dir="${OUTPUT_BASE_DIR}/configs/proxmox"
# Main Proxmox configuration files
safe_copy "/etc/pve/datacenter.cfg" "${pve_dir}/datacenter.cfg" "Datacenter config" || true
safe_copy "/etc/pve/storage.cfg" "${pve_dir}/storage.cfg" "Storage config" || true
safe_copy "/etc/pve/user.cfg" "${pve_dir}/user.cfg" "User config" || true
safe_copy "/etc/pve/domains.cfg" "${pve_dir}/domains.cfg" "Authentication domains" || true
safe_copy "/etc/pve/authkey.pub" "${pve_dir}/authkey.pub" "Auth public key" || true
# Firewall configurations
if [[ -f /etc/pve/firewall/cluster.fw ]]; then
safe_copy "/etc/pve/firewall/cluster.fw" "${pve_dir}/firewall-cluster.fw" "Cluster firewall rules" || true
fi
# Cluster configuration (if in a cluster)
if [[ -f /etc/pve/corosync.conf ]]; then
safe_copy "/etc/pve/corosync.conf" "${pve_dir}/corosync.conf" "Corosync config" || true
fi
# HA configuration
if [[ -d /etc/pve/ha ]]; then
safe_copy "/etc/pve/ha" "${pve_dir}/ha" "HA configuration" || true
fi
# Sanitize sensitive information
for file in "${pve_dir}"/*; do
[[ -f "${file}" ]] && sanitize_file "${file}" || true
done
}
collect_vm_configs() {
banner "Collecting VM Configurations"
local vm_dir="${OUTPUT_BASE_DIR}/configs/vms"
# Get list of VMs
if [[ -d /etc/pve/nodes ]]; then
for node_dir in /etc/pve/nodes/*; do
local node_name="$(basename "${node_dir}")"
if [[ -d "${node_dir}/qemu-server" ]]; then
for vm_config in "${node_dir}/qemu-server"/*.conf; do
[[ -f "${vm_config}" ]] || continue
local vmid="$(basename "${vm_config}" .conf)"
local vm_name="$(grep -E '^name:' "${vm_config}" 2>/dev/null | cut -d' ' -f2 || echo "unknown")"
safe_copy "${vm_config}" "${vm_dir}/${vmid}-${vm_name}.conf" "VM ${vmid} (${vm_name}) config" || true
# Firewall rules for this VM
if [[ -f "${node_dir}/qemu-server/${vmid}.fw" ]]; then
safe_copy "${node_dir}/qemu-server/${vmid}.fw" "${vm_dir}/${vmid}-${vm_name}.fw" "VM ${vmid} firewall rules" || true
fi
done
fi
done
fi
# Sanitize VM configs
for file in "${vm_dir}"/*; do
[[ -f "${file}" ]] && sanitize_file "${file}" || true
done
}
collect_lxc_configs() {
banner "Collecting LXC Container Configurations"
local lxc_dir="${OUTPUT_BASE_DIR}/configs/lxc"
# Get list of containers
if [[ -d /etc/pve/nodes ]]; then
for node_dir in /etc/pve/nodes/*; do
local node_name="$(basename "${node_dir}")"
if [[ -d "${node_dir}/lxc" ]]; then
for lxc_config in "${node_dir}/lxc"/*.conf; do
[[ -f "${lxc_config}" ]] || continue
local ctid="$(basename "${lxc_config}" .conf)"
local ct_name="$(grep -E '^hostname:' "${lxc_config}" 2>/dev/null | cut -d' ' -f2 || echo "unknown")"
safe_copy "${lxc_config}" "${lxc_dir}/${ctid}-${ct_name}.conf" "Container ${ctid} (${ct_name}) config" || true
# Firewall rules for this container
if [[ -f "${node_dir}/lxc/${ctid}.fw" ]]; then
safe_copy "${node_dir}/lxc/${ctid}.fw" "${lxc_dir}/${ctid}-${ct_name}.fw" "Container ${ctid} firewall rules" || true
fi
done
fi
done
fi
# Sanitize LXC configs
for file in "${lxc_dir}"/*; do
[[ -f "${file}" ]] && sanitize_file "${file}" || true
done
}
collect_network_configs() {
banner "Collecting Network Configurations"
local net_dir="${OUTPUT_BASE_DIR}/configs/network"
# Network interface configurations
safe_copy "/etc/network/interfaces" "${net_dir}/interfaces" "Network interfaces config" || true
if [[ -d /etc/network/interfaces.d ]]; then
safe_copy "/etc/network/interfaces.d" "${net_dir}/interfaces.d" "Additional interface configs" || true
fi
# SDN configuration (Software Defined Networking)
if [[ -d /etc/pve/sdn ]]; then
safe_copy "/etc/pve/sdn" "${net_dir}/sdn" "SDN configuration" || true
fi
# Hosts file
safe_copy "/etc/hosts" "${net_dir}/hosts" "Hosts file" || true
safe_copy "/etc/resolv.conf" "${net_dir}/resolv.conf" "DNS resolver config" || true
# Sanitize network configs
for file in "${net_dir}"/*; do
[[ -f "${file}" ]] && sanitize_file "${file}" || true
done
}
collect_storage_configs() {
banner "Collecting Storage Information"
local storage_dir="${OUTPUT_BASE_DIR}/configs/storage"
# Storage configuration is already in proxmox config, but let's get status
if check_command "pvesm"; then
safe_command "${storage_dir}/pvesm-status.txt" "Storage status" pvesm status || true
fi
# ZFS pools (if any)
if check_command "zpool"; then
safe_command "${storage_dir}/zpool-status.txt" "ZFS pool status" zpool status || true
safe_command "${storage_dir}/zpool-list.txt" "ZFS pool list" zpool list || true
fi
if check_command "zfs"; then
safe_command "${storage_dir}/zfs-list.txt" "ZFS datasets" zfs list || true
fi
# NFS exports
if [[ -f /etc/exports ]]; then
safe_copy "/etc/exports" "${storage_dir}/nfs-exports" "NFS exports" || true
fi
# Samba configuration
if [[ -f /etc/samba/smb.conf ]]; then
safe_copy "/etc/samba/smb.conf" "${storage_dir}/smb.conf" "Samba config" || true
fi
# iSCSI configuration
if [[ -f /etc/iscsi/iscsid.conf ]]; then
safe_copy "/etc/iscsi/iscsid.conf" "${storage_dir}/iscsid.conf" "iSCSI initiator config" || true
fi
}
collect_backup_configs() {
banner "Collecting Backup Configurations"
local backup_dir="${OUTPUT_BASE_DIR}/configs/backup"
# Vzdump configuration
if [[ -f /etc/vzdump.conf ]]; then
safe_copy "/etc/vzdump.conf" "${backup_dir}/vzdump.conf" "Vzdump config" || true
fi
# Backup jobs
if [[ -d /etc/pve/jobs ]]; then
safe_copy "/etc/pve/jobs" "${backup_dir}/jobs" "Scheduled backup jobs" || true
fi
# PBS configuration (if connected to Proxmox Backup Server)
if [[ -f /etc/pve/priv/storage.cfg ]]; then
grep -A5 "type: pbs" /etc/pve/priv/storage.cfg > "${backup_dir}/pbs-storage.txt" 2>/dev/null || true
fi
}
collect_cluster_information() {
banner "Collecting Cluster Information"
local cluster_dir="${OUTPUT_BASE_DIR}/exports/cluster"
# Cluster status
if check_command "pvecm"; then
safe_command "${cluster_dir}/cluster-status.txt" "Cluster status" pvecm status || true
safe_command "${cluster_dir}/cluster-nodes.txt" "Cluster nodes" pvecm nodes || true
fi
# Resource information
if check_command "pvesh"; then
safe_command "${cluster_dir}/cluster-resources.json" "Cluster resources" pvesh get /cluster/resources --output-format json
safe_command "${cluster_dir}/cluster-tasks.json" "Recent tasks" pvesh get /cluster/tasks --output-format json
fi
}
collect_guest_information() {
banner "Collecting Guest Information"
local guests_dir="${OUTPUT_BASE_DIR}/exports/guests"
# List all VMs
if check_command "qm"; then
safe_command "${guests_dir}/vm-list.txt" "VM list" qm list
fi
# List all containers
if check_command "pct"; then
safe_command "${guests_dir}/container-list.txt" "Container list" pct list
fi
# Detailed guest information in JSON format
if check_command "pvesh"; then
safe_command "${guests_dir}/all-guests.json" "All guests (JSON)" pvesh get /cluster/resources --type vm --output-format json
fi
}
collect_service_configs() {
banner "Collecting Service Configurations (Advanced)"
# Only collect if level is 'full' or 'paranoid'
if [[ "${COLLECTION_LEVEL}" != "full" ]] && [[ "${COLLECTION_LEVEL}" != "paranoid" ]]; then
log INFO "Skipping service configs (collection level: ${COLLECTION_LEVEL})"
return 0
fi
local services_dir="${OUTPUT_BASE_DIR}/configs/services"
mkdir -p "${services_dir}"
# Systemd service status
safe_command "${services_dir}/systemd-services.txt" "Systemd services" systemctl list-units --type=service --all
# Collect specific Proxmox service configs
local pve_services=(
"pve-cluster"
"pvedaemon"
"pveproxy"
"pvestatd"
"pve-firewall"
"pvescheduler"
)
for service in "${pve_services[@]}"; do
if systemctl list-unit-files | grep -q "^${service}"; then
safe_command "${services_dir}/${service}-status.txt" "${service} status" systemctl status "${service}" || true
fi
done
}
################################################################################
# Documentation Generation
################################################################################
generate_readme() {
banner "Generating Documentation"
local readme="${OUTPUT_BASE_DIR}/README.md"
cat > "${readme}" <<'EOF'
# Homelab Infrastructure Export
This directory contains a complete snapshot of your Proxmox-based homelab infrastructure, collected automatically via the homelab collection script.
## Collection Information
- **Collection Date**: $(date '+%Y-%m-%d %H:%M:%S')
- **Proxmox Node**: $(hostname)
- **Collection Level**: ${COLLECTION_LEVEL}
- **Sanitization Applied**: IPs=${SANITIZE_IPS}, Passwords=${SANITIZE_PASSWORDS}, Tokens=${SANITIZE_TOKENS}
## Directory Structure
```
homelab-export-<timestamp>/
├── README.md # This file
├── SUMMARY.md # Collection summary report
├── collection.log # Detailed collection log
├── configs/ # Configuration files
│ ├── proxmox/ # Proxmox VE configurations
│ ├── vms/ # Virtual machine configs
│ ├── lxc/ # LXC container configs
│ ├── storage/ # Storage configurations
│ ├── network/ # Network configurations
│ ├── backup/ # Backup job configurations
│ └── services/ # System service configs (if collected)
├── exports/ # System state exports
│ ├── system/ # System information
│ ├── cluster/ # Cluster status and resources
│ └── guests/ # Guest VM/CT information
├── docs/ # Documentation (for manual additions)
├── scripts/ # Automation scripts (for manual additions)
└── diagrams/ # Network diagrams (for manual additions)
```
## Configuration Files
### Proxmox Core Configurations
- `datacenter.cfg` - Datacenter-wide settings
- `storage.cfg` - Storage pool definitions
- `user.cfg` - User and permission configurations
- `firewall-cluster.fw` - Cluster-level firewall rules
### Virtual Machines
Each VM configuration is named: `<VMID>-<name>.conf`
Firewall rules (if present): `<VMID>-<name>.fw`
### LXC Containers
Each container configuration is named: `<CTID>-<name>.conf`
Firewall rules (if present): `<CTID>-<name>.fw`
## System Exports
### System Information
- Proxmox version, hostname, kernel info
- CPU, memory, and disk information
- Network configuration and routing
- Installed packages
### Cluster Information
- Cluster status and membership
- Resource allocation
- Recent tasks
### Guest Information
- List of all VMs and containers
- Resource usage and status
- JSON exports for programmatic access
## Security Notes
This export may contain sensitive information depending on sanitization settings:
- **Passwords**: ${SANITIZE_PASSWORDS}
- **API Tokens**: ${SANITIZE_TOKENS}
- **IP Addresses**: ${SANITIZE_IPS}
**Recommendation**: Store this export securely. Do not commit to public repositories without careful review.
## Using This Export
### As Documentation
These files serve as a snapshot of your infrastructure at a point in time. Use them for:
- Documentation and disaster recovery
- Change tracking (diff with previous exports)
- Migration planning
### Infrastructure as Code
Use the collected configurations to:
- Create Terraform/OpenTofu templates
- Build Ansible playbooks
- Document network architecture
### Restoration Reference
In a disaster recovery scenario:
1. Reinstall Proxmox VE
2. Reference storage configuration from `configs/proxmox/storage.cfg`
3. Reference network setup from `configs/network/interfaces`
4. Recreate VMs/containers using configs in `configs/vms/` and `configs/lxc/`
5. Restore VM disk images from backups
## Next Steps
1. **Review the SUMMARY.md** for collection statistics
2. **Check collection.log** for any warnings or errors
3. **Manually add documentation** to the `docs/` folder
4. **Create network diagrams** and place in `diagrams/`
5. **Version control** this export in a private Git repository
6. **Set up regular collections** to track infrastructure changes
## Collection Script
This export was created by the Homelab Infrastructure Collection Script.
For questions or issues, consult the script documentation.
---
*Generated by homelab-export-script v${SCRIPT_VERSION}*
EOF
# Perform variable substitution
eval "cat > \"${readme}\" <<EOF
$(<"${readme}")
EOF"
log SUCCESS "Generated README.md"
}
generate_summary() {
banner "Generating Summary Report"
local summary="${OUTPUT_BASE_DIR}/SUMMARY.md"
cat > "${summary}" <<EOF
# Collection Summary Report
## Collection Metadata
- **Date/Time**: $(date '+%Y-%m-%d %H:%M:%S')
- **Hostname**: $(hostname)
- **Collection Level**: ${COLLECTION_LEVEL}
- **Script Version**: ${SCRIPT_VERSION}
## Sanitization Settings
- **IP Addresses**: ${SANITIZE_IPS}
- **Passwords**: ${SANITIZE_PASSWORDS}
- **Tokens/Keys**: ${SANITIZE_TOKENS}
## Collection Statistics
### Successfully Collected
Total items collected: ${#COLLECTED_ITEMS[@]}
EOF
for item in "${COLLECTED_ITEMS[@]}"; do
echo "- ${item}" >> "${summary}"
done
cat >> "${summary}" <<EOF
### Skipped Items
Total items skipped: ${#SKIPPED_ITEMS[@]}
EOF
if [[ ${#SKIPPED_ITEMS[@]} -gt 0 ]]; then
for item in "${SKIPPED_ITEMS[@]}"; do
echo "- ${item}" >> "${summary}"
done
else
echo "*None*" >> "${summary}"
fi
cat >> "${summary}" <<EOF
### Errors
Total errors: ${#ERROR_ITEMS[@]}
EOF
if [[ ${#ERROR_ITEMS[@]} -gt 0 ]]; then
for item in "${ERROR_ITEMS[@]}"; do
echo "- ${item}" >> "${summary}"
done
else
echo "*None*" >> "${summary}"
fi
cat >> "${summary}" <<EOF
## System Overview
### Proxmox Version
\`\`\`
$(pveversion -v 2>/dev/null || echo "Unable to retrieve version")
\`\`\`
### Virtual Machines
\`\`\`
$(qm list 2>/dev/null || echo "Unable to retrieve VM list")
\`\`\`
### Containers
\`\`\`
$(pct list 2>/dev/null || echo "Unable to retrieve container list")
\`\`\`
### Storage
\`\`\`
$(pvesm status 2>/dev/null || echo "Unable to retrieve storage status")
\`\`\`
### Disk Usage
\`\`\`
$(df -h 2>/dev/null || echo "Unable to retrieve disk usage")
\`\`\`
## Next Actions
1. Review any errors or skipped items above
2. Consult collection.log for detailed information
3. Manually verify sensitive information was sanitized
4. Add this export to your documentation repository
5. Create diagrams and additional documentation in respective folders
---
*Report generated $(date '+%Y-%m-%d %H:%M:%S')*
EOF
log SUCCESS "Generated SUMMARY.md"
}
################################################################################
# Main Collection Orchestration
################################################################################
run_collection() {
banner "Starting Homelab Infrastructure Collection"
log INFO "Collection Level: ${COLLECTION_LEVEL}"
log INFO "Output Directory: ${OUTPUT_BASE_DIR}"
log INFO "Sanitization: IPs=${SANITIZE_IPS} | Passwords=${SANITIZE_PASSWORDS} | Tokens=${SANITIZE_TOKENS}"
# Check if we're on a Proxmox host
if ! check_proxmox; then
log ERROR "This script must be run on a Proxmox VE host"
exit 1
fi
# Create directory structure
create_directory_structure
# System information (always collected)
collect_system_information
# Proxmox configurations (always collected)
collect_proxmox_configs
# VM and container configs (always collected)
collect_vm_configs
collect_lxc_configs
# Network configurations (always collected)
collect_network_configs
# Storage configurations (always collected)
collect_storage_configs
# Backup configurations (standard and above)
if [[ "${COLLECTION_LEVEL}" != "basic" ]]; then
collect_backup_configs
collect_cluster_information
collect_guest_information
fi
# Service configurations (full and paranoid)
collect_service_configs
# Generate documentation
generate_readme
generate_summary
banner "Collection Complete"
log SUCCESS "Total items collected: ${#COLLECTED_ITEMS[@]}"
log INFO "Total items skipped: ${#SKIPPED_ITEMS[@]}"
if [[ ${#ERROR_ITEMS[@]} -gt 0 ]]; then
log WARN "Total errors: ${#ERROR_ITEMS[@]}"
log WARN "Review ${LOG_FILE} for details"
fi
# Compress output if requested
if [[ "${COMPRESS_OUTPUT}" == "true" ]]; then
banner "Compressing Export"
local archive="${OUTPUT_BASE_DIR}.tar.gz"
tar -czf "${archive}" -C "$(dirname "${OUTPUT_BASE_DIR}")" "$(basename "${OUTPUT_BASE_DIR}")" 2>/dev/null && {
log SUCCESS "Created archive: ${archive}"
log INFO "Archive size: $(du -h "${archive}" | cut -f1)"
} || {
log WARN "Failed to create archive"
}
fi
echo ""
echo -e "${BOLD}${GREEN}Export Location:${NC} ${OUTPUT_BASE_DIR}"
echo -e "${BOLD}${GREEN}Summary Report:${NC} ${OUTPUT_BASE_DIR}/SUMMARY.md"
echo -e "${BOLD}${GREEN}Collection Log:${NC} ${LOG_FILE}"
echo ""
}
################################################################################
# Script Usage and Help
################################################################################
usage() {
cat <<EOF
${BOLD}Homelab Infrastructure Collection Script${NC}
Version: ${SCRIPT_VERSION}
${BOLD}USAGE:${NC}
${SCRIPT_NAME} [OPTIONS]
${BOLD}DESCRIPTION:${NC}
Collects Proxmox VE infrastructure configurations, system information,
and exports them in an organized directory structure. This script performs
READ-ONLY operations and makes no modifications to your system.
${BOLD}OPTIONS:${NC}
-l, --level LEVEL Collection level: basic, standard, full, paranoid
Default: standard
-o, --output DIR Output directory (default: ./homelab-export-<timestamp>)
-s, --sanitize WHAT Sanitize sensitive data. Options:
all - Sanitize everything (IPs, passwords, tokens)
ips - Sanitize IP addresses only
none - No sanitization
Default: passwords and tokens only
-c, --compress Compress output to .tar.gz (default: true)
--no-compress Skip compression
-v, --verbose Verbose output
-h, --help Show this help message
${BOLD}COLLECTION LEVELS:${NC}
basic - System info, Proxmox configs, VM/CT configs
standard - Basic + storage, network, backup configs, cluster info
full - Standard + service configs, detailed system state
paranoid - Full + everything possible (experimental)
${BOLD}EXAMPLES:${NC}
# Standard collection with default settings
${SCRIPT_NAME}
# Full collection with complete sanitization
${SCRIPT_NAME} --level full --sanitize all
# Basic collection without compression
${SCRIPT_NAME} --level basic --no-compress
# Custom output location with verbose logging
${SCRIPT_NAME} -o /backup/homelab-export -v
${BOLD}NOTES:${NC}
- Must be run on the Proxmox VE host (or via SSH)
- Requires root privileges for full access to configurations
- Output can be transferred to your documentation repository
- Review SUMMARY.md and collection.log after completion
${BOLD}SECURITY:${NC}
By default, passwords and tokens are sanitized. Use --sanitize all
to also redact IP addresses. Review exported files before committing
to version control or sharing.
For more information, consult the README.md generated with each export.
EOF
}
################################################################################
# Argument Parsing
################################################################################
parse_arguments() {
while [[ $# -gt 0 ]]; do
case "$1" in
-l|--level)
COLLECTION_LEVEL="$2"
shift 2
;;
-o|--output)
OUTPUT_BASE_DIR="$2"
LOG_FILE="${OUTPUT_BASE_DIR}/collection.log"
shift 2
;;
-s|--sanitize)
case "$2" in
all)
SANITIZE_IPS=true
SANITIZE_PASSWORDS=true
SANITIZE_TOKENS=true
;;
ips)
SANITIZE_IPS=true
SANITIZE_PASSWORDS=false
SANITIZE_TOKENS=false
;;
none)
SANITIZE_IPS=false
SANITIZE_PASSWORDS=false
SANITIZE_TOKENS=false
;;
*)
echo "Invalid sanitization option: $2"
usage
exit 1
;;
esac
shift 2
;;
-c|--compress)
COMPRESS_OUTPUT=true
shift
;;
--no-compress)
COMPRESS_OUTPUT=false
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done
# Validate collection level
case "${COLLECTION_LEVEL}" in
basic|standard|full|paranoid)
;;
*)
echo "Invalid collection level: ${COLLECTION_LEVEL}"
usage
exit 1
;;
esac
}
################################################################################
# Main Execution
################################################################################
main() {
parse_arguments "$@"
run_collection
}
# Check if script is being sourced or executed
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi