├── .github └── workflows │ └── shellcheck.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── proxmox-cloudinit-script ├── README.md └── pve_cloudinit.sh └── proxmox-truenas-script ├── Bash └── pve_truenas.sh └── README.md /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | name: ShellCheck 2 | on: 3 | push: 4 | branches: [ "main" ] 5 | pull_request: 6 | branches: [ "main" ] 7 | workflow_dispatch: 8 | inputs: 9 | git-ref: 10 | description: Git Ref (Optional) 11 | required: false 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | shellcheck: 18 | name: ShellCheck 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Run ShellCheck 23 | uses: ludeeus/action-shellcheck@2.0.0 24 | with: 25 | format: gcc 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome. If you notice a bug or another issue, you can either open an Issue or Pull Request. If you have a fix, you can open a Pull Request without first opening an Issue. All contributions will be licensed under this repository's MIT License. 4 | 5 | If you run into any security issues, please report them in the [Security tab](https://github.com/roib20/proxmox-scripts/security). Please note there is no bug bounty. 6 | 7 | If you have a question or not sure about something, please open a [Discussion](https://github.com/roib20/proxmox-scripts/security) instead of an Issue. 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 roib20 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CalVer](https://img.shields.io/badge/CalVer-YY.0M.MICRO-22bfda.svg)](https://calver.org) 2 | [![License](https://img.shields.io/badge/license-MIT-blue)](https://opensource.org/licenses/MIT) 3 | [![ShellCheck](https://github.com/roib20/proxmox-scripts/actions/workflows/shellcheck.yml/badge.svg)](https://github.com/roib20/proxmox-scripts/actions/workflows/shellcheck.yml) 4 | 5 | # Proxmox Scripts by [roib20](https://github.com/roib20) 6 | 7 | Proxmox VE shell scripts for automating different types of Virtual Machine or Template installs. Current scripts include: 8 | 9 | - [proxmox-cloudinit-script](https://github.com/roib20/proxmox-scripts/tree/main/proxmox-cloudinit-script) 10 | - [proxmox-truenas-script](https://github.com/roib20/proxmox-scripts/tree/main/proxmox-truenas-script) 11 | 12 | ## FAQ 13 | ### Is there any more information available about these scripts? 14 | Of course! Choose one of the scripts above to read a full detailed README and FAQ for each script. 15 | 16 | ### I don't want to run random Bash scripts from GitHub. 17 | This is understandable. All these scripts are open-source; you can read their source code and judge for yourself if you want to run these scripts. In fact I would encourage understanding what any shell script does before blindly running it! If you have any questions about any of the scripts and what they do, feel free to open a post in [Discussions](https://github.com/roib20/proxmox-scripts/discussions). 18 | 19 | If you run into any security issues, please report them in the [Security tab](https://github.com/roib20/proxmox-scripts/security). 20 | 21 | If you are not happy with everything a certain script does, feel free to fork or clone this repo and edit it to fit your needs, or open a Pull Request if you think it will help others. 22 | 23 | ### Are contributions welcome? 24 | Yes. Please read through [CONTRIBUTING.md](https://github.com/roib20/proxmox-scripts/blob/main/CONTRIBUTING.md). 25 | -------------------------------------------------------------------------------- /proxmox-cloudinit-script/README.md: -------------------------------------------------------------------------------- 1 | # Proxmox VE script for generating Cloud-init templates 2 | 3 | A user-friendly guided script for Proxmox VE 8.x. Guides you through downloading a Linux cloud image and automatically configuring it as a Cloud-init template - that can then be used to easily generate VMs! 4 | 5 | Various distros are avaible to download and configure. Current choices include: 6 | - [Ubuntu Cloud 22.04 LS (Jammy Jellyfish)](https://cloud-images.ubuntu.com/releases/22.04/) 7 | - [Ubuntu Minimal Cloud 22.04 LTS (Jammy Jellyfish)](https://cloud-images.ubuntu.com/minimal/releases/jammy/) 8 | - [Ubuntu Cloud 24.04 LTS (Noble Numbat)](https://cloud-images.ubuntu.com/noble/) 9 | - [Ubuntu Minimal Cloud 24.04 LTS (Noble Numbat)](https://cloud-images.ubuntu.com/releases/24.04/) 10 | - [Debian 11 "bullseye" (GenericCloud)](https://cloud.debian.org/images/cloud/bullseye/) 11 | - [Debian 12 "bookworm" (GenericCloud)](https://cloud.debian.org/images/cloud/bookworm/) 12 | - [Fedora Cloud 40 (base)](https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/) 13 | - [AlmaLinux 9 (GenericCloud)](https://repo.almalinux.org/almalinux/9/cloud/) 14 | 15 | ## FAQ 16 | ### What is Cloud-init? 17 | [Cloud-init](https://cloudinit.readthedocs.io/) makes it easy to deploy virtual machines; Instead of going through a lengthy installation ISO, you simply set up the wanted values before installation, then the VM boots up and automatically configures itself. 18 | 19 | ### How can Cloud-init be used together with Proxmox VE? 20 | 21 | [Proxmox VE includes Cloud-init support](https://pve.proxmox.com/wiki/Cloud-Init_Support). You can download any supported cloud image and configure it to use Cloud-init. This script simply automates this process. For more information, refer to the resources below. 22 | 23 | ### How to run this script? 24 | SSH into your PVE server. 25 | Run the following command: `sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/roib20/proxmox-scripts/main/proxmox-cloudinit-script/pve_cloudinit.sh)"` 26 | 27 | ### What to do after running the script? 28 | When the script finishes, you should get a message stating the name and ID of your newly created PVE template. Go to the Proxmox VE Web UI (default port 8006) to view your template. You can then clone the template to create a new VM. 29 | 30 | ### How to configure a Cloud-init VM? 31 | After cloning a template into a new VM, view your new VM in the Proxmox VE web UI, choose the "Cloud-init" tab and configure any options you want (e.g. username, password, SSH public key and network settings). Make sure to also modify any VM options that you want to change under the "Hardware" tab (e.g. Memory and Hard Disk size). Then boot up your system and it will automatically configure itself with Cloud-init and your chosen options! If you configured an SSH public key, you will be able to SSH into your new VM. 32 | 33 | ### How to further automate this? 34 | After creating templates using this script (or other methods), you can then use Infrastructure as Code (IaC) tools to create VMs based on the created templates. For example, you can use Terraform together with the [Telmate/proxmox provider](https://registry.terraform.io/providers/Telmate/proxmox/latest), or Ansible together with the [community.general.proxmox module](https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_module.html) or [community.general.proxmox_kvm module](https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_kvm_module.html). 35 | 36 | 37 | ## Resources 38 | It is important to understand what any shell script does before blindly running it. The following resources help explain the usage of Cloud-init together with Proxmox: 39 | - Proxmox VE: 40 | + Cloud-Init Support: [Wiki](https://pve.proxmox.com/wiki/Cloud-Init_Support) 41 | + Cloud-Init FAQ: [Wiki](https://pve.proxmox.com/wiki/Cloud-Init_FAQ) 42 | - Techno Tim: 43 | + Perfect Proxmox Template with Cloud Image and Cloud Init: [Video](https://youtu.be/shiIi38cJe4) | [Docs](https://docs.technotim.live/posts/cloud-init-cloud-image/) 44 | - Learn Linux TV: 45 | + Proxmox VE - How to build an Ubuntu 22.04 Template (Updated Method): [Video](https://youtu.be/MJgIm03Jxdo) | [Blog post](https://www.learnlinux.tv/proxmox-ve-how-to-build-an-ubuntu-22-04-template-updated-method/) 46 | - Austin's Nerdy Things: 47 | + How to create a Proxmox Ubuntu cloud-init image: [Blog post](https://austinsnerdythings.com/2021/08/30/how-to-create-a-proxmox-ubuntu-cloud-init-image/) 48 | - Joshua Powers: 49 | + Launching Ubuntu Cloud Images with QEMU: [Blog post](https://powersj.io/posts/ubuntu-qemu-cli/) 50 | 51 | ## Alternative approaches: 52 | 53 | If you want an approach that's more "Infrastructure as Code (IaC)", you could instead use Ansible or Packer to generate these PVE Cloud-init templates: 54 | 55 | - Tim's Blog: 56 | + Proxmox Cloud-init image using Ansible: [Blog post](https://www.timatlee.com/post/proxmox-cloudinit-image-ansible/) 57 | - Christian Lempa: 58 | + Create VMs on Proxmox in Seconds! (Proxmox + Packer): [Video](https://youtu.be/1nf3WOEFq1Y) | [Packer Templates on GitHub](https://github.com/christianlempa/boilerplates/tree/main/packer/proxmox) 59 | -------------------------------------------------------------------------------- /proxmox-cloudinit-script/pve_cloudinit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | choose_distro() { 4 | echo -e "Welcome to the Proxmox Cloud-Init template installer!\n" 5 | PS3="Please choose a distro image to download (1-12): " 6 | local distro_list=("Ubuntu Cloud 22.04 LTS" "Ubuntu Cloud 22.04 LTS (Minimal)" "Ubuntu Cloud 24.04 LTS" "Ubuntu Cloud 24.04 LTS (Minimal)" "Debian 11 (GenericCloud)" "Debian 12 (GenericCloud)" 7 | "Fedora Cloud 40 (base)" "Fedora Cloud 41 (base)" "AlmaLinux 9 (GenericCloud)" "RockyLinux 9 (GenericCloud)" "FreeBSD 14.1 (Basic)" "CentOS 9 Stream (GenericCloud)" "Quit") 8 | select distro in "${distro_list[@]}"; do 9 | case $distro in 10 | "${distro_list[0]}") 11 | echo -e "${distro_list[0]}" 12 | IMAGE_URL="https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-amd64.img" 13 | CHECKSUM_URL="https://cloud-images.ubuntu.com/releases/jammy/release/SHA256SUMS" 14 | SHA=256 15 | CLOUDIMG_NAME="ubuntu-22.04-server-cloudimg-amd64.img" 16 | TEMPLATE_NAME="ubuntu-22.04-template" 17 | break 18 | ;; 19 | "${distro_list[1]}") 20 | echo -e "${distro_list[1]}" 21 | IMAGE_URL="https://cloud-images.ubuntu.com/minimal/releases/jammy/release/ubuntu-22.04-minimal-cloudimg-amd64.img" 22 | CHECKSUM_URL="https://cloud-images.ubuntu.com/minimal/releases/jammy/release/SHA256SUMS" 23 | SHA=256 24 | CLOUDIMG_NAME="ubuntu-22.04-minimal-cloudimg-amd64.img" 25 | TEMPLATE_NAME="ubuntu-22.04-minimal-template" 26 | break 27 | ;; 28 | "${distro_list[2]}") 29 | echo -e "${distro_list[2]}" 30 | IMAGE_URL="https://cloud-images.ubuntu.com/releases/noble/release/ubuntu-24.04-server-cloudimg-amd64.img" 31 | CHECKSUM_URL="https://cloud-images.ubuntu.com/releases/noble/release/SHA256SUMS" 32 | SHA=256 33 | CLOUDIMG_NAME="ubuntu-24.04-server-cloudimg-amd64.img" 34 | TEMPLATE_NAME="ubuntu-24.04-template" 35 | break 36 | ;; 37 | "${distro_list[3]}") 38 | echo -e "${distro_list[3]}" 39 | IMAGE_URL="https://cloud-images.ubuntu.com/minimal/releases/noble/release/ubuntu-24.04-minimal-cloudimg-amd64.img" 40 | CHECKSUM_URL="https://cloud-images.ubuntu.com/minimal/releases/noble/release/SHA256SUMS" 41 | SHA=256 42 | CLOUDIMG_NAME="ubuntu-24.04-minimal-cloudimg-amd64.img" 43 | TEMPLATE_NAME="ubuntu-24.04-minimal-template" 44 | break 45 | ;; 46 | "${distro_list[4]}") 47 | echo -e "${distro_list[4]}" 48 | IMAGE_URL="https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-genericcloud-amd64.qcow2" 49 | CHECKSUM_URL="https://cloud.debian.org/images/cloud/bullseye/latest/SHA512SUMS" 50 | SHA=512 51 | CLOUDIMG_NAME="debian-11-genericcloud-amd64.qcow2" 52 | TEMPLATE_NAME="debian-11-template" 53 | break 54 | ;; 55 | "${distro_list[5]}") 56 | echo -e "${distro_list[5]}" 57 | IMAGE_URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2" 58 | CHECKSUM_URL="https://cloud.debian.org/images/cloud/bookworm/latest/SHA512SUMS" 59 | SHA=512 60 | CLOUDIMG_NAME="debian-12-genericcloud-amd64.qcow2" 61 | TEMPLATE_NAME="debian-12-template" 62 | break 63 | ;; 64 | "${distro_list[6]}") 65 | echo -e "${distro_list[6]}" 66 | IMAGE_URL="https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2" 67 | CHECKSUM_URL="https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-40-1.14-x86_64-CHECKSUM" 68 | SHA=256 69 | CLOUDIMG_NAME="Fedora-Cloud-40-1.14-x86_64.qcow2" 70 | TEMPLATE_NAME="fedora-40-template" 71 | break 72 | ;; 73 | "${distro_list[7]}") 74 | echo -e "${distro_list[7]}" 75 | IMAGE_URL="https://mirror.nl.mirhosting.net/fedora/linux/releases/41/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2" 76 | CHECKSUM_URL="https://mirror.i3d.net/pub/fedora/linux/releases/41/Cloud/x86_64/images/Fedora-Cloud-41-1.4-x86_64-CHECKSUM" 77 | SHA=256 78 | CLOUDIMG_NAME="Fedora-Cloud-Base-Generic-41-1.4.x86_64.qcow2" 79 | TEMPLATE_NAME="fedora-41-template" 80 | break 81 | ;; 82 | "${distro_list[8]}") 83 | echo -e "${distro_list[8]}" 84 | IMAGE_URL="https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/AlmaLinux-9-GenericCloud-latest.x86_64.qcow2" 85 | CHECKSUM_URL="https://repo.almalinux.org/almalinux/9/cloud/x86_64/images/CHECKSUM" 86 | SHA=256 87 | CLOUDIMG_NAME="AlmaLinux-9-GenericCloud-latest.x86_64.qcow2" 88 | TEMPLATE_NAME="almalinux-9-template" 89 | break 90 | ;; 91 | "${distro_list[9]}") 92 | echo -e "${distro_list[9]}" 93 | IMAGE_URL="https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2" 94 | CHECKSUM_URL="https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud.latest.x86_64.qcow2.CHECKSUM" 95 | SHA=256 96 | CLOUDIMG_NAME="Rocky-9-GenericCloud.latest.x86_64.qcow2" 97 | TEMPLATE_NAME="rockylinux-9-template" 98 | break 99 | ;; 100 | "${distro_list[10]}") 101 | echo -e "${distro_list[10]}" 102 | IMAGE_URL="https://download.freebsd.org/releases/VM-IMAGES/14.1-RELEASE/amd64/Latest/FreeBSD-14.1-RELEASE-amd64-BASIC-CLOUDINIT-zfs.qcow2.xz" 103 | CHECKSUM_URL="https://download.freebsd.org/releases/VM-IMAGES/14.1-RELEASE/amd64/Latest/CHECKSUM.SHA256" 104 | SHA=256 105 | CLOUDIMG_NAME="FreeBSD-14.1-RELEASE-amd64-BASIC-CLOUDINIT-zfs.qcow2.xz" 106 | TEMPLATE_NAME="freebsd-14.1-template" 107 | break 108 | ;; 109 | "${distro_list[11]}") 110 | echo -e "${distro_list[11]}" 111 | IMAGE_URL="https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2" 112 | CHECKSUM_URL="https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2.SHA256SUM" 113 | SHA=256 114 | CLOUDIMG_NAME="CentOS-Stream-GenericCloud-9-latest.x86_64.qcow2" 115 | TEMPLATE_NAME="Centos-9-stream-template" 116 | break 117 | ;; 118 | "Quit") 119 | exit 0 120 | ;; 121 | *) echo -e "Invalid choice option $REPLY" ;; 122 | esac 123 | done 124 | } 125 | 126 | choose_id() { 127 | local existing_id=true 128 | while [ ${existing_id} = true ]; do 129 | while :; do 130 | echo -e "\n" 131 | read -r -ep 'Enter VM ID for the template (from 100): ' ID 132 | [[ $ID =~ ^[[:digit:]]+$ ]] || continue 133 | ((((ID = (10#$ID)) <= 999999999) && ID >= 100)) || continue 134 | break 135 | done 136 | if ( (qm status "${ID}" 2>/dev/null)); then 137 | echo -e "ID is in use" 138 | existing_id=true 139 | else 140 | existing_id=false 141 | fi 142 | done 143 | } 144 | 145 | choose_storage() { 146 | echo -e "\n" && pvesm status 147 | local existing_storage=false 148 | while [ ${existing_storage} = false ]; do 149 | read -r -ep 'Choose Promxox VE storage (by Name): ' STORAGE 150 | if ( (pvesm list "${STORAGE}" 2>/dev/null)); then 151 | existing_storage=true 152 | else 153 | echo -e "Invalid storage" 154 | existing_storage=false 155 | fi 156 | done 157 | 158 | echo -e "\n" 159 | local valid_choice=false 160 | while [ ${valid_choice} = false ]; do 161 | read -r -ep "Is the chosen storage using an SSD? (y/n) " selection 162 | case "$selection" in 163 | y | Y | yes | Yes) 164 | SSD=true 165 | valid_choice=true 166 | ;; 167 | n | N | no | No) 168 | SSD=false 169 | valid_choice=true 170 | ;; 171 | *) 172 | echo -e "Invalid choice" 173 | valid_choice=false 174 | ;; 175 | esac 176 | done 177 | } 178 | 179 | choose_agent() { 180 | echo -e "\n" 181 | local valid_choice=false 182 | while [ ${valid_choice} = false ]; do 183 | read -r -ep "Should the qemu-guest-agent package be pre-installed in the image? (y/n) " selection 184 | case "$selection" in 185 | y | Y | yes | Yes) 186 | agent=true 187 | valid_choice=true 188 | ;; 189 | n | N | no | No) 190 | agent=false 191 | valid_choice=true 192 | ;; 193 | *) 194 | echo -e "Invalid choice" 195 | valid_choice=false 196 | ;; 197 | esac 198 | done 199 | } 200 | 201 | choose_libguestfs() { 202 | if "${agent}"; then 203 | if command -v virt-customize --version &>/dev/null; then 204 | libguestfs=true 205 | else 206 | echo -e "\n" 207 | echo -e "In order to pre-install qemu-guest-agent in the image, libguestfs-tools is required." 208 | local valid_choice=false 209 | while [ ${valid_choice} = false ]; do 210 | read -r -ep "Install libguestfs-tools? (y/n) " selection 211 | case "$selection" in 212 | y | Y | yes | Yes) 213 | libguestfs=true 214 | valid_choice=true 215 | ;; 216 | n | N | no | No) 217 | libguestfs=false 218 | valid_choice=true 219 | ;; 220 | *) 221 | echo -e "Invalid choice" 222 | valid_choice=false 223 | ;; 224 | esac 225 | done 226 | fi 227 | fi 228 | } 229 | 230 | download_image() { 231 | if [ -f "$CLOUDIMG_NAME" ]; then 232 | echo -e "\nDistro image already downloaded." 233 | 234 | local valid_choice=false 235 | while [ ${valid_choice} = false ]; do 236 | read -r -ep "Download image again? (y/n) " selection 237 | case "$selection" in 238 | y | Y | yes | Yes) 239 | wget "${IMAGE_URL}" -O "${CLOUDIMG_NAME}" 240 | wget -q -O - "${CHECKSUM_URL}" | grep "${CLOUDIMG_NAME}" | sha"${SHA}"sum -c --ignore-missing 241 | valid_choice=true 242 | ;; 243 | n | N | no | No) 244 | true 245 | valid_choice=true 246 | ;; 247 | *) 248 | echo -e "Invalid choice" 249 | valid_choice=false 250 | ;; 251 | esac 252 | done 253 | else 254 | wget "${IMAGE_URL}" -O "${CLOUDIMG_NAME}" 255 | if (wget -q -O - "${CHECKSUM_URL}" | grep "${CLOUDIMG_NAME}" | sha"${SHA}"sum -c --ignore-missing); then 256 | true 257 | else 258 | exit 1 259 | fi 260 | fi 261 | } 262 | 263 | qm_create() { 264 | # create a new VM with VirtIO SCSI single controller 265 | qm create "${ID}" --name "${TEMPLATE_NAME}" --memory 2048 --balloon 1024 --core 1 --cpu x86-64-v2-AES --numa 1 \ 266 | --net0 virtio,bridge=vmbr0,firewall=1 --scsihw virtio-scsi-single \ 267 | --agent "${agent_params}" 268 | 269 | # import the downloaded disk to the storage, attaching it as a SCSI drive 270 | qm importdisk "${ID}" "${CLOUDIMG_NAME}" "${STORAGE}" 271 | qm set "${ID}" --scsihw virtio-scsi-single \ 272 | --scsi0 file="${STORAGE}":"${ID}"/vm-"${ID}"-disk-0.raw,iothread=1,"${ssd_params}" 273 | 274 | # configure a CD-ROM drive, which will be used to pass the Cloud-Init data to the VM 275 | qm set "${ID}" --ide2 "${STORAGE}":cloudinit 276 | 277 | # set the boot parameter to order=scsi0 to restrict BIOS to boot from this disk only 278 | qm set "${ID}" --boot order=scsi0 279 | 280 | # configure a serial console and use it as a display 281 | qm set "${ID}" --serial0 socket --vga serial0 282 | 283 | # Set the default IP to DHCP in Cloud-Init (can be changed later through Cloud-Init) 284 | qm set "${ID}" --ipconfig0 ip=dhcp 285 | 286 | # convert the VM into a template 287 | qm template "${ID}" 288 | } 289 | 290 | cleanup() { 291 | echo -e "\n" 292 | 293 | local valid_choice=false 294 | while [ ${valid_choice} = false ]; do 295 | read -r -ep "Perform post-install cleanup? (y/n) " selection 296 | case "$selection" in 297 | y | Y | yes | Yes) 298 | cleanup=true 299 | valid_choice=true 300 | ;; 301 | n | N | no | No) 302 | cleanup=false 303 | valid_choice=true 304 | ;; 305 | *) 306 | echo -e "Invalid choice" 307 | valid_choice=false 308 | ;; 309 | esac 310 | done 311 | 312 | if "${cleanup}"; then 313 | echo -e "\nPost-install cleanup:" 314 | 315 | local valid_choice=false 316 | while [ ${valid_choice} = false ]; do 317 | read -r -ep "Remove libguestfs-tools? (y/n) " selection 318 | case "$selection" in 319 | y | Y | yes | Yes) 320 | apt-get autopurge -y libguestfs-tools 321 | valid_choice=true 322 | ;; 323 | n | N | no | No) 324 | true 325 | valid_choice=true 326 | ;; 327 | *) 328 | echo -e "Invalid choice" 329 | valid_choice=false 330 | ;; 331 | esac 332 | done 333 | fi 334 | 335 | echo -e "\n" 336 | local valid_choice=false 337 | while [ ${valid_choice} = false ]; do 338 | read -r -ep "Delete downloaded cloud image file? (y/n) " selection 339 | case "$selection" in 340 | y | Y | yes | Yes) 341 | rm "${CLOUDIMG_NAME}" 342 | valid_choice=true 343 | ;; 344 | n | N | no | No) 345 | true 346 | valid_choice=true 347 | ;; 348 | *) 349 | echo -e "Invalid choice" 350 | valid_choice=false 351 | ;; 352 | esac 353 | done 354 | 355 | echo -e "\n" 356 | local valid_choice=false 357 | while [ ${valid_choice} = false ]; do 358 | read -r -ep "Delete VM template that was JUST created by this script? (y/n) " selection 359 | case "$selection" in 360 | y | Y | yes | Yes) 361 | qm destroy "${ID}" 362 | valid_choice=true 363 | ;; 364 | n | N | no | No) 365 | true 366 | valid_choice=true 367 | ;; 368 | *) 369 | echo -e "Invalid choice" 370 | valid_choice=false 371 | ;; 372 | esac 373 | done 374 | } 375 | 376 | main() { 377 | choose_distro "$@" 378 | choose_id "$@" 379 | choose_storage "$@" 380 | choose_agent "$@" 381 | choose_libguestfs "$@" 382 | 383 | # # add date to filename 384 | # date=$(date --iso-8601=date) 385 | # CLOUDIMG_NAME="${CLOUDIMG_NAME}_${date}" 386 | 387 | # download the image 388 | download_image "$@" 389 | 390 | # configure SSD parameters 391 | if "${SSD}"; then 392 | ssd_params="discard=on,ssd=1" 393 | else 394 | ssd_params="ssd=0" 395 | fi 396 | 397 | # install qemu-guest-agent (requires libguestfs-tools) 398 | # and configure agent parameters 399 | if "${agent}"; then 400 | if "${libguestfs}"; then 401 | apt-get install -y libguestfs-tools 402 | virt-customize -a "${CLOUDIMG_NAME}" --install qemu-guest-agent 403 | 404 | agent_params="enabled=1,fstrim_cloned_disks=1" 405 | else 406 | agent_params="enabled=0" 407 | fi 408 | else 409 | agent_params="enabled=0" 410 | fi 411 | 412 | qm_create "$@" 413 | 414 | echo -e "\n" 415 | if ( (qm status "${ID}" 2>/dev/null)); then 416 | echo -e "Install complete! 417 | $(basename "${CLOUDIMG_NAME}" ".qcow2") installed as PVE Template #${ID}" 418 | else 419 | echo -e "Install failed. Please try again." 420 | fi 421 | 422 | cleanup "$@" 423 | } 424 | 425 | main "$@" 426 | 427 | exit 0 428 | -------------------------------------------------------------------------------- /proxmox-truenas-script/Bash/pve_truenas.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | choose_distro() { 4 | echo -e "Welcome to the Proxmox Cloud-Init template installer!\n" 5 | PS3="Please choose a distro image to download (1-6): " 6 | local TRUENAS_SCALE_MIRROR="https://download.sys.truenas.net" # Alternative mirror: "https://download.truenas.com" 7 | local TRUENAS_CORE_MIRROR="https://download-core.sys.truenas.net" # Alternative mirror: "https://download.freenas.org" 8 | local TRUENAS_SCALE_CODENAME="Dragonfish" # 24.04 9 | local TRUENAS_SCALE_VERSION="24.04.1.1" 10 | local TRUENAS_CORE_VERSION="13.0" 11 | local TRUENAS_CORE_UPDATE="U6.1" 12 | 13 | local distro_list=("TrueNAS SCALE ${TRUENAS_SCALE_VERSION}" "TrueNAS CORE ${TRUENAS_CORE_VERSION}-${TRUENAS_CORE_UPDATE}" "Quit") 14 | select distro in "${distro_list[@]}"; do 15 | case $distro in 16 | "${distro_list[0]}") 17 | echo -e "${distro_list[0]}" 18 | TEMPLATE_NAME="TrueNAS-SCALE" 19 | OSTYPE="l26" 20 | IMAGE_URL="${TRUENAS_SCALE_MIRROR}/TrueNAS-SCALE-${TRUENAS_SCALE_CODENAME}/${TRUENAS_SCALE_VERSION}/TrueNAS-SCALE-${TRUENAS_SCALE_VERSION}.iso" 21 | CLOUDIMG_NAME="TrueNAS-SCALE-${TRUENAS_SCALE_VERSION}.iso" 22 | CHECKSUM_URL="${TRUENAS_SCALE_MIRROR}/TrueNAS-SCALE-${TRUENAS_SCALE_CODENAME}/${TRUENAS_SCALE_VERSION}/TrueNAS-SCALE-${TRUENAS_SCALE_VERSION}.iso.sha256" 23 | SHA=256 24 | break 25 | ;; 26 | "${distro_list[1]}") 27 | echo -e "${distro_list[1]}" 28 | TEMPLATE_NAME="TrueNAS-CORE" 29 | OSTYPE="other" 30 | IMAGE_URL="${TRUENAS_CORE_MIRROR}/${TRUENAS_CORE_VERSION}/STABLE/${TRUENAS_CORE_UPDATE}/x64/TrueNAS-${TRUENAS_CORE_VERSION}-${TRUENAS_CORE_UPDATE}.iso" 31 | CLOUDIMG_NAME="TrueNAS-${TRUENAS_CORE_VERSION}-${TRUENAS_CORE_UPDATE}.iso" 32 | CHECKSUM_URL="${TRUENAS_CORE_MIRROR}/${TRUENAS_CORE_VERSION}/STABLE/${TRUENAS_CORE_UPDATE}/x64/TrueNAS-${TRUENAS_CORE_VERSION}-${TRUENAS_CORE_UPDATE}.iso.sha256" 33 | CHECKSUM="$(curl -s "$CHECKSUM_URL" | grep -oP "(?<= = ).*$") ${CLOUDIMG_NAME}" 34 | SHA=256 35 | break 36 | ;; 37 | "Quit") 38 | exit 0 39 | ;; 40 | *) echo -e "Invalid choice option $REPLY" ;; 41 | esac 42 | done 43 | } 44 | 45 | choose_id() { 46 | local existing_id=true 47 | while [ "$existing_id" = true ]; do 48 | while :; do 49 | echo -e "\n" 50 | read -r -ep 'Enter VM ID for the template (from 100): ' ID 51 | [[ $ID =~ ^[[:digit:]]+$ ]] || continue 52 | ((((ID = (10#$ID)) <= 999999999) && ID >= 100)) || continue 53 | break 54 | done 55 | if ( (qm status "${ID}" 2>/dev/null)); then 56 | echo -e "ID is in use" 57 | existing_id=true 58 | else 59 | existing_id=false 60 | fi 61 | done 62 | } 63 | 64 | choose_storage() { 65 | echo -e "\n" && pvesm status 66 | local existing_storage=false 67 | while [ "$existing_storage" = false ]; do 68 | read -r -ep 'Choose Promxox VE storage (by Name): ' STORAGE 69 | if ( (pvesm list "${STORAGE}" 2>/dev/null)); then 70 | existing_storage=true 71 | else 72 | echo -e "Invalid storage" 73 | existing_storage=false 74 | fi 75 | done 76 | } 77 | 78 | download_image() { 79 | pushd /var/lib/vz/template/iso || exit 1 80 | 81 | if [ -f "$CLOUDIMG_NAME" ]; then 82 | echo -e "\nDistro image already downloaded." 83 | 84 | local valid_choice=false 85 | local download=false 86 | while [ "$valid_choice" = false ]; do 87 | read -r -ep "Download image again? (y/n) " selection 88 | case "$selection" in 89 | y | Y | yes | Yes) 90 | download=true 91 | valid_choice=true 92 | ;; 93 | n | N | no | No) 94 | download=false 95 | valid_choice=true 96 | ;; 97 | *) 98 | echo -e "Invalid choice" 99 | valid_choice=false 100 | ;; 101 | esac 102 | done 103 | else 104 | download=true 105 | fi 106 | 107 | if [ "$download" = true ]; then 108 | wget "${IMAGE_URL}" -O "${CLOUDIMG_NAME}" 109 | if [ "$OSTYPE" = "l26" ]; then 110 | if (wget -q -O - "${CHECKSUM_URL}" | grep "${CLOUDIMG_NAME}" | 111 | sha"${SHA}"sum --strict --check --ignore-missing); then 112 | true 113 | else 114 | exit 1 115 | fi 116 | elif [ "$OSTYPE" = "other" ]; then 117 | if (echo "${CHECKSUM}" | grep "${CLOUDIMG_NAME}" | 118 | sha"${SHA}"sum --strict --check --ignore-missing); then 119 | true 120 | else 121 | exit 1 122 | fi 123 | true 124 | fi 125 | fi 126 | 127 | popd || exit 1 128 | } 129 | 130 | qm_create() { 131 | # create a new VM with VirtIO SCSI single controller 132 | qm create "${ID}" --name "${TEMPLATE_NAME}" --memory 8096 --core 1 \ 133 | --net0 virtio,bridge=vmbr0,firewall=1 --scsihw virtio-scsi-single \ 134 | --ostype "${OSTYPE}" \ 135 | --agent "${agent_params}" \ 136 | --cpu host --machine q35 --bios ovmf 137 | 138 | qm set "${ID}" --efidisk0 "${STORAGE}":0,format=qcow2,efitype=4m,pre-enrolled-keys=0,size=528K 139 | 140 | qm set "${ID}" --scsihw virtio-scsi-single \ 141 | --scsi0 file="${STORAGE}":32,format=qcow2,iothread=1 142 | 143 | # qm set "${VMID}" --hostpci0 --hostpci0 host"${device-id}",pcie=1,rombar=1 144 | 145 | # configure a CD-ROM drive, with the installation ISO 146 | qm set "${ID}" --ide2 media=cdrom,file=none 147 | qm set "${ID}" --ide2 media=cdrom,file=local:iso/"${CLOUDIMG_NAME}" 148 | 149 | # set the boot parameter to order="ide2;scsi0" 150 | qm set "${ID}" --boot order="ide2;scsi0" 151 | } 152 | 153 | convert_to_template() { 154 | echo -e "\n" 155 | 156 | local valid_choice=false 157 | while [ "$valid_choice" = false ]; do 158 | read -r -ep "Convert VM to template? (y/n) " selection 159 | case "$selection" in 160 | y | Y | yes | Yes) 161 | # convert the VM into a template 162 | qm template "${ID}" 163 | valid_choice=true 164 | ;; 165 | n | N | no | No) 166 | true 167 | valid_choice=true 168 | ;; 169 | *) 170 | echo -e "Invalid choice" 171 | valid_choice=false 172 | ;; 173 | esac 174 | done 175 | } 176 | 177 | cleanup() { 178 | echo -e "\n" 179 | 180 | local valid_choice=false 181 | while [ "$valid_choice" = false ]; do 182 | read -r -ep "Perform post-install cleanup? (y/n) " selection 183 | case "$selection" in 184 | y | Y | yes | Yes) 185 | cleanup=true 186 | valid_choice=true 187 | ;; 188 | n | N | no | No) 189 | cleanup=false 190 | valid_choice=true 191 | ;; 192 | *) 193 | echo -e "Invalid choice" 194 | valid_choice=false 195 | ;; 196 | esac 197 | done 198 | 199 | if "${cleanup}"; then 200 | echo -e "\nPost-install cleanup:" 201 | 202 | local valid_choice=false 203 | while [ "$valid_choice" = false ]; do 204 | read -r -ep "Delete downloaded cloud image file? (y/n) " selection 205 | case "$selection" in 206 | y | Y | yes | Yes) 207 | rm "/var/lib/vz/template/iso/${CLOUDIMG_NAME}" 208 | valid_choice=true 209 | ;; 210 | n | N | no | No) 211 | true 212 | valid_choice=true 213 | ;; 214 | *) 215 | echo -e "Invalid choice" 216 | valid_choice=false 217 | ;; 218 | esac 219 | done 220 | 221 | echo -e "\n" 222 | local valid_choice=false 223 | while [ "$valid_choice" = false ]; do 224 | read -r -ep "Delete VM template that was JUST created by this script? (y/n) " selection 225 | case "$selection" in 226 | y | Y | yes | Yes) 227 | qm destroy "${ID}" 228 | valid_choice=true 229 | ;; 230 | n | N | no | No) 231 | true 232 | valid_choice=true 233 | ;; 234 | *) 235 | echo -e "Invalid choice" 236 | valid_choice=false 237 | ;; 238 | esac 239 | done 240 | fi 241 | } 242 | 243 | main() { 244 | choose_distro "$@" 245 | choose_id "$@" 246 | choose_storage "$@" 247 | # TEMPLATE_NAME="Template-TrueNAS" 248 | 249 | # download the ISO 250 | download_image "$@" 251 | 252 | # configure qemu-guest-agent parameters 253 | if [ "$OSTYPE" = "l26" ]; then 254 | agent_params="enabled=1,fstrim_cloned_disks=1" 255 | elif [ "$OSTYPE" = "other" ]; then 256 | agent_params="enabled=0" 257 | fi 258 | 259 | qm_create "$@" 260 | convert_to_template "$@" 261 | 262 | echo -e "\n" 263 | if ( (qm status "${ID}" 2>/dev/null)); then 264 | echo -e "Install complete! 265 | ${TEMPLATE_NAME} installed as #${ID}" 266 | else 267 | echo -e "Install failed. Please try again." 268 | fi 269 | 270 | cleanup "$@" 271 | } 272 | 273 | main "$@" 274 | 275 | exit 0 276 | -------------------------------------------------------------------------------- /proxmox-truenas-script/README.md: -------------------------------------------------------------------------------- 1 | # Proxmox VE script for creating TrueNAS virtual machines 2 | 3 | A user-friendly guided script for Proxmox VE 8.x. Guides you through downloading a TrueNAS ISO and configuring it as a VM. 4 | 5 | Multiple versions of TrueNAS are available to download: 6 | - [TrueNAS SCALE](https://www.truenas.com/download-truenas-scale/) 24.04.1.1 (Dragonfish) 7 | - [TrueNAS CORE](https://www.truenas.com/download-truenas-core/) 13.0-U6.1 8 | 9 | ## FAQ 10 | 11 | ### What does the script do? 12 | This script automates the process of creating a TrueNAS virtual machine inside Proxmox VE. It downloads the ISO for the chosen version of TrueNAS, verifies it's SHA256 checksum, then configures a VM with it. 13 | 14 | ### How to run this script? 15 | SSH into your PVE server. 16 | Run the following command: `sudo bash -c "$(wget -qLO - https://raw.githubusercontent.com/roib20/proxmox-scripts/main/proxmox-truenas-script/Bash/pve_truenas.sh)"` 17 | 18 | ### What to do after running the script? 19 | When the script finishes, you should get a message stating the name and ID of your newly created PVE VM or template. Go to the Proxmox VE Web UI (default port 8006) to view it, change any options you want then boot it up to go through the TrueNAS installer. If asked for a boot mode during installation, choose "Boot via UEFI". After installation is complete, make sure to detach the ISO image from the VM, or change the boot order to use scsi0 instead of ide2. 20 | 21 | ### What are the default VM options that this script configures? 22 | Default Settings: `8GB RAM - 32GB Storage - 1vCPU - Q35 Machine - OVMF (UEFI)` 23 | 24 | If TrueNAS SCALE is chosen then QEMU Agent is automatically enabled. [For TrueNAS CORE, QEMU Guest Agent needs to be installed manually](https://www.truenas.com/community/resources/qemu-guest-agent.167/). 25 | 26 | ### What are the recommended system requirements for the host? 27 | Based on the TrueNAS system requirements and my observation, I recommend at minimum: 28 | **Processor:** 2-Core Intel 64-Bit or AMD x86_64 processor 29 | **Memory:** 16GB Memory (ECC recommended) 30 | **Boot device:** 64GB SSD boot device 31 | **Storage:** Two or more identically-sized drives for a single storage pool (the bigger the better) 32 | **HBA card:** LSI 9207-8i (or similar), flashed in IT mode 33 | 34 | Since this is virtualized, 8GB of memory and 32GB of storage will be dedicated to the configured VM (this is the script defaults). The rest of the memory and storage will be used for the host. 35 | 36 | ### Is HBA passthrough configured for the VM? 37 | This script does not fully configure the VM with an HBA card, this has to be done manually after the script is run. If you have an HBA card installed, follow the instructions in the Proxmox wiki to configure PCI passthrough (see resources below). 38 | 39 | However, this script does configure the VM with optimized settings for pass through: 40 | >When passing through, the best compatibility is reached when using q35 as machine type, OVMF (EFI for VMs) instead of SeaBIOS and PCIe instead of PCI. 41 | 42 | ## Resources 43 | It is important to understand what any shell script does before blindly running it. The following resources show the manual installation process for TrueNAS inside Proxmox VE. Note that some of the chosen settings are different than what is included in this script. The process of passing through an LSI card (which is not done by this script) is explained by the Proxmox wiki and demonstrated by IBRACORP. 44 | 45 | - IBRACORP: 46 | + Proxmox Series | [playlist](https://www.youtube.com/watch?v=wPd6lpM01FY&list=PLOgmFrM3hTGeDNcvYVrnqx7qI_wCxT4w0) 47 | - Christian Lempa: 48 | + How to run TrueNAS [CORE] on Proxmox? [Video](https://youtu.be/M3pKprTdNqQ) 49 | - Proxmox: 50 | + FreeBSD Guest Notes: [Wiki](https://pve.proxmox.com/wiki/FreeBSD_Guest_Notes) 51 | + PCI passthrough: [Wiki](https://pve.proxmox.com/wiki/Pci_passthrough) 52 | + PCI(e) Passthrough: [Wiki](https://pve.proxmox.com/wiki/PCI(e)_Passthrough) 53 | - TrueNAS SCALE: 54 | + Getting Started with SCALE: [Docs](https://www.truenas.com/docs/scale/gettingstarted/) 55 | + Getting Started with SCALE/Installation Instructions: [Docs](https://www.truenas.com/docs/scale/gettingstarted/install/) 56 | - TrueNAS CORE: 57 | + Getting Started: [Docs](https://www.truenas.com/docs/core/gettingstarted/) 58 | + Getting Started/CORE Hardware Guide: [Docs](https://www.truenas.com/docs/core/gettingstarted/corehardwareguide/) 59 | + Getting Started/Install: [Docs](https://www.truenas.com/docs/core/gettingstarted/install/) 60 | - Serverbuilds.net: 61 | + Recommended SAS2 HBA (internal & external): [Forum](https://forums.serverbuilds.net/t/official-recommended-sas2-hba-internal-external/4581) 62 | + Updating your LSI SAS Controller with a UEFI Motherboard: [Forum](https://forums.serverbuilds.net/t/guide-updating-your-lsi-sas-controller-with-a-uefi-motherboard/131) 63 | --------------------------------------------------------------------------------