Initial commit: loc_az_hci (smom-dbis-138 excluded via .gitignore)
Some checks failed
Test / test (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-08 09:04:46 -08:00
commit c39465c2bd
386 changed files with 50649 additions and 0 deletions

View File

@@ -0,0 +1,272 @@
#!/bin/bash
source ~/.bashrc
# Create All Service VMs via Proxmox API
# Attempts to create VMs using available templates or provides detailed instructions
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
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 -e "${BLUE}[STEP]${NC} $1"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
else
log_error ".env file not found!"
exit 1
fi
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_HOST="${1:-192.168.1.206}"
PROXMOX_URL="https://${PROXMOX_HOST}:8006"
PROXMOX_NODE="${2:-pve}"
# Get authentication ticket
get_ticket() {
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket")
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
echo "$ticket|$csrf"
}
# List available templates
list_templates() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
templates = [v for v in data.get('data', []) if v.get('template') == 1]
if templates:
print('Available Templates:')
for t in templates:
print(f\" - {t.get('name', 'unknown')} (ID: {t.get('vmid', 'N/A')})\")
else:
print('No templates found')
" 2>/dev/null || echo "Could not retrieve templates"
}
# List available ISOs
list_isos() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/storage/local/content")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
isos = [f for f in data.get('data', []) if f.get('content') == 'iso']
if isos:
print('Available ISO Images:')
for iso in isos[:10]:
print(f\" - {iso.get('volid', 'unknown')}\")
else:
print('No ISO images found')
" 2>/dev/null || echo "Could not retrieve ISO images"
}
# Check if VM exists
vm_exists() {
local auth=$1
local vmid=$2
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/cluster/resources?type=vm")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
vms = [v for v in data.get('data', []) if v.get('type') == 'qemu' and str(v.get('vmid')) == '$vmid']
print('true' if vms else 'false')
" 2>/dev/null || echo "false"
}
# Create VM via API (requires template or ISO)
create_vm_api() {
local auth=$1
local vmid=$2
local name=$3
local cores=$4
local memory=$5
local disk_size=$6
local template_or_iso=$7
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_info "Creating VM $name (ID: $vmid) via API..."
# Check if template exists
if [ "$(vm_exists "$auth" "$template_or_iso")" = "true" ]; then
# Clone from template
log_info "Cloning from template $template_or_iso..."
curl -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "newid=$vmid" \
-d "name=$name" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$template_or_iso/clone" 2>/dev/null
# Update VM configuration
curl -k -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "cores=$cores" \
-d "memory=$memory" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>/dev/null
log_info "✓ VM $name created from template"
return 0
else
log_warn "Template/ISO $template_or_iso not found. Cannot create VM via API."
return 1
fi
}
# VM configurations
declare -A VMS=(
["100"]="cloudflare-tunnel:2:4096:40"
["101"]="k3s-master:4:8192:80"
["102"]="git-server:4:8192:100"
["103"]="observability:4:8192:200"
)
main() {
echo "========================================="
echo "Create All Service VMs"
echo "========================================="
echo ""
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
log_info "Connecting to Proxmox: $PROXMOX_URL"
# Authenticate
auth=$(get_ticket)
if [ $? -ne 0 ]; then
exit 1
fi
log_info "Authentication successful"
echo ""
# Check available resources
log_step "Checking available resources..."
list_templates "$auth"
echo ""
list_isos "$auth"
echo ""
# Check existing VMs
log_step "Checking existing VMs..."
for vmid in "${!VMS[@]}"; do
IFS=':' read -r name cores memory disk <<< "${VMS[$vmid]}"
if [ "$(vm_exists "$auth" "$vmid")" = "true" ]; then
log_warn "VM $name (ID: $vmid) already exists"
else
log_info "VM $name (ID: $vmid) - Ready to create"
fi
done
echo ""
log_warn "VM creation via API requires templates or ISOs."
log_info "Generating Proxmox Web UI creation guide..."
echo ""
# Generate creation instructions
cat > /tmp/vm-creation-instructions.txt <<EOF
========================================
VM Creation Instructions for Proxmox Web UI
========================================
Access: $PROXMOX_URL
Login: $PVE_USERNAME / (password from PVE_ROOT_PASS)
For each VM, follow these steps:
1. Click "Create VM" (top right)
2. Fill in the General tab
3. Select OS (ISO image or template)
4. Configure System, Hard Disk, CPU, Memory, Network
5. Use Cloud-Init for network configuration (if available)
VM Configurations:
EOF
for vmid in "${!VMS[@]}"; do
IFS=':' read -r name cores memory disk <<< "${VMS[$vmid]}"
case $vmid in
100) ip="192.168.1.60" ;;
101) ip="192.168.1.188" ;;
102) ip="192.168.1.121" ;;
103) ip="192.168.1.82" ;;
esac
cat >> /tmp/vm-creation-instructions.txt <<EOF
VM: $name
- VM ID: $vmid
- IP Address: $ip
- CPU: $cores cores
- RAM: ${memory}MB
- Disk: ${disk}GB
- Network: vmbr0
- Gateway: 192.168.1.254
EOF
done
cat /tmp/vm-creation-instructions.txt
echo ""
log_info "Full instructions saved to: /tmp/vm-creation-instructions.txt"
log_info "Also see: CREATE_VMS.md"
}
main "$@"

View File

@@ -0,0 +1,157 @@
#!/bin/bash
source ~/.bashrc
# Create First VM (Cloudflare Tunnel) via Proxmox API
# This script helps create a VM using the Proxmox API
set -e
# 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"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
else
log_error ".env file not found!"
exit 1
fi
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_HOST="${1:-192.168.1.206}"
PROXMOX_URL="https://${PROXMOX_HOST}:8006"
PROXMOX_NODE="${2:-pve}"
# Get authentication ticket
get_ticket() {
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket")
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
echo "$ticket|$csrf"
}
# Get next available VM ID
get_next_vmid() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/cluster/nextid")
echo "$response" | grep -o '"data":"[^"]*' | cut -d'"' -f4
}
# Check if VM exists
vm_exists() {
local auth=$1
local vmid=$2
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/cluster/resources?type=vm")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
vms = [v for v in data.get('data', []) if v.get('type') == 'qemu' and str(v.get('vmid')) == '$vmid']
print('true' if vms else 'false')
" 2>/dev/null || echo "false"
}
# Main
echo "========================================="
echo "Create Cloudflare Tunnel VM"
echo "========================================="
echo ""
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
log_info "Connecting to Proxmox: $PROXMOX_URL"
# Authenticate
auth=$(get_ticket)
if [ $? -ne 0 ]; then
exit 1
fi
log_info "Authentication successful"
echo ""
# Check for existing VM
if [ "$(vm_exists "$auth" 100)" = "true" ]; then
log_warn "VM with ID 100 already exists"
read -p "Continue anyway? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0
fi
fi
# Get next VM ID
next_id=$(get_next_vmid "$auth")
log_info "Next available VM ID: $next_id"
echo ""
log_warn "VM creation via Proxmox API requires:"
log_warn " 1. A VM template (e.g., ubuntu-22.04-template)"
log_warn " 2. Or an ISO image uploaded to Proxmox"
echo ""
log_info "Recommended approach:"
echo " 1. Use Proxmox Web UI to create the first VM"
echo " 2. Convert it to a template for future use"
echo " 3. Then use Terraform or API for additional VMs"
echo ""
log_info "Proxmox Web UI: $PROXMOX_URL"
log_info "See CREATE_VMS.md for step-by-step instructions"
echo ""
log_info "VM Configuration for Cloudflare Tunnel:"
echo " - VM ID: 100"
echo " - Name: cloudflare-tunnel"
echo " - IP: 192.168.1.60"
echo " - CPU: 2 cores"
echo " - RAM: 4096 MB"
echo " - Disk: 40GB"
echo " - OS: Ubuntu 22.04 LTS"
echo ""
log_info "After creating the VM:"
echo " 1. Install Ubuntu 22.04 LTS"
echo " 2. Configure static IP: 192.168.1.60"
echo " 3. Run: sudo bash scripts/setup-cloudflare-tunnel.sh"
echo ""

View File

