Skip to content

Instantly share code, notes, and snippets.

@masterjanic
Created January 11, 2024 21:52
Show Gist options
  • Save masterjanic/1822fb8dadb7b69f5b71baace6debd6c to your computer and use it in GitHub Desktop.
Save masterjanic/1822fb8dadb7b69f5b71baace6debd6c to your computer and use it in GitHub Desktop.
A bash script to create many different VM templates across multiple distros for Proxmox VE.
#!/bin/bash
# Sources using Openstack images
declare -A sources
declare -A template_ids
declare -A result_ids
template_ids["debian"]=9000
template_ids["ubuntu"]=9100
template_ids["almalinux"]=9200
template_ids["fedora"]=9300
template_ids["centos"]=9400
# Debian Versions
sources["debian-12"]="http://cdimage.debian.org/cdimage/cloud/bookworm/latest/debian-12-generic-amd64.qcow2"
sources["debian-11"]="http://cdimage.debian.org/cdimage/cloud/bullseye/latest/debian-11-generic-amd64.qcow2"
sources["debian-10"]="http://cdimage.debian.org/cdimage/openstack/current-10/debian-10-openstack-amd64.qcow2"
# Ubuntu Versions
# Here we use a kvm image, because they are smaller than the openstack images
sources["ubuntu-23.10"]="https://cloud-images.ubuntu.com/mantic/current/mantic-server-cloudimg-amd64-disk-kvm.img"
sources["ubuntu-23.04"]="https://cloud-images.ubuntu.com/lunar/current/lunar-server-cloudimg-amd64-disk-kvm.img"
sources["ubuntu-22.10"]="https://cloud-images.ubuntu.com/kinetic/current/kinetic-server-cloudimg-amd64-disk-kvm.img"
sources["ubuntu-22.04"]="https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64-disk-kvm.img"
sources["ubuntu-20.04"]="https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64-disk-kvm.img"
sources["ubuntu-18.04"]="https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img"
sources["ubuntu-16.04"]="https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img"
# AlmaLinux Versions
sources["almalinux-9"]="https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2"
sources["almalinux-8"]="https://repo.almalinux.org/almalinux/8/cloud/x86_64/images/AlmaLinux-8-GenericCloud-latest.x86_64.qcow2"
# Fedora Versions
sources["fedora-38"]="https://download.fedoraproject.org/pub/fedora/linux/releases/38/Cloud/x86_64/images/Fedora-Cloud-Base-38-1.6.x86_64.qcow2"
# CentOS Versions
sources["centos-stream-9"]="https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2"
download_template() {
echo "[*] Downloading template $1..."
wget -q --show-progress -O "$2" "$1"
echo "[*] Download complete."
}
vm_exists() {
qm list | grep -q "$1"
}
echo " _ _ "
echo " | | | | "
echo " | |_ ______ ___ _ __ ___ __ _| |_ ___ "
echo " | __|______/ __| '__/ _ \\/ _\` | __/ _ \\"
echo " | |_ | (__| | | __/ (_| | || __/"
echo " \__| \___|_| \___|\__,_|\__\___|"
echo ""
echo " > Create Proxmox Templates <"
echo " by @masterjanic"
echo ""
# Check if script is run with root permissions
if [ "$(id -u)" != "0" ]; then
echo "[x] Please run this script with root privileges."
exit 1
fi
# Check if Proxmox is installed
if ! [ -f /etc/pve/pve-root-ca.pem ]; then
echo "[x] Proxmox is not installed, please run this script in a Proxmox Environment."
exit 1
fi
echo "[*] Installing dependencies..."
apt -qq install -y libguestfs-tools wget dialog &> /dev/null
# Let user enter a storage name
read -p "[?] Please enter a the name for the storage to save the template machines to (default=local-lvm): " -r storage
storage=${storage:-"local-lvm"}
# Let user choose whether to delete templates after creation
read -p "[?] Do you want to delete the downloaded images after creation? (y/n, default=y): " -r delete_templates
delete_templates=${delete_templates:-y}
read -p "[?] Do you want to delete existing templates? (y/n, default=n): " -r delete_previous
delete_previous=${delete_previous:-n}
read -p "[?] Use CPU type host instead of kvm64 by default? (y/n, default=y): " -r use_cpu_host
use_cpu_host=${use_cpu_host:-y}
clear
# Let user select distros to download
cmd=(dialog --separate-output --title "Distro Selection" --checklist "Select distros to create templates for:" 22 76 16)
options=(
$(for key in "${!sources[@]}"; do
echo "$key"
echo "$key"
echo "on"
done)
)
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
clear
# Check if storage exists in Proxmox
if ! (pvesm list "$storage" | grep -q "$storage"); then
echo "[x] Storage does not exist in Proxmox or does not have any hosts, please choose another name."
exit 1
fi
# Delete existing templates
if [ "$delete_previous" = "y" ]; then
echo "[*] Deleting existing templates..."
vm_list=$(qm list | grep template | awk '{print $1}')
for vm_id in "${template_ids[@]}"; do
for i in $(seq $vm_id $(($vm_id + ${#sources[@]}))); do
if echo "$vm_list" | grep -q "$i"; then
qm destroy "$i"
fi
done
done
echo "[*] Deletion complete."
fi
start=$(date +%s)
sorted=$(for i in "${!choices[@]}"; do echo "${choices[i]}"; done | sort)
for source in $sorted; do
if vm_exists "template-$source"; then
echo "[*] Template for $source already exists, skipping..."
continue
fi
distro=$(echo "$source" | cut -d'-' -f1)
vm_id=${template_ids[$distro]}
while vm_exists "$vm_id"; do
echo "[*] VM with ID $vm_id already exists, trying next id..."
vm_id=$(($vm_id + 1))
done
echo "[*] Creating template for $source..."
extension="${sources[$source]##*.}"
image_path="/tmp/$source.$extension"
download_template "${sources[$source]}" "$image_path"
echo "[*] Customizing image, please wait..."
virt-customize -a "$image_path" \
--install qemu-guest-agent \
--run-command 'sed -i "s/ssh_pwauth:.*0/ssh_pwauth: 1/" /etc/cloud/cloud.cfg' \
--run-command 'sed -i "s/ssh_pwauth:.*[Ff]alse/ssh_pwauth: true/" /etc/cloud/cloud.cfg' \
--run-command 'sed -i "s/disable_root:.*[Tt]rue/disable_root: false/" /etc/cloud/cloud.cfg' \
--run-command 'sed -i "s/disable_root:.*1/disable_root: 0/" /etc/cloud/cloud.cfg' \
--run-command 'sed -i "s/lock_passwd:.*[Tt]rue/lock_passwd: false/" /etc/cloud/cloud.cfg' \
--run-command 'sed -i "s/lock_passwd:.*1/lock_passwd: 0/" /etc/cloud/cloud.cfg' \
--run-command 'sed -i "s/PasswordAuthentication no/PasswordAuthentication yes/" /etc/ssh/sshd_config' \
--run-command 'sed -i "s/PermitRootLogin [Nn]o/PermitRootLogin yes/" /etc/ssh/sshd_config' \
--run-command 'sed -i "s/#PermitRootLogin [Yy]es/PermitRootLogin yes/" /etc/ssh/sshd_config' \
--run-command 'sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/" /etc/ssh/sshd_config' \
--run-command 'sed -i "s/KbdInteractiveAuthentication [Nn]o/#KbdInteractiveAuthentication no/" /etc/ssh/sshd_config' \
--run-command 'sed -i "s/[#M]axAuthTries 6/MaxAuthTries 20/" /etc/ssh/sshd_config' \
--no-logfile
echo "[*] Creating new VM with ID $vm_id..."
is_x86_64_v2=$(echo "${sources[$source]}" | grep -c "x86_64")
cpu_type="kvm64"
if [ "$use_cpu_host" = "y" ]; then
cpu_type="host"
fi
if [ "$is_x86_64_v2" -eq 1 ]; then
cpu_type="x86-64-v2"
fi
qm create $vm_id --name "template-$source" --memory 512 --cores 4 --net0 virtio,bridge=vmbr0,firewall=1 --agent enabled=1,fstrim_cloned_disks=1 --cpu $cpu_type
echo "[*] Importing template image..."
qm importdisk $vm_id "/tmp/$source.$extension" "$storage" -format qcow2
echo "[*] Attaching disk to template..."
qm set $vm_id --scsihw virtio-scsi-pci --scsi0 "$storage:vm-$vm_id-disk-0"
echo "[*] Applying important settings..."
qm set $vm_id --ide2 "$storage:cloudinit" --boot c --bootdisk scsi0 --serial0 socket --vga serial0
echo "[*] Resizing disk..."
qm resize $vm_id scsi0 +2G
echo "[*] Creating template from VM..."
qm template $vm_id
if [ "$delete_templates" = "y" ]; then
echo "[*] Deleting downloaded image file:"
rm -v $image_path
fi
result_ids[$source]="$vm_id"
template_ids[$distro]=$(($vm_id+1))
echo "[*] Template creation for $source complete."
done
end=$(date +%s)
echo "[*] Script execution finished after $((($end-$start)/60)) min $((($end-$start)%60)) sec."
created_count=${#result_ids[@]}
if [ "$created_count" -eq 0 ]; then
echo "[!] No new templates were created."
exit 0
else
echo "[*] The following templates were created:"
for source in "${!result_ids[@]}"; do
echo " - $source: ${result_ids[$source]}"
done
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment