Files
loc_az_hci/scripts/infrastructure/improve-template-9000.sh
defiQUG c39465c2bd
Some checks failed
Test / test (push) Has been cancelled
Initial commit: loc_az_hci (smom-dbis-138 excluded via .gitignore)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 09:04:46 -08:00

337 lines
11 KiB
Bash
Executable File

#!/bin/bash
source ~/.bashrc
# Improve Template VM 9000 with Recommended Enhancements
# This script applies all recommended improvements to template 9000
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
}
PROXMOX_HOST="${PROXMOX_ML110_IP:-192.168.1.206}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
SSH_OPTS="-i $SSH_KEY -o StrictHostKeyChecking=no"
TEMPLATE_VMID=9000
TEMP_VMID=9999
TEMP_VM_NAME="template-update-temp"
VM_USER="${VM_USER:-ubuntu}"
# Check if running on Proxmox host or remotely
if command -v qm >/dev/null 2>&1; then
RUN_LOCAL=true
PROXMOX_CMD=""
else
RUN_LOCAL=false
PROXMOX_CMD="ssh $SSH_OPTS root@$PROXMOX_HOST"
fi
run_proxmox_cmd() {
if [ "$RUN_LOCAL" = true ]; then
eval "$1"
else
ssh $SSH_OPTS "root@$PROXMOX_HOST" "$1"
fi
}
wait_for_ssh() {
local ip="$1"
local max_attempts=30
local attempt=0
log_info "Waiting for SSH to be available on $ip..."
while [ $attempt -lt $max_attempts ]; do
if ssh $SSH_OPTS -o ConnectTimeout=5 "${VM_USER}@${ip}" "echo 'SSH ready'" &>/dev/null; then
log_info "✓ SSH is ready"
return 0
fi
attempt=$((attempt + 1))
sleep 2
done
log_error "SSH not available after $max_attempts attempts"
return 1
}
get_vm_ip() {
local vmid="$1"
local ip=""
# Try to use helper library if available (when running on Proxmox host)
if [ "$RUN_LOCAL" = true ] && [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" 2>/dev/null || true
if command -v get_vm_ip_from_guest_agent &>/dev/null; then
ip=$(get_vm_ip_from_guest_agent "$vmid" 2>/dev/null || echo "")
if [[ -n "$ip" && "$ip" != "null" ]]; then
echo "$ip"
return 0
fi
fi
fi
# Try to get IP from guest agent using jq directly (suppress errors)
if run_proxmox_cmd "command -v jq >/dev/null 2>&1" 2>/dev/null; then
ip=$(run_proxmox_cmd "qm guest cmd $vmid network-get-interfaces 2>/dev/null | jq -r '.[]?.\"ip-addresses\"[]? | select(.[\"ip-address-type\"] == \"ipv4\" and .\"ip-address\" != \"127.0.0.1\") | .\"ip-address\"' | head -n1" 2>/dev/null || echo "")
if [[ -n "$ip" && "$ip" != "null" && "$ip" != "" ]]; then
echo "$ip"
return 0
fi
fi
# Try MAC-based discovery: get VM MAC and match with ARP table
local mac
mac=$(run_proxmox_cmd "qm config $vmid 2>/dev/null | grep -E '^net0:' | cut -d',' -f1 | cut -d'=' -f2 | tr '[:upper:]' '[:lower:]' | tr -d ':'" 2>/dev/null || echo "")
if [[ -n "$mac" ]]; then
# Format MAC for matching (with colons)
local mac_formatted="${mac:0:2}:${mac:2:2}:${mac:4:2}:${mac:6:2}:${mac:8:2}:${mac:10:2}"
# Try to find IP in ARP table
ip=$(run_proxmox_cmd "ip neigh show 2>/dev/null | grep -i '$mac_formatted' | grep -oE '192\.168\.1\.[0-9]+' | head -n1" 2>/dev/null || echo "")
if [[ -n "$ip" ]]; then
echo "$ip"
return 0
fi
fi
# Return empty string (not a warning message)
echo ""
return 1
}
main() {
log_step "Template 9000 Improvement Script"
log_warn "This script will:"
log_warn " 1. Clone template 9000 to temporary VM 9999"
log_warn " 2. Boot the temporary VM"
log_warn " 3. Apply all recommended improvements"
log_warn " 4. Convert back to template"
log_warn " 5. Replace original template 9000"
echo ""
# Check if template exists
if ! run_proxmox_cmd "qm config $TEMPLATE_VMID &>/dev/null"; then
log_error "Template VM $TEMPLATE_VMID not found"
exit 1
fi
# Check if temp VM already exists
if run_proxmox_cmd "qm config $TEMP_VMID &>/dev/null" 2>/dev/null; then
log_warn "Temporary VM $TEMP_VMID already exists. Destroying it..."
run_proxmox_cmd "qm stop $TEMP_VMID" 2>/dev/null || true
sleep 2
run_proxmox_cmd "qm destroy $TEMP_VMID --purge" 2>/dev/null || true
sleep 2
fi
# Step 1: Clone template
log_step "Step 1: Cloning Template to Temporary VM"
log_info "Cloning template $TEMPLATE_VMID to VM $TEMP_VMID..."
run_proxmox_cmd "qm clone $TEMPLATE_VMID $TEMP_VMID --name $TEMP_VM_NAME"
log_info "✓ Template cloned"
# Step 2: Boot temporary VM
log_step "Step 2: Booting Temporary VM"
log_info "Starting VM $TEMP_VMID..."
run_proxmox_cmd "qm start $TEMP_VMID"
log_info "Waiting for VM to boot and get DHCP IP (this may take 60-90 seconds)..."
sleep 60
# Step 3: Get IP and wait for SSH
log_step "Step 3: Getting VM IP and Waiting for SSH"
local vm_ip=""
# Try multiple times to get IP (VM may still be booting)
log_info "Attempting to discover VM IP (may take a few attempts)..."
for attempt in {1..10}; do
vm_ip=$(get_vm_ip "$TEMP_VMID" 2>/dev/null || echo "")
if [[ -n "$vm_ip" ]]; then
log_info "✓ Discovered IP: $vm_ip"
break
fi
if [ $attempt -lt 10 ]; then
log_info "Attempt $attempt/10: Waiting for VM to finish booting..."
sleep 10
fi
done
# If still no IP, try to get from Proxmox API or prompt user
if [[ -z "$vm_ip" ]]; then
log_warn "Could not automatically discover IP via guest agent."
log_info "Please check Proxmox web UI or router DHCP leases for VM $TEMP_VMID IP address."
log_info "You can also check with: ssh root@$PROXMOX_HOST 'qm config $TEMP_VMID'"
echo ""
read -p "Enter the VM IP address (or press Enter to skip and try again later): " vm_ip
if [[ -z "$vm_ip" ]]; then
log_error "IP address required. Exiting."
log_info "VM $TEMP_VMID is running. You can manually:"
log_info " 1. Get the IP from Proxmox UI or router"
log_info " 2. SSH into the VM and apply improvements manually"
log_info " 3. Run this script again with the IP"
exit 1
fi
fi
wait_for_ssh "$vm_ip" || {
log_error "Failed to connect to VM. Please check:"
log_error " 1. VM is booted: qm status $TEMP_VMID"
log_error " 2. IP address is correct: $vm_ip"
log_error " 3. SSH key is correct: $SSH_KEY"
exit 1
}
# Step 4: Apply improvements
log_step "Step 4: Applying Template Improvements"
log_info "Installing essential packages and QEMU Guest Agent..."
ssh $SSH_OPTS "${VM_USER}@${vm_ip}" <<'EOF'
set -e
sudo apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -qq
# Install essential packages
sudo apt-get install -y \
jq \
curl \
wget \
git \
vim \
nano \
net-tools \
htop \
unattended-upgrades \
apt-transport-https \
ca-certificates \
qemu-guest-agent \
ufw
# Enable and start QEMU Guest Agent
sudo systemctl enable qemu-guest-agent
sudo systemctl start qemu-guest-agent
# Configure automatic security updates
echo 'Unattended-Upgrade::Automatic-Reboot "false";' | sudo tee -a /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null
echo 'Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";' | sudo tee -a /etc/apt/apt.conf.d/50unattended-upgrades > /dev/null
# Set timezone
sudo timedatectl set-timezone UTC
# Configure locale
sudo locale-gen en_US.UTF-8
sudo update-locale LANG=en_US.UTF-8
# SSH hardening (disable root login, password auth)
sudo sed -i 's/#PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/#PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/#PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
sudo systemctl restart sshd
# Install UFW (firewall) but don't enable it - let VMs configure as needed
# UFW is installed but not enabled, so VMs can configure firewall rules per their needs
# Clean up disk
sudo apt-get autoremove -y -qq
sudo apt-get autoclean -qq
sudo rm -rf /tmp/*
sudo rm -rf /var/tmp/*
sudo truncate -s 0 /var/log/*.log 2>/dev/null || true
sudo journalctl --vacuum-time=1d --quiet
# Create template version file
echo "template-9000-v1.1.0-$(date +%Y%m%d)" | sudo tee /etc/template-version > /dev/null
echo "✓ All improvements applied"
EOF
if [ $? -ne 0 ]; then
log_error "Failed to apply improvements"
exit 1
fi
log_info "✓ All improvements applied successfully"
# Step 5: Stop VM and convert to template
log_step "Step 5: Converting Back to Template"
log_info "Stopping VM $TEMP_VMID..."
run_proxmox_cmd "qm stop $TEMP_VMID"
sleep 5
log_info "Converting VM $TEMP_VMID to template..."
run_proxmox_cmd "qm template $TEMP_VMID"
log_info "✓ VM converted to template"
# Step 6: Replace original template
log_step "Step 6: Replacing Original Template"
log_warn "This will destroy the original template 9000 and replace it with the improved version"
echo ""
if [ -t 0 ]; then
read -p "Continue? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
log_info "Cancelled. Improved template is available as VM $TEMP_VMID"
log_info "You can manually:"
log_info " 1. Destroy template 9000: qm destroy 9000"
log_info " 2. Change VMID: qm set $TEMP_VMID --vmid 9000"
exit 0
fi
else
log_info "Non-interactive mode: auto-confirming"
fi
log_info "Destroying original template 9000..."
run_proxmox_cmd "qm destroy $TEMPLATE_VMID --purge" 2>/dev/null || true
sleep 2
log_info "Changing VMID from $TEMP_VMID to $TEMPLATE_VMID..."
run_proxmox_cmd "qm set $TEMP_VMID --vmid $TEMPLATE_VMID"
log_step "Template Improvement Complete!"
log_info "✓ Template 9000 has been improved with:"
log_info " - QEMU Guest Agent pre-installed and enabled"
log_info " - Essential utilities (jq, curl, wget, git, vim, nano, htop, net-tools, etc.)"
log_info " - Automatic security updates configured (unattended-upgrades)"
log_info " - Timezone set to UTC"
log_info " - Locale configured (en_US.UTF-8)"
log_info " - SSH hardened (no root login, no password auth, pubkey only)"
log_info " - UFW firewall installed (not enabled - VMs configure as needed)"
log_info " - Disk optimized and cleaned"
log_info " - Template version tracking (/etc/template-version)"
log_info ""
log_info "You can now clone VMs from template 9000 and they will have all these improvements!"
}
main "$@"