@@ -0,0 +1,408 @@
#!/bin/bash
source ~/.bashrc
# Create Proxmox Mail Gateway VM via Proxmox API using ISO
# Downloads ISO if needed, uploads to Proxmox, and creates VM automatically
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
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 -e "${BLUE}[STEP]${NC} $1"
}
log_header() {
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}========================================${NC}"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
else
log_error ".env file not found!"
exit 1
fi
# Configuration
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_HOST="${1:-192.168.1.206}"
PROXMOX_URL="https://${PROXMOX_HOST}:8006"
PROXMOX_NODE="${2:-pve}"
STORAGE_POOL="${3:-local}"
# PMG VM Configuration
VMID=105
VM_NAME="proxmox-mail-gateway"
CORES=2
MEMORY=4096
DISK_SIZE="50G"
# ISO Configuration
ISO_FILE="proxmox-mail-gateway_9.0-1.iso"
ISO_URL="https://enterprise.proxmox.com/iso/proxmox-mail-gateway_9.0-1.iso"
ISO_DIR="${4:-./downloads/iso}"
ISO_PATH="${ISO_DIR}/${ISO_FILE}"
# Get authentication ticket
get_ticket() {
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket")
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
echo "$ticket|$csrf"
}
# Download ISO if not present
download_iso() {
if [ -f "$ISO_PATH" ]; then
log_info "ISO already exists locally: $ISO_PATH"
ISO_SIZE=$(du -h "$ISO_PATH" | cut -f1)
log_info "ISO size: $ISO_SIZE"
return 0
fi
log_step "Downloading PMG ISO..."
log_info "URL: $ISO_URL"
log_info "Destination: $ISO_PATH"
log_warn "This may take several minutes depending on network speed..."
# Create ISO directory if it doesn't exist
mkdir -p "$ISO_DIR"
# Download ISO with progress
if command -v wget >/dev/null 2>&1; then
wget --progress=bar:force -O "$ISO_PATH" "$ISO_URL" 2>&1 | grep --line-buffered -oP '\d+%' | while read -r line; do
echo -ne "\r${GREEN}[INFO]${NC} Download progress: $line"
done
echo ""
elif command -v curl >/dev/null 2>&1; then
curl -L --progress-bar -o "$ISO_PATH" "$ISO_URL"
else
log_error "Neither wget nor curl is available. Cannot download ISO."
return 1
fi
if [ ! -f "$ISO_PATH" ]; then
log_error "ISO download failed"
return 1
fi
ISO_SIZE=$(du -h "$ISO_PATH" | cut -f1)
log_info "✓ ISO downloaded successfully: $ISO_SIZE"
return 0
}
# Check if ISO exists locally
check_iso() {
if [ ! -f "$ISO_PATH" ]; then
log_warn "ISO file not found: $ISO_PATH"
log_info "Attempting to download from: $ISO_URL"
if ! download_iso; then
log_error "Failed to download ISO. Please download manually and place it at: $ISO_PATH"
exit 1
fi
else
log_info "Found ISO: $ISO_PATH"
ISO_SIZE=$(du -h "$ISO_PATH" | cut -f1)
log_info "ISO size: $ISO_SIZE"
fi
}
# Check if ISO already exists in Proxmox
iso_exists() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/storage/${STORAGE_POOL}/content")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
isos = [f for f in data.get('data', []) if f.get('content') == 'iso' and '$ISO_FILE' in f.get('volid', '')]
print('true' if isos else 'false')
" 2>/dev/null || echo "false"
}
# Upload ISO to Proxmox
upload_iso() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_step "Uploading ISO to Proxmox..."
log_warn "This may take several minutes depending on ISO size and network speed..."
# Upload ISO using multipart form
local result=$(curl -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-F "content=iso" \
-F "filename=@$ISO_PATH" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/storage/${STORAGE_POOL}/upload" 2>&1)
if echo "$result" | grep -q "error"; then
log_error "ISO upload failed: $result"
return 1
fi
log_info "✓ ISO uploaded successfully"
return 0
}
# Check if VM exists
vm_exists() {
local auth=$1
local vmid=$2
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/cluster/resources?type=vm")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
vms = [v for v in data.get('data', []) if v.get('type') == 'qemu' and str(v.get('vmid')) == '$vmid']
print('true' if vms else 'false')
" 2>/dev/null || echo "false"
}
# Create VM via API
create_vm() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_step "Creating VM: $VM_NAME (ID: $VMID)..."
# First, verify ISO exists in storage
local iso_volid="${STORAGE_POOL}:iso/${ISO_FILE}"
log_info "Using ISO: $iso_volid"
# Strategy: Create VM with minimal config, then add hardware via separate API calls
log_info "Step 1: Creating VM skeleton..."
local create_response=$(curl -k -s -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "vmid=$VMID" \
-d "name=$VM_NAME" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu" 2>&1)
if echo "$create_response" | grep -q '"errors"'; then
log_error "Failed to create VM skeleton:"
echo "$create_response" | python3 -c "import sys, json; d=json.load(sys.stdin); print(json.dumps(d.get('errors', {}), indent=2))" 2>/dev/null || echo "$create_response"
return 1
fi
log_info "✓ VM skeleton created"
sleep 1
# Step 2: Configure basic VM settings
log_info "Step 2: Configuring CPU and memory..."
curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "cores=$CORES" \
-d "memory=$MEMORY" \
-d "ostype=l26" \
-d "agent=1" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/config" > /dev/null 2>&1
# Step 3: Add disk
log_info "Step 3: Adding disk..."
local disk_response=$(curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "scsi0=${STORAGE_POOL}:${DISK_SIZE}" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/config" 2>&1)
if echo "$disk_response" | grep -q '"errors"'; then
log_warn "Disk configuration warning (continuing anyway)"
fi
# Step 4: Add ISO
log_info "Step 4: Adding ISO..."
local iso_response=$(curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "ide2=$iso_volid" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/config" 2>&1)
if echo "$iso_response" | grep -q '"errors"'; then
log_warn "ISO configuration warning (continuing anyway)"
fi
# Step 5: Add network (DHCP configuration)
log_info "Step 5: Adding network (DHCP)..."
local net_response=$(curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "net0=model=virtio,bridge=vmbr0" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/config" 2>&1)
if echo "$net_response" | grep -q '"errors"'; then
log_warn "Network configuration warning (may need manual configuration)"
fi
# Step 6: Set boot order
log_info "Step 6: Configuring boot order..."
curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "boot=order=ide2" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/config" > /dev/null 2>&1
# Step 7: Add tags
log_info "Step 7: Adding tags..."
curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "tags=mail;security;gateway" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/config" > /dev/null 2>&1
# Verify VM config file was created
sleep 2
local verify_response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/config" 2>&1)
if echo "$verify_response" | grep -q '"errors"'; then
log_error "VM $VM_NAME was not created properly. Config file missing."
log_error "Response: $verify_response"
return 1
fi
log_info "✓ VM $VM_NAME created successfully"
return 0
}
# Start VM
start_vm() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_info "Starting VM $VMID..."
local start_response=$(curl -k -s -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$VMID/status/start")
if echo "$start_response" | grep -q '"error"'; then
log_warn "Failed to start VM $VMID: $start_response"
return 1
fi
log_info "✓ VM $VMID started"
return 0
}
main() {
log_header "Create Proxmox Mail Gateway VM"
echo ""
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
# Check/download ISO file
check_iso
echo ""
# Authenticate
log_step "Authenticating with Proxmox..."
auth=$(get_ticket)
if [ $? -ne 0 ]; then
exit 1
fi
log_info "✓ Authentication successful"
echo ""
# Check if VM already exists
if [ "$(vm_exists "$auth" "$VMID")" = "true" ]; then
log_warn "VM $VM_NAME (ID: $VMID) already exists. Skipping creation..."
log_info "To recreate, delete the VM first via Proxmox Web UI or API"
exit 0
fi
# Check if ISO already uploaded
if [ "$(iso_exists "$auth")" = "true" ]; then
log_info "✓ ISO already exists in Proxmox storage"
else
# Upload ISO
if ! upload_iso "$auth"; then
log_error "Failed to upload ISO"
exit 1
fi
fi
echo ""
# Create VM
log_step "Creating VM..."
echo ""
if create_vm "$auth"; then
# Start VM
start_vm "$auth"
echo ""
log_header "VM Creation Complete"
echo ""
log_info "Proxmox Mail Gateway VM has been created and started!"
echo ""
log_info "VM Details:"
echo " - Name: $VM_NAME"
echo " - ID: $VMID"
echo " - Cores: $CORES"
echo " - Memory: ${MEMORY}MB"
echo " - Disk: $DISK_SIZE"
echo " - Network: DHCP (vmbr0)"
echo ""
log_info "Next steps:"
echo " 1. Access VM console via Proxmox Web UI: $PROXMOX_URL"
echo " 2. Complete Proxmox Mail Gateway installation via console"
echo " 3. Configure PMG after installation completes"
echo ""
else
log_error "Failed to create VM $VM_NAME"
exit 1
fi
}
main "$@"

View File

@@ -0,0 +1,94 @@
#!/bin/bash
source ~/.bashrc
# Create Proxmox Cloud-Init Template from Ubuntu Cloud Image
set -e
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
fi
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="${PROXMOX_URL:-https://192.168.1.206:8006}"
PROXMOX_NODE="${PROXMOX_NODE:-pve}"
STORAGE="${STORAGE:-local-lvm}"
CLOUD_IMAGE="${1:-./downloads/ubuntu-24.04-server-cloudimg-amd64.img}"
TEMPLATE_NAME="${2:-ubuntu-24.04-cloudinit}"
TEMPLATE_ID="${3:-9000}"
main() {
echo "========================================="
echo "Create Proxmox Cloud-Init Template"
echo "========================================="
echo ""
if [ ! -f "$CLOUD_IMAGE" ]; then
log_error "Cloud image not found: $CLOUD_IMAGE"
log_info "Download it first: ./scripts/download-ubuntu-cloud-image.sh"
exit 1
fi
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
log_step "Creating template from: $CLOUD_IMAGE"
log_info "Template name: $TEMPLATE_NAME"
log_info "Template ID: $TEMPLATE_ID"
log_info "Storage: $STORAGE"
echo ""
log_info "This script provides instructions for manual template creation."
log_info "Proxmox Web UI method is more reliable for template creation."
echo ""
log_step "Manual Steps (Recommended):"
echo ""
echo "1. Upload Cloud Image to Proxmox:"
echo " - Proxmox Web UI → Datacenter → $PROXMOX_NODE → Storage"
echo " - Select storage → Content → Upload"
echo " - Upload: $CLOUD_IMAGE"
echo ""
echo "2. Create VM from Cloud Image:"
echo " - Create VM (ID: $TEMPLATE_ID)"
echo " - Import disk from uploaded image"
echo " - Configure Cloud-Init settings"
echo ""
echo "3. Convert to Template:"
echo " - Right-click VM → Convert to Template"
echo ""
echo "4. Use Template:"
echo " - Clone template to create new VMs"
echo " - Configure Cloud-Init on clone"
echo ""
log_info "See: docs/proxmox-ubuntu-images.md for detailed instructions"
}
main "$@"

View File

@@ -0,0 +1,90 @@
#!/bin/bash
source ~/.bashrc
# Quick Template Creation Guide
# This provides step-by-step instructions for creating the template
set -e
cat <<'EOF'
========================================
Ubuntu Cloud-Init Template Creation
========================================
This guide will help you create a Ubuntu Cloud-Init template in Proxmox.
STEP 1: Download Ubuntu Cloud Image
------------------------------------
Run this command to download the image:
wget https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img
Or use the script:
./scripts/download-ubuntu-cloud-image.sh 24.04
STEP 2: Upload to Proxmox
--------------------------
1. Open Proxmox Web UI: https://192.168.1.206:8006
2. Go to: Datacenter → pve → Storage → local
3. Click "Upload" button
4. Select the downloaded .img file
5. Wait for upload to complete (may take a few minutes)
STEP 3: Create VM from Image
------------------------------
1. Click "Create VM" (top right)
2. General:
- VM ID: 9000
- Name: ubuntu-24.04-cloudinit
- Click "Next"
3. OS:
- Select "Do not use any media"
- Click "Next"
4. System:
- Keep defaults
- Click "Next"
5. Disks:
- Delete the default disk
- Click "Add" → "Hard Disk"
- Storage: local
- Import from: Select the uploaded .img file
- Disk size: 20GB (minimum)
- Click "Add"
- Click "Next"
6. CPU:
- Cores: 2
- Click "Next"
7. Memory:
- Memory: 2048 MB
- Click "Next"
8. Network:
- Bridge: vmbr0
- Model: VirtIO
- Click "Next"
9. Confirm:
- Review settings
- Click "Finish"
STEP 4: Configure Cloud-Init
-----------------------------
1. Select the VM (9000)
2. Go to "Options" tab
3. Click "Cloud-Init"
4. Configure:
- User: ubuntu
- Password: (leave empty, use SSH keys)
- SSH Public Keys: Paste your public key
- Click "OK"
STEP 5: Convert to Template
----------------------------
1. Right-click on VM 9000
2. Select "Convert to Template"
3. Confirm
Done! Template is ready.
Now you can run:
./scripts/recreate-vms-from-template.sh
EOF

View File

@@ -0,0 +1,221 @@
#!/bin/bash
source ~/.bashrc
# Create Ubuntu Cloud-Init Template via Proxmox API
# This attempts to automate template creation as much as possible
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_header() {
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}========================================${NC}"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
fi
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="https://192.168.1.206:8006"
PROXMOX_NODE="pve"
STORAGE="${STORAGE:-local}"
TEMPLATE_ID=9000
TEMPLATE_NAME="ubuntu-24.04-cloudinit"
CLOUD_IMAGE="ubuntu-24.04-server-cloudimg-amd64.img"
IMAGE_PATH="./downloads/${CLOUD_IMAGE}"
# Get authentication ticket
get_ticket() {
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket")
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
echo "$ticket|$csrf"
}
# Check if image is uploaded
check_image_uploaded() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/storage/$STORAGE/content")
if echo "$response" | grep -q "$CLOUD_IMAGE"; then
return 0
else
return 1
fi
}
# Upload image to Proxmox
upload_image() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_step "Uploading cloud image to Proxmox..."
if [ ! -f "$IMAGE_PATH" ]; then
log_error "Cloud image not found: $IMAGE_PATH"
return 1
fi
log_warn "Image upload via API is complex. Please upload manually:"
log_info "1. Proxmox Web UI → Storage → $STORAGE → Upload"
log_info "2. Select file: $IMAGE_PATH"
log_info "3. Wait for upload to complete"
echo ""
read -p "Press Enter after image is uploaded..."
if check_image_uploaded "$auth"; then
log_info "✓ Image uploaded"
return 0
else
log_warn "Image upload not detected. Please verify manually."
return 1
fi
}
# Create VM from uploaded image
create_vm_from_image() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_step "Creating VM $TEMPLATE_ID from cloud image..."
# Check if VM already exists
local existing=$(curl -k -s \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_ID/config" 2>&1)
if echo "$existing" | grep -q '"name"'; then
log_warn "VM $TEMPLATE_ID already exists"
read -p "Delete and recreate? (y/N): " confirm
if [ "$confirm" != "y" ]; then
return 0
fi
# Delete existing VM
curl -k -s -X DELETE \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_ID" > /dev/null 2>&1
sleep 2
fi
log_warn "VM creation from image requires manual steps in Proxmox Web UI:"
echo ""
log_info "1. Create VM:"
log_info " • Click 'Create VM'"
log_info " • VM ID: $TEMPLATE_ID"
log_info " • Name: $TEMPLATE_NAME"
log_info " • OS: 'Do not use any media'"
log_info " • Delete default disk"
log_info " • Add disk: Import from $CLOUD_IMAGE"
log_info " • CPU: 2, Memory: 2048MB"
log_info " • Network: vmbr0, VirtIO"
echo ""
log_info "2. Configure Cloud-Init:"
log_info " • Options → Cloud-Init"
log_info " • User: ubuntu"
log_info " • SSH Public Key: $(cat ~/.ssh/id_rsa.pub 2>/dev/null | head -1 || echo 'Your SSH key')"
echo ""
log_info "3. Convert to Template:"
log_info " • Right-click VM → Convert to Template"
echo ""
read -p "Press Enter after template is created..."
# Verify template exists
local template_check=$(curl -k -s \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_ID/config" 2>&1)
if echo "$template_check" | grep -q '"template".*1'; then
log_info "✓ Template created successfully"
return 0
else
log_warn "Template verification failed. Please check manually."
return 1
fi
}
main() {
log_header "Create Ubuntu Cloud-Init Template"
echo ""
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
if [ ! -f "$IMAGE_PATH" ]; then
log_error "Cloud image not found: $IMAGE_PATH"
log_info "Download it first: ./scripts/download-ubuntu-cloud-image.sh 24.04"
exit 1
fi
# Authenticate
auth=$(get_ticket)
if [ $? -ne 0 ]; then
exit 1
fi
# Step 1: Upload image
if ! check_image_uploaded "$auth"; then
upload_image "$auth"
else
log_info "✓ Image already uploaded"
fi
# Step 2: Create VM and convert to template
create_vm_from_image "$auth"
log_header "Template Creation Complete!"
echo ""
log_info "Template $TEMPLATE_ID is ready"
log_info "You can now run: ./scripts/recreate-vms-from-template.sh"
}
main "$@"

View File

@@ -0,0 +1,722 @@
#!/bin/bash
source ~/.bashrc
# Create Proxmox VM from QCOW2/RAW Image - Comprehensive Automation Script
#
# This script automates the complete workflow for creating a VM from any disk image
# in Proxmox VE using the qm command-line interface.
#
# Reference: https://pve.proxmox.com/pve-docs/qm.1.html
#
# Usage:
# ./scripts/create-vm-from-image.sh --vmid 9000 --name "ubuntu-24.04" \
# --image /path/to/image.img --storage local-lvm
set -e
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Logging functions
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 -e "${BLUE}[STEP]${NC} $1"
}
# Default values
VMID=""
VMNAME=""
IMAGE=""
STORAGE="local-lvm"
MEMORY=4096
CORES=2
BRIDGE="vmbr0"
VLAN_TAG=""
ENABLE_CLOUD_INIT=false
ENABLE_UEFI=false
ENABLE_TEMPLATE=false
ENABLE_SERIAL=false
CIUSER=""
CIPASSWORD=""
SSHKEY=""
IPCONFIG=""
NAMESERVER=""
SEARCHDOMAIN=""
CPU_TYPE="host"
ENABLE_AGENT=true
IOTHREAD=true
CACHE_MODE="none"
ENABLE_DISCARD=false
BALLOON=0
DESCRIPTION=""
TAGS=""
NODE=""
DRY_RUN=false
# Load environment variables from .env if available
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
fi
# Parse command line arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
--vmid)
VMID="$2"
shift 2
;;
--name)
VMNAME="$2"
shift 2
;;
--image)
IMAGE="$2"
shift 2
;;
--storage)
STORAGE="$2"
shift 2
;;
--memory)
MEMORY="$2"
shift 2
;;
--cores)
CORES="$2"
shift 2
;;
--bridge)
BRIDGE="$2"
shift 2
;;
--vlan)
VLAN_TAG="$2"
shift 2
;;
--cloud-init)
ENABLE_CLOUD_INIT=true
shift
;;
--uefi)
ENABLE_UEFI=true
shift
;;
--template)
ENABLE_TEMPLATE=true
shift
;;
--serial)
ENABLE_SERIAL=true
shift
;;
--ciuser)
CIUSER="$2"
shift 2
;;
--cipassword)
CIPASSWORD="$2"
shift 2
;;
--sshkey)
SSHKEY="$2"
shift 2
;;
--sshkey-file)
if [ -f "$2" ]; then
SSHKEY="$(cat "$2")"
else
log_error "SSH key file not found: $2"
exit 1
fi
shift 2
;;
--ipconfig)
IPCONFIG="$2"
shift 2
;;
--nameserver)
NAMESERVER="$2"
shift 2
;;
--searchdomain)
SEARCHDOMAIN="$2"
shift 2
;;
--cpu)
CPU_TYPE="$2"
shift 2
;;
--no-agent)
ENABLE_AGENT=false
shift
;;
--no-iothread)
IOTHREAD=false
shift
;;
--cache)
CACHE_MODE="$2"
shift 2
;;
--discard)
ENABLE_DISCARD=true
shift
;;
--balloon)
BALLOON="$2"
shift 2
;;
--description)
DESCRIPTION="$2"
shift 2
;;
--tags)
TAGS="$2"
shift 2
;;
--node)
NODE="$2"
shift 2
;;
--dry-run)
DRY_RUN=true
shift
;;
--help)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
}
# Show help message
show_help() {
cat << EOF
Create Proxmox VM from QCOW2/RAW Image
Usage: $0 [OPTIONS]
Required Options:
--vmid ID VM ID (e.g., 9000)
--name NAME VM name (e.g., "ubuntu-24.04-cloudinit")
--image PATH Full path to image file
Optional Options:
--storage STORAGE Storage pool (default: local-lvm)
--memory MB Memory in MB (default: 4096)
--cores NUM CPU cores (default: 2)
--bridge BRIDGE Network bridge (default: vmbr0)
--vlan TAG VLAN tag number
Cloud-Init Options:
--cloud-init Enable Cloud-Init support
--ciuser USER Cloud-Init username
--cipassword PASS Cloud-Init password (not recommended)
--sshkey KEY SSH public key (or use --sshkey-file)
--sshkey-file FILE Read SSH key from file
--ipconfig CONFIG IP configuration (e.g., "ip=192.168.1.100/24,gw=192.168.1.1")
--nameserver DNS DNS servers (space-separated)
--searchdomain DOMAIN Search domains
VM Configuration:
--uefi Enable UEFI/OVMF (recommended for modern images)
--cpu TYPE CPU type (default: host, options: host, kvm64, etc.)
--no-agent Disable QEMU Guest Agent
--no-iothread Disable IO thread
--cache MODE Disk cache mode (none, writeback, writethrough)
--discard Enable discard (for thin provisioning)
--balloon MB Memory balloon size in MB
Other Options:
--template Convert to template after creation
--serial Enable serial console
--description TEXT VM description
--tags TAGS Tags (comma-separated, e.g., "dev,web")
--node NODE Target Proxmox node
--dry-run Show commands without executing
--help Show this help message
Examples:
# Basic VM creation
$0 --vmid 9000 --name "ubuntu-24.04" \\
--image /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img
# Full cloud-init VM
$0 --vmid 9000 --name "ubuntu-24.04-cloudinit" \\
--image /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img \\
--storage local-lvm --memory 4096 --cores 2 \\
--cloud-init --uefi --serial \\
--ciuser ubuntu --sshkey-file ~/.ssh/id_rsa.pub \\
--ipconfig "ip=192.168.1.100/24,gw=192.168.1.1"
# Create and convert to template
$0 --vmid 9000 --name "ubuntu-template" \\
--image /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img \\
--cloud-init --uefi --template \\
--ciuser ubuntu --sshkey-file ~/.ssh/id_rsa.pub
EOF
}
# Validate required arguments
validate_args() {
if [ -z "$VMID" ]; then
log_error "VMID is required. Use --vmid option."
exit 1
fi
if [ -z "$VMNAME" ]; then
log_error "VM name is required. Use --name option."
exit 1
fi
if [ -z "$IMAGE" ]; then
log_error "Image path is required. Use --image option."
exit 1
fi
if [ ! -f "$IMAGE" ]; then
log_error "Image file not found: $IMAGE"
exit 1
fi
# Validate VMID is numeric
if ! [[ "$VMID" =~ ^[0-9]+$ ]]; then
log_error "VMID must be numeric: $VMID"
exit 1
fi
# Check if VMID already exists
if qm list | grep -q "^\s*$VMID\s"; then
log_error "VM with ID $VMID already exists"
exit 1
fi
# Validate storage exists
if ! pvesm status | grep -q "^$STORAGE\s"; then
log_warn "Storage '$STORAGE' not found in pvesm status"
log_info "Available storage:"
pvesm status
log_warn "Continuing anyway..."
fi
}
# Validate image
validate_image() {
log_step "Validating image: $IMAGE"
# Check image format
if ! command -v qemu-img &> /dev/null; then
log_warn "qemu-img not found, skipping image validation"
return
fi
local image_info
image_info=$(qemu-img info "$IMAGE" 2>&1)
if [ $? -ne 0 ]; then
log_error "Failed to read image: $IMAGE"
log_error "$image_info"
exit 1
fi
log_info "Image format: $(echo "$image_info" | grep "file format" | awk '{print $3}')"
log_info "Virtual size: $(echo "$image_info" | grep "virtual size" | awk -F'[()]' '{print $2}')"
}
# Create VM shell
create_vm_shell() {
log_step "Creating VM shell (ID: $VMID, Name: $VMNAME)"
local cmd="qm create $VMID --name \"$VMNAME\" --memory $MEMORY --cores $CORES"
# Add node if specified
if [ -n "$NODE" ]; then
cmd="$cmd --target $NODE"
fi
# Configure network
if [ -n "$VLAN_TAG" ]; then
cmd="$cmd --net0 virtio,bridge=$BRIDGE,tag=$VLAN_TAG"
else
cmd="$cmd --net0 virtio,bridge=$BRIDGE"
fi
# Configure CPU
cmd="$cmd --cpu $CPU_TYPE"
# Enable agent
if [ "$ENABLE_AGENT" = true ]; then
cmd="$cmd --agent 1"
fi
# Add description if provided
if [ -n "$DESCRIPTION" ]; then
cmd="$cmd --description \"$DESCRIPTION\""
fi
# Add tags if provided
if [ -n "$TAGS" ]; then
cmd="$cmd --tags $TAGS"
fi
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
log_info "✓ VM shell created"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
}
# Import disk
import_disk() {
log_step "Importing disk from image: $IMAGE"
local cmd="qm importdisk $VMID \"$IMAGE\" $STORAGE"
log_info "Command: $cmd"
log_info "This may take several minutes depending on image size..."
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
log_info "✓ Disk imported"
# Get the volume name (usually vm-<VMID>-disk-0)
local volume_name="vm-${VMID}-disk-0"
log_info "Imported volume: $volume_name"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
}
# Attach disk
attach_disk() {
log_step "Attaching imported disk"
local volume_name="vm-${VMID}-disk-0"
local cmd="qm set $VMID --scsihw virtio-scsi-pci --scsi0 ${STORAGE}:${volume_name}"
# Add IO thread if enabled
if [ "$IOTHREAD" = true ]; then
cmd="$cmd --iothread 1"
fi
# Add cache mode
cmd="$cmd --cache $CACHE_MODE"
# Add discard if enabled
if [ "$ENABLE_DISCARD" = true ]; then
cmd="$cmd --discard on"
fi
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
log_info "✓ Disk attached"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
}
# Configure boot
configure_boot() {
log_step "Configuring boot settings"
local cmd="qm set $VMID --boot order=scsi0"
# Configure BIOS/UEFI
if [ "$ENABLE_UEFI" = true ]; then
cmd="$cmd --bios ovmf --efidisk0 ${STORAGE}:1,format=raw"
log_info "UEFI/OVMF enabled"
else
cmd="$cmd --bios seabios"
log_info "BIOS (SeaBIOS) enabled"
fi
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
log_info "✓ Boot configured"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
}
# Configure Cloud-Init
configure_cloud_init() {
log_step "Configuring Cloud-Init"
# Add Cloud-Init drive
local cmd="qm set $VMID --ide2 ${STORAGE}:cloudinit"
# Enable serial console if requested
if [ "$ENABLE_SERIAL" = true ] || [ "$ENABLE_CLOUD_INIT" = true ]; then
cmd="$cmd --serial0 socket --vga serial0"
fi
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
# Configure Cloud-Init user
if [ -n "$CIUSER" ]; then
cmd="qm set $VMID --ciuser $CIUSER"
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
fi
# Configure password (if provided, but not recommended)
if [ -n "$CIPASSWORD" ]; then
cmd="qm set $VMID --cipassword \"$CIPASSWORD\""
log_warn "Setting password via Cloud-Init (not recommended, use SSH keys instead)"
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
fi
# Configure SSH key
if [ -n "$SSHKEY" ]; then
cmd="qm set $VMID --sshkey \"$SSHKEY\""
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
log_info "✓ SSH key configured"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
fi
# Configure IP
if [ -n "$IPCONFIG" ]; then
cmd="qm set $VMID --ipconfig0 $IPCONFIG"
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
fi
# Configure DNS
if [ -n "$NAMESERVER" ]; then
cmd="qm set $VMID --nameserver \"$NAMESERVER\""
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
fi
# Configure search domain
if [ -n "$SEARCHDOMAIN" ]; then
cmd="qm set $VMID --searchdomain \"$SEARCHDOMAIN\""
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
fi
if [ "$DRY_RUN" = false ]; then
log_info "✓ Cloud-Init configured"
fi
}
# Configure memory balloon
configure_balloon() {
if [ "$BALLOON" -gt 0 ]; then
log_step "Configuring memory balloon: ${BALLOON}MB"
local cmd="qm set $VMID --balloon $BALLOON"
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
log_info "✓ Memory balloon configured"
else
log_info "[DRY RUN] Would execute: $cmd"
fi
fi
}
# Start VM
start_vm() {
log_step "Starting VM"
local cmd="qm start $VMID"
log_info "Command: $cmd"
if [ "$DRY_RUN" = false ]; then
eval "$cmd"
log_info "✓ VM started"
# Show status
sleep 2
qm status $VMID
else
log_info "[DRY RUN] Would execute: $cmd"
fi
}
# Convert to template
convert_to_template() {
if [ "$ENABLE_TEMPLATE" = false ]; then
return
fi
log_step "Converting VM to template"
log_warn "VM must be shut down before converting to template"
if [ "$DRY_RUN" = false ]; then
# Check if VM is running
local status
status=$(qm status $VMID 2>&1 | grep "status:" | awk '{print $2}')
if [ "$status" = "running" ]; then
log_info "VM is running. Shutting down..."
qm shutdown $VMID
log_info "Waiting for shutdown (this may take a minute)..."
local max_wait=60
local waited=0
while [ $waited -lt $max_wait ]; do
status=$(qm status $VMID 2>&1 | grep "status:" | awk '{print $2}')
if [ "$status" != "running" ]; then
break
fi
sleep 2
waited=$((waited + 2))
echo -n "."
done
echo ""
fi
# Convert to template
qm template $VMID
log_info "✓ VM converted to template"
else
log_info "[DRY RUN] Would execute: qm shutdown $VMID && qm template $VMID"
fi
}
# Main function
main() {
echo "========================================="
echo "Create Proxmox VM from Image"
echo "========================================="
echo ""
parse_args "$@"
if [ "$DRY_RUN" = true ]; then
log_warn "DRY RUN MODE - No changes will be made"
echo ""
fi
validate_args
validate_image
echo ""
log_info "VM Configuration:"
log_info " VMID: $VMID"
log_info " Name: $VMNAME"
log_info " Image: $IMAGE"
log_info " Storage: $STORAGE"
log_info " Memory: ${MEMORY}MB"
log_info " Cores: $CORES"
log_info " Bridge: $BRIDGE"
[ -n "$VLAN_TAG" ] && log_info " VLAN: $VLAN_TAG"
[ "$ENABLE_CLOUD_INIT" = true ] && log_info " Cloud-Init: Enabled"
[ "$ENABLE_UEFI" = true ] && log_info " UEFI: Enabled"
[ "$ENABLE_TEMPLATE" = true ] && log_info " Convert to Template: Yes"
echo ""
if [ "$DRY_RUN" = false ]; then
read -p "Continue with VM creation? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Aborted by user"
exit 0
fi
echo ""
fi
create_vm_shell
import_disk
attach_disk
configure_boot
if [ "$ENABLE_CLOUD_INIT" = true ]; then
configure_cloud_init
fi
configure_balloon
if [ "$ENABLE_TEMPLATE" = false ]; then
start_vm
else
log_info "Skipping VM start (will be converted to template)"
fi
convert_to_template
echo ""
log_info "========================================="
log_info "VM Creation Complete!"
log_info "========================================="
if [ "$ENABLE_TEMPLATE" = false ] && [ "$DRY_RUN" = false ]; then
echo ""
log_info "VM Status:"
qm status $VMID
echo ""
log_info "View VM console: qm terminal $VMID"
log_info "View VM config: qm config $VMID"
fi
}
# Run main function
main "$@"

