#!/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=/g' "${file}" 2>/dev/null || true sed -i 's/passwd=.*/passwd=/g' "${file}" 2>/dev/null || true sed -i 's/"password"[[:space:]]*:[[:space:]]*"[^"]*"/"password": ""/g' "${file}" 2>/dev/null || true fi # Sanitize tokens and keys if [[ "${SANITIZE_TOKENS}" == "true" ]]; then sed -i 's/token=.*/token=/g' "${file}" 2>/dev/null || true sed -i 's/api[_-]key=.*/api_key=/g' "${file}" 2>/dev/null || true sed -i 's/secret=.*/secret=/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-/ ├── 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: `-.conf` Firewall rules (if present): `-.fw` ### LXC Containers Each container configuration is named: `-.conf` Firewall rules (if present): `-.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}\" < "${summary}" <> "${summary}" done cat >> "${summary}" <> "${summary}" done else echo "*None*" >> "${summary}" fi cat >> "${summary}" <> "${summary}" done else echo "*None*" >> "${summary}" fi cat >> "${summary}" </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 <) -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