View File

@@ -0,0 +1,519 @@
#!/bin/bash
source ~/.bashrc
# Create Proxmox Cloud-Init Template with Best Practices
#
# This script creates an optimized cloud-init template for Proxmox VE
# following best practices for template management and cloud-init configuration.
#
# Reference: https://pve.proxmox.com/pve-docs/qm.1.html
set -e
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m'
# Logging functions
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 -e "${BLUE}[STEP]${NC} $1"
}
# Default values
VMID=""
TEMPLATE_NAME=""
IMAGE=""
STORAGE="local-lvm"
MEMORY=2048
CORES=2
BRIDGE="vmbr0"
CIUSER="ubuntu"
SSHKEY=""
SSHKEY_FILE=""
IPCONFIG="ip=dhcp"
NAMESERVER=""
SEARCHDOMAIN=""
DESCRIPTION=""
TAGS="template,cloud-init"
NODE=""
SKIP_VERIFICATION=false
OPTIMIZE_TEMPLATE=true
# Load environment variables from .env if available
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
fi
# Parse command line arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
--vmid)
VMID="$2"
shift 2
;;
--name)
TEMPLATE_NAME="$2"
shift 2
;;
--image)
IMAGE="$2"
shift 2
;;
--storage)
STORAGE="$2"
shift 2
;;
--memory)
MEMORY="$2"
shift 2
;;
--cores)
CORES="$2"
shift 2
;;
--bridge)
BRIDGE="$2"
shift 2
;;
--ciuser)
CIUSER="$2"
shift 2
;;
--sshkey)
SSHKEY="$2"
shift 2
;;
--sshkey-file)
SSHKEY_FILE="$2"
shift 2
;;
--ipconfig)
IPCONFIG="$2"
shift 2
;;
--nameserver)
NAMESERVER="$2"
shift 2
;;
--searchdomain)
SEARCHDOMAIN="$2"
shift 2
;;
--description)
DESCRIPTION="$2"
shift 2
;;
--tags)
TAGS="$2"
shift 2
;;
--node)
NODE="$2"
shift 2
;;
--skip-verification)
SKIP_VERIFICATION=true
shift
;;
--no-optimize)
OPTIMIZE_TEMPLATE=false
shift
;;
--help)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
show_help
exit 1
;;
esac
done
}
# Show help message
show_help() {
cat << EOF
Create Proxmox Cloud-Init Template with Best Practices
Usage: $0 [OPTIONS]
Required Options:
--vmid ID VM ID (e.g., 9000)
--name NAME Template name (e.g., "ubuntu-24.04-cloudinit")
--image PATH Full path to cloud image file
Optional Options:
--storage STORAGE Storage pool (default: local-lvm)
--memory MB Memory in MB (default: 2048, minimal for template)
--cores NUM CPU cores (default: 2)
--bridge BRIDGE Network bridge (default: vmbr0)
Cloud-Init Configuration:
--ciuser USER Cloud-Init username (default: ubuntu)
--sshkey KEY SSH public key (or use --sshkey-file)
--sshkey-file FILE Read SSH key from file
--ipconfig CONFIG IP configuration (default: ip=dhcp)
--nameserver DNS DNS servers (space-separated)
--searchdomain DOMAIN Search domains
Template Options:
--description TEXT Template description
--tags TAGS Tags (comma-separated, default: "template,cloud-init")
--node NODE Target Proxmox node
--skip-verification Skip template verification after creation
--no-optimize Skip template optimization steps
Examples:
# Create template from Ubuntu cloud image
$0 --vmid 9000 --name "ubuntu-24.04-cloudinit" \\
--image /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img \\
--sshkey-file ~/.ssh/id_rsa.pub
# Create template with custom configuration
$0 --vmid 9000 --name "ubuntu-24.04-cloudinit" \\
--image /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img \\
--storage local-lvm --memory 2048 --cores 2 \\
--ciuser ubuntu --sshkey-file ~/.ssh/id_rsa.pub \\
--description "Ubuntu 24.04 LTS Cloud-Init Template"
EOF
}
# Validate required arguments
validate_args() {
if [ -z "$VMID" ]; then
log_error "VMID is required. Use --vmid option."
exit 1
fi
if [ -z "$TEMPLATE_NAME" ]; then
log_error "Template name is required. Use --name option."
exit 1
fi
if [ -z "$IMAGE" ]; then
log_error "Image path is required. Use --image option."
exit 1
fi
if [ ! -f "$IMAGE" ]; then
log_error "Image file not found: $IMAGE"
exit 1
fi
# Validate VMID is numeric
if ! [[ "$VMID" =~ ^[0-9]+$ ]]; then
log_error "VMID must be numeric: $VMID"
exit 1
fi
# Check if VMID already exists
if qm list | grep -q "^\s*$VMID\s"; then
log_error "VM with ID $VMID already exists"
exit 1
fi
# Load SSH key from file if specified
if [ -n "$SSHKEY_FILE" ]; then
if [ ! -f "$SSHKEY_FILE" ]; then
log_error "SSH key file not found: $SSHKEY_FILE"
exit 1
fi
SSHKEY="$(cat "$SSHKEY_FILE")"
log_info "Loaded SSH key from: $SSHKEY_FILE"
fi
# Validate SSH key format if provided
if [ -n "$SSHKEY" ]; then
if ! echo "$SSHKEY" | grep -qE "^ssh-(rsa|ed25519|ecdsa)"; then
log_warn "SSH key format may be invalid (should start with ssh-rsa, ssh-ed25519, or ecdsa)"
fi
fi
}
# Validate template after creation
verify_template() {
log_step "Verifying template configuration"
local config
config=$(qm config $VMID 2>&1)
if [ $? -ne 0 ]; then
log_error "Failed to read template configuration"
return 1
fi
local errors=0
# Check Cloud-Init is configured
if ! echo "$config" | grep -q "ide2.*cloudinit"; then
log_warn "Cloud-Init drive not found in template"
errors=$((errors + 1))
fi
# Check serial console is enabled
if ! echo "$config" | grep -q "serial0.*socket"; then
log_warn "Serial console not enabled (recommended for cloud-init)"
errors=$((errors + 1))
fi
# Check SSH key is configured
if [ -n "$SSHKEY" ]; then
if ! echo "$config" | grep -q "sshkey"; then
log_warn "SSH key not found in template configuration"
errors=$((errors + 1))
fi
fi
# Check UEFI is enabled
if ! echo "$config" | grep -q "bios.*ovmf"; then
log_warn "UEFI not enabled (recommended for modern images)"
fi
if [ $errors -eq 0 ]; then
log_info "✓ Template configuration verified"
return 0
else
log_warn "Template has $errors configuration warnings"
return 1
fi
}
# Clone template for testing
test_template_clone() {
if [ "$SKIP_VERIFICATION" = true ]; then
return
fi
log_step "Testing template by creating a temporary clone"
local test_vmid=$((VMID + 1000)) # Use a different VMID range
local test_name="${TEMPLATE_NAME}-test-$$"
# Find available VMID
while qm list | grep -q "^\s*$test_vmid\s"; do
test_vmid=$((test_vmid + 1))
done
log_info "Creating test clone: VMID $test_vmid"
# Create linked clone
if ! qm clone $VMID $test_vmid --name "$test_name" > /dev/null 2>&1; then
log_error "Failed to create test clone"
return 1
fi
log_info "✓ Test clone created successfully (VMID: $test_vmid)"
# Clean up test clone
read -p "Delete test clone $test_vmid? (Y/n): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
log_info "Deleting test clone..."
qm destroy $test_vmid --purge
log_info "✓ Test clone deleted"
else
log_info "Test clone preserved. Manual cleanup required: qm destroy $test_vmid --purge"
fi
return 0
}
# Create template using the main script
create_template() {
log_step "Creating template using create-vm-from-image.sh"
# Build command
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local create_script="${script_dir}/create-vm-from-image.sh"
if [ ! -f "$create_script" ]; then
log_error "create-vm-from-image.sh not found at: $create_script"
exit 1
fi
local cmd="$create_script"
cmd="$cmd --vmid $VMID"
cmd="$cmd --name \"$TEMPLATE_NAME\""
cmd="$cmd --image \"$IMAGE\""
cmd="$cmd --storage $STORAGE"
cmd="$cmd --memory $MEMORY"
cmd="$cmd --cores $CORES"
cmd="$cmd --bridge $BRIDGE"
cmd="$cmd --cloud-init"
cmd="$cmd --uefi"
cmd="$cmd --serial"
cmd="$cmd --template"
cmd="$cmd --cpu host"
cmd="$cmd --cache none"
cmd="$cmd --discard"
# Add node if specified
if [ -n "$NODE" ]; then
cmd="$cmd --node $NODE"
fi
# Add Cloud-Init configuration
if [ -n "$CIUSER" ]; then
cmd="$cmd --ciuser $CIUSER"
fi
if [ -n "$SSHKEY" ]; then
cmd="$cmd --sshkey \"$SSHKEY\""
fi
if [ -n "$IPCONFIG" ]; then
cmd="$cmd --ipconfig \"$IPCONFIG\""
fi
if [ -n "$NAMESERVER" ]; then
cmd="$cmd --nameserver \"$NAMESERVER\""
fi
if [ -n "$SEARCHDOMAIN" ]; then
cmd="$cmd --searchdomain \"$SEARCHDOMAIN\""
fi
if [ -n "$DESCRIPTION" ]; then
cmd="$cmd --description \"$DESCRIPTION\""
fi
if [ -n "$TAGS" ]; then
cmd="$cmd --tags \"$TAGS\""
fi
log_info "Executing: $cmd"
echo ""
# Execute the main script
eval "$cmd"
if [ $? -ne 0 ]; then
log_error "Failed to create template"
exit 1
fi
}
# Add template metadata
add_template_metadata() {
log_step "Adding template metadata"
local metadata_desc
if [ -n "$DESCRIPTION" ]; then
metadata_desc="$DESCRIPTION"
else
metadata_desc="Cloud-Init Template - Created $(date +%Y-%m-%d)"
fi
# Update description
qm set $VMID --description "$metadata_desc"
# Ensure tags include template
if [[ ! "$TAGS" =~ template ]]; then
TAGS="template,$TAGS"
fi
# Update tags
qm set $VMID --tags "$TAGS"
log_info "✓ Template metadata added"
}
# Main function
main() {
echo "========================================="
echo "Create Proxmox Cloud-Init Template"
echo "========================================="
echo ""
parse_args "$@"
validate_args
echo ""
log_info "Template Configuration:"
log_info " VMID: $VMID"
log_info " Name: $TEMPLATE_NAME"
log_info " Image: $IMAGE"
log_info " Storage: $STORAGE"
log_info " Memory: ${MEMORY}MB (template minimal)"
log_info " Cores: $CORES"
log_info " Bridge: $BRIDGE"
log_info " Cloud-Init User: $CIUSER"
[ -n "$SSHKEY" ] && log_info " SSH Key: Configured"
[ -n "$DESCRIPTION" ] && log_info " Description: $DESCRIPTION"
[ -n "$TAGS" ] && log_info " Tags: $TAGS"
echo ""
read -p "Continue with template creation? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_info "Aborted by user"
exit 0
fi
echo ""
# Create template
create_template
# Add metadata
add_template_metadata
# Verify template
if [ "$SKIP_VERIFICATION" = false ]; then
echo ""
verify_template
fi
# Test clone if verification enabled
if [ "$SKIP_VERIFICATION" = false ]; then
echo ""
read -p "Test template by creating a temporary clone? (Y/n): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
test_template_clone
fi
fi
echo ""
log_info "========================================="
log_info "Template Creation Complete!"
log_info "========================================="
echo ""
log_info "Template Details:"
qm config $VMID | head -20
echo ""
log_info "Clone template with:"
log_info " qm clone $VMID <new-vmid> --name \"<name>\""
echo ""
log_info "Full clone:"
log_info " qm clone $VMID <new-vmid> --full --name \"<name>\""
echo ""
log_info "After cloning, configure Cloud-Init:"
log_info " qm set <new-vmid> --ciuser $CIUSER"
log_info " qm set <new-vmid> --sshkey \"<ssh-key>\""
log_info " qm set <new-vmid> --ipconfig0 ip=<ip>/24,gw=<gateway>"
}
# Run main function
main "$@"

View File

@@ -0,0 +1,386 @@
#!/bin/bash
source ~/.bashrc
# Create All Service VMs via Proxmox API using ISO
# Uploads ISO and creates all VMs automatically
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
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 -e "${BLUE}[STEP]${NC} $1"
}
log_header() {
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}========================================${NC}"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
else
log_error ".env file not found!"
exit 1
fi
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_HOST="${1:-192.168.1.206}"
PROXMOX_URL="https://${PROXMOX_HOST}:8006"
PROXMOX_NODE="${2:-pve}"
ISO_FILE="${ISO_FILE:-ubuntu-24.04.3-live-server-amd64.iso}"
ISO_PATH="${ISO_PATH:-./${ISO_FILE}}"
# Get authentication ticket
get_ticket() {
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket")
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
echo "$ticket|$csrf"
}
# Check if ISO exists locally
check_iso() {
if [ ! -f "$ISO_PATH" ]; then
log_error "ISO file not found: $ISO_PATH"
log_info "Looking for ISO in project root..."
ISO_PATH="./ubuntu-24.04.3-live-server-amd64.iso"
if [ ! -f "$ISO_PATH" ]; then
log_error "ISO file not found. Please ensure ubuntu-24.04.3-live-server-amd64.iso is in the project root."
exit 1
fi
fi
log_info "Found ISO: $ISO_PATH"
ISO_SIZE=$(du -h "$ISO_PATH" | cut -f1)
log_info "ISO size: $ISO_SIZE"
}
# Check if ISO already exists in Proxmox
iso_exists() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/storage/local/content")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
isos = [f for f in data.get('data', []) if f.get('content') == 'iso' and '$ISO_FILE' in f.get('volid', '')]
print('true' if isos else 'false')
" 2>/dev/null || echo "false"
}
# Upload ISO to Proxmox
upload_iso() {
local auth=$1
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_step "Uploading ISO to Proxmox..."
log_warn "This may take several minutes depending on ISO size and network speed..."
# Upload ISO using multipart form
local result=$(curl -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-F "content=iso" \
-F "filename=@$ISO_PATH" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/storage/local/upload" 2>&1)
if echo "$result" | grep -q "error"; then
log_error "ISO upload failed: $result"
return 1
fi
log_info "✓ ISO uploaded successfully"
return 0
}
# Check if VM exists
vm_exists() {
local auth=$1
local vmid=$2
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
local response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/cluster/resources?type=vm")
echo "$response" | python3 -c "
import sys, json
data = json.load(sys.stdin)
vms = [v for v in data.get('data', []) if v.get('type') == 'qemu' and str(v.get('vmid')) == '$vmid']
print('true' if vms else 'false')
" 2>/dev/null || echo "false"
}
# Create VM via API
create_vm() {
local auth=$1
local vmid=$2
local name=$3
local cores=$4
local memory=$5
local disk_size=$6
local ip_address=$7
local gateway=$8
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_step "Creating VM: $name (ID: $vmid)..."
# First, verify ISO exists in storage
local iso_volid="local:iso/${ISO_FILE}"
log_info "Using ISO: $iso_volid"
# Create VM with proper API format
# Note: Proxmox API requires specific parameter format
log_info "API Call: POST $PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu"
log_info "Parameters: vmid=$vmid, name=$name, cores=$cores, memory=$memory"
# Strategy: Create VM with minimal config, then add hardware via separate API calls
log_info "Step 1: Creating VM skeleton..."
local create_response=$(curl -k -s -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "vmid=$vmid" \
-d "name=$name" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu" 2>&1)
if echo "$create_response" | grep -q '"errors"'; then
log_error "Failed to create VM skeleton:"
echo "$create_response" | python3 -c "import sys, json; d=json.load(sys.stdin); print(json.dumps(d.get('errors', {}), indent=2))" 2>/dev/null || echo "$create_response"
return 1
fi
log_info "✓ VM skeleton created"
sleep 1
# Step 2: Configure basic VM settings
log_info "Step 2: Configuring CPU and memory..."
curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "cores=$cores" \
-d "memory=$memory" \
-d "ostype=l26" \
-d "agent=1" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null 2>&1
# Step 3: Add disk (simplest format)
log_info "Step 3: Adding disk..."
local disk_response=$(curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "scsi0=local:${disk_size}" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$disk_response" | grep -q '"errors"'; then
log_warn "Disk configuration warning (continuing anyway)"
fi
# Step 4: Add ISO
log_info "Step 4: Adding ISO..."
local iso_response=$(curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "ide2=$iso_volid" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$iso_response" | grep -q '"errors"'; then
log_warn "ISO configuration warning (continuing anyway)"
fi
# Step 5: Add network (try simplest format)
log_info "Step 5: Adding network..."
local net_response=$(curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "net0=bridge=vmbr0" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$net_response" | grep -q '"errors"'; then
log_warn "Network configuration warning (may need manual configuration)"
fi
# Step 6: Set boot order
log_info "Step 6: Configuring boot order..."
curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "boot=order=ide2" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null 2>&1
# Verify VM config file was created
sleep 2
local verify_response=$(curl -k -s -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$verify_response" | grep -q '"errors"'; then
log_error "VM $name was not created properly. Config file missing."
log_error "Response: $verify_response"
return 1
fi
log_info "✓ VM $name created successfully"
# Configure Cloud-Init if IP is provided
if [ -n "$ip_address" ] && [ -n "$gateway" ]; then
log_info "Configuring Cloud-Init for $name..."
local config_response=$(curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "ipconfig0=ip=${ip_address}/24,gw=${gateway}" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$config_response" | grep -q '"errors"'; then
log_warn "Cloud-Init configuration may have failed (VM will use DHCP)"
echo "$config_response" | python3 -c "import sys, json; d=json.load(sys.stdin); print(d.get('errors', {}).get('errors', 'Unknown error'))" 2>/dev/null || echo "$config_response"
else
log_info "✓ Network configured for $name"
fi
fi
return 0
}
# Start VM
start_vm() {
local auth=$1
local vmid=$2
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_info "Starting VM $vmid..."
local start_response=$(curl -k -s -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/start")
if echo "$start_response" | grep -q '"error"'; then
log_warn "Failed to start VM $vmid: $start_response"
return 1
fi
log_info "✓ VM $vmid started"
return 0
}
# VM configurations
declare -A VMS=(
["100"]="cloudflare-tunnel:2:4096:40:192.168.1.60:192.168.1.254"
["101"]="k3s-master:4:8192:80:192.168.1.188:192.168.1.254"
["102"]="git-server:4:8192:100:192.168.1.121:192.168.1.254"
["103"]="observability:4:8192:200:192.168.1.82:192.168.1.254"
)
main() {
log_header "Create All Service VMs via Proxmox API"
echo ""
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
# Check ISO file
check_iso
echo ""
# Authenticate
log_step "Authenticating with Proxmox..."
auth=$(get_ticket)
if [ $? -ne 0 ]; then
exit 1
fi
log_info "✓ Authentication successful"
echo ""
# Check if ISO already uploaded
if [ "$(iso_exists "$auth")" = "true" ]; then
log_info "✓ ISO already exists in Proxmox storage"
else
# Upload ISO
if ! upload_iso "$auth"; then
log_error "Failed to upload ISO"
exit 1
fi
fi
echo ""
# Create VMs
log_step "Creating VMs..."
echo ""
for vmid in "${!VMS[@]}"; do
IFS=':' read -r name cores memory disk_size ip_address gateway <<< "${VMS[$vmid]}"
# Check if VM already exists
if [ "$(vm_exists "$auth" "$vmid")" = "true" ]; then
log_warn "VM $name (ID: $vmid) already exists. Skipping..."
continue
fi
# Create VM
if create_vm "$auth" "$vmid" "$name" "$cores" "$memory" "$disk_size" "$ip_address" "$gateway"; then
# Start VM
start_vm "$auth" "$vmid"
echo ""
else
log_error "Failed to create VM $name"
fi
done
log_header "VM Creation Complete"
echo ""
log_info "All VMs have been created and started!"
echo ""
log_info "Next steps:"
echo " 1. Access each VM console via Proxmox Web UI: $PROXMOX_URL"
echo " 2. Complete Ubuntu installation on each VM"
echo " 3. After OS installation, run setup scripts:"
echo " - scripts/setup-cloudflare-tunnel.sh (on 192.168.1.60)"
echo " - scripts/setup-k3s.sh (on 192.168.1.188)"
echo " - scripts/setup-git-server.sh (on 192.168.1.121)"
echo " - scripts/setup-observability.sh (on 192.168.1.82)"
echo ""
}
main "$@"

View File

@@ -0,0 +1,257 @@
#!/bin/bash
source ~/.bashrc
# Create VMs from Cloud-Init Template with Automated Setup Scripts
# This script creates VMs from a Cloud-Init template and applies service-specific install scripts
set -e
# 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_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
fi
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="https://192.168.1.206:8006"
PROXMOX_NODE="pve"
STORAGE="${STORAGE:-local-lvm}"
TEMPLATE_NAME="${TEMPLATE_NAME:-ubuntu-24.04-cloudinit}"
# VM Configuration
declare -A VMS=(
[100]="cloudflare-tunnel:2:4096:40G:192.168.1.60:192.168.1.1:setup-cloudflare-tunnel.sh"
[101]="k3s-master:4:8192:80G:192.168.1.188:192.168.1.1:setup-k3s.sh"
[102]="git-server:2:4096:100G:192.168.1.121:192.168.1.1:setup-git-server.sh"
[103]="observability:4:8192:200G:192.168.1.82:192.168.1.1:setup-observability.sh"
)
# Get authentication ticket
get_ticket() {
local response=$(curl -k -s -d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket")
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
if [ -z "$ticket" ] || [ -z "$csrf" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
echo "$ticket|$csrf"
}
# Read install script and create Cloud-Init user-data
create_cloud_init_user_data() {
local script_path=$1
local vm_name=$2
local ip_address=$3
local gateway=$4
if [ ! -f "$script_path" ]; then
log_error "Install script not found: $script_path"
return 1
fi
local script_content=$(cat "$script_path" | base64 -w 0)
cat <<EOF
#cloud-config
hostname: ${vm_name}
manage_etc_hosts: true
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ${SSH_PUBLIC_KEY:-}
# Network configuration
network:
version: 2
ethernets:
eth0:
addresses:
- ${ip_address}/24
gateway4: ${gateway}
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
# Write install script
write_files:
- path: /tmp/install-service.sh
content: !!binary |
${script_content}
owner: root:root
permissions: '0755'
encoding: b64
# Run install script on first boot
runcmd:
- /tmp/install-service.sh
- rm -f /tmp/install-service.sh
# Update packages
package_update: true
package_upgrade: true
# Install common packages
packages:
- curl
- wget
- git
- vim
- net-tools
EOF
}
# Create VM from template
create_vm_from_template() {
local auth=$1
local vmid=$2
local name=$3
local cores=$4
local memory=$5
local disk_size=$6
local ip_address=$7
local gateway=$8
local install_script=$9
local ticket=$(echo "$auth" | cut -d'|' -f1)
local csrf=$(echo "$auth" | cut -d'|' -f2)
log_step "Creating VM $vmid: $name"
# Create Cloud-Init user-data
local user_data_file="/tmp/cloud-init-${vmid}.yaml"
create_cloud_init_user_data "scripts/${install_script}" "$name" "$ip_address" "$gateway" > "$user_data_file"
log_info "Cloud-Init user-data created: $user_data_file"
# Upload user-data to Proxmox (requires manual step or SCP)
log_warn "Note: Cloud-Init user-data needs to be uploaded to Proxmox storage"
log_info "You can:"
log_info " 1. Upload $user_data_file to Proxmox storage manually"
log_info " 2. Or use cicustom parameter in API call"
# Clone template to create VM
log_info "Cloning template $TEMPLATE_NAME to VM $vmid..."
local clone_response=$(curl -k -s -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "newid=$vmid" \
-d "name=$name" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_NAME/clone")
if echo "$clone_response" | grep -q '"errors"'; then
log_error "Failed to clone template: $clone_response"
return 1
fi
log_info "Template cloned successfully"
# Wait for clone to complete
sleep 5
# Configure VM
log_info "Configuring VM..."
# Set CPU, memory, disk
curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "cores=$cores" \
-d "memory=$memory" \
-d "net0=virtio,bridge=vmbr0" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
# Configure Cloud-Init
curl -k -s -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf" \
-d "ipconfig0=ip=${ip_address}/24,gw=${gateway}" \
-d "ciuser=ubuntu" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
log_info "✓ VM $vmid configured"
# Cleanup
rm -f "$user_data_file"
}
main() {
echo "========================================="
echo "Create VMs from Cloud-Init Template"
echo "========================================="
echo ""
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
if [ -z "$TEMPLATE_NAME" ]; then
log_error "TEMPLATE_NAME not set. Create template first."
log_info "See: scripts/create-proxmox-template.sh"
exit 1
fi
# Authenticate
auth=$(get_ticket)
if [ $? -ne 0 ]; then
exit 1
fi
log_step "Creating VMs from template: $TEMPLATE_NAME"
echo ""
# Create each VM
for vmid in 100 101 102 103; do
IFS=':' read -r name cores memory disk_size ip_address gateway install_script <<< "${VMS[$vmid]}"
if create_vm_from_template "$auth" "$vmid" "$name" "$cores" "$memory" "$disk_size" "$ip_address" "$gateway" "$install_script"; then
log_info "✓ VM $vmid ($name) created successfully"
else
log_error "✗ Failed to create VM $vmid"
fi
echo ""
done
log_info "========================================="
log_info "VM Creation Complete"
log_info "========================================="
echo ""
log_warn "Next steps:"
echo " 1. Start each VM"
echo " 2. VM will boot and run install script automatically"
echo " 3. Check VM console for installation progress"
echo " 4. SSH to VM after installation completes"
}
main "$@"

View File

@@ -0,0 +1,135 @@
#!/bin/bash
source ~/.bashrc
# Create VMs via SSH using qm command (more reliable than API)
# Requires SSH access to Proxmox host
set -e
# 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_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
fi
PROXMOX_HOST="${1:-192.168.1.206}"
PROXMOX_USER="${2:-root}"
ISO_FILE="ubuntu-24.04.3-live-server-amd64.iso"
# VM configurations
declare -A VMS=(
["100"]="cloudflare-tunnel:2:4096:40:192.168.1.60:192.168.1.254"
["101"]="k3s-master:4:8192:80:192.168.1.188:192.168.1.254"
["102"]="git-server:4:8192:100:192.168.1.121:192.168.1.254"
["103"]="observability:4:8192:200:192.168.1.82:192.168.1.254"
)
create_vm_ssh() {
local vmid=$1
local name=$2
local cores=$3
local memory=$4
local disk_size=$5
local ip_address=$6
local gateway=$7
log_step "Creating VM: $name (ID: $vmid) via SSH..."
ssh "$PROXMOX_USER@$PROXMOX_HOST" <<EOF
# Create VM
qm create $vmid --name $name --cores $cores --memory $memory --ostype l26
# Add network
qm set $vmid --net0 virtio,bridge=vmbr0
# Add disk
qm set $vmid --scsi0 local:${disk_size},format=raw
# Add ISO
qm set $vmid --ide2 local:iso/${ISO_FILE},media=cdrom
# Configure boot order
qm set $vmid --boot c --bootdisk scsi0
# Enable QEMU agent
qm set $vmid --agent 1
# Configure Cloud-Init (if IP provided)
if [ -n "$ip_address" ] && [ -n "$gateway" ]; then
qm set $vmid --ipconfig0 ip=${ip_address}/24,gw=${gateway}
fi
# Start VM
qm start $vmid
EOF
if [ $? -eq 0 ]; then
log_info "✓ VM $name created and started"
return 0
else
log_error "Failed to create VM $name"
return 1
fi
}
main() {
echo "========================================="
echo "Create VMs via SSH (qm command)"
echo "========================================="
echo ""
log_info "This script requires SSH access to Proxmox host"
log_info "Host: $PROXMOX_HOST"
log_info "User: $PROXMOX_USER"
echo ""
read -p "Do you have SSH access configured? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_error "SSH access required. Please configure SSH keys first."
exit 1
fi
# Test SSH connection
if ! ssh -o ConnectTimeout=5 "$PROXMOX_USER@$PROXMOX_HOST" "echo 'SSH connection successful'" 2>/dev/null; then
log_error "Cannot connect to Proxmox host via SSH"
log_info "Please configure SSH access or use Proxmox Web UI instead"
exit 1
fi
log_info "✓ SSH connection successful"
echo ""
# Create VMs
for vmid in "${!VMS[@]}"; do
IFS=':' read -r name cores memory disk_size ip_address gateway <<< "${VMS[$vmid]}"
create_vm_ssh "$vmid" "$name" "$cores" "$memory" "$disk_size" "$ip_address" "$gateway"
echo ""
done
log_info "========================================="
log_info "VM Creation Complete!"
log_info "========================================="
}
main "$@"