├── .gitignore ├── .vscode └── settings.json ├── provisioners ├── reboot.sh ├── apt_proxy.sh ├── localisation-pt.sh ├── upgrade.sh ├── network.sh └── provision.sh ├── answer.toml ├── Vagrantfile.template ├── Vagrantfile-uefi.template ├── example ├── summary.sh ├── configure-hyperv.ps1 ├── configure-vsphere.sh ├── provision-pveproxy-certificate.sh ├── provision-containers.sh ├── Vagrantfile └── provision.sh ├── box-metadata.sh ├── LICENSE ├── Makefile ├── proxmox-ve-vsphere.pkr.hcl ├── README.md └── proxmox-ve.pkr.hcl /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ 2 | packer_cache/ 3 | output-*/ 4 | shared/ 5 | tmp/ 6 | *.box 7 | *.box.json 8 | *.log 9 | secrets* 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "cacher", 4 | "DATACENTER", 5 | "datacenters", 6 | "ESXI", 7 | "govc", 8 | "hyperv", 9 | "pkrvars" 10 | ] 11 | } -------------------------------------------------------------------------------- /provisioners/reboot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | # configure apt for non-interactive mode. 5 | export DEBIAN_FRONTEND=noninteractive 6 | 7 | # reboot. 8 | nohup bash -c "ps -eo pid,comm | awk '/sshd/{print \$1}' | xargs kill; sync; reboot" 9 | -------------------------------------------------------------------------------- /provisioners/apt_proxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | [ -n "${apt_cache_host:-}" ] || exit 0 5 | 6 | cat >/etc/apt/apt.conf.d/00apt_proxy < 'unix', :target_name => 'org.qemu.guest_agent.0', :target_type => 'virtio' 9 | lv.channel :type => 'spicevmc', :target_name => 'com.redhat.spice.0', :target_type => 'virtio' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Vagrantfile-uefi.template: -------------------------------------------------------------------------------- 1 | Vagrant.configure(2) do |config| 2 | config.vm.provider 'libvirt' do |lv| 3 | lv.machine_type = 'q35' 4 | lv.loader = '/usr/share/ovmf/OVMF.fd' 5 | lv.graphics_type = 'spice' 6 | lv.video_type = 'qxl' 7 | lv.disk_bus = 'scsi' 8 | lv.disk_device = 'sda' 9 | lv.channel :type => 'unix', :target_name => 'org.qemu.guest_agent.0', :target_type => 'virtio' 10 | lv.channel :type => 'spicevmc', :target_name => 'com.redhat.spice.0', :target_type => 'virtio' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /provisioners/localisation-pt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | # configure apt for non-interactive mode. 5 | export DEBIAN_FRONTEND=noninteractive 6 | 7 | # add support for the pt_PT locale. 8 | sed -i -E 's,.+(pt_PT.UTF-8 .+),\1,' /etc/locale.gen 9 | locale-gen 10 | locale -a 11 | 12 | # set the keyboard layout. 13 | apt-get install -y console-data 14 | cat >/etc/default/keyboard <<'EOF' 15 | # KEYBOARD CONFIGURATION FILE 16 | # Consult the keyboard(5) manual page. 17 | XKBMODEL="pc105" 18 | XKBLAYOUT="pt" 19 | XKBVARIANT="" 20 | XKBOPTIONS="" 21 | KEYMAP="pt-latin1" 22 | BACKSPACE="guess" 23 | EOF 24 | dpkg-reconfigure keyboard-configuration 25 | 26 | # set the timezone. 27 | ln -fs /usr/share/zoneinfo/Europe/Lisbon /etc/localtime 28 | dpkg-reconfigure tzdata 29 | -------------------------------------------------------------------------------- /example/summary.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | ip=$1 5 | fqdn=$(hostname --fqdn) 6 | 7 | # configure apt for non-interactive mode. 8 | export DEBIAN_FRONTEND=noninteractive 9 | 10 | # show running containers. 11 | pct list 12 | 13 | # show running VMs. 14 | qm list 15 | 16 | # show versions. 17 | uname -a 18 | lvm version 19 | kvm --version 20 | lxc-ls --version 21 | cat /etc/os-release 22 | pveversion -v 23 | 24 | # show block devices. 25 | lsblk -x KNAME -o KNAME,SIZE,TRAN,SUBSYSTEMS,FSTYPE,UUID,LABEL,MODEL,SERIAL 26 | 27 | # show disk partitions. 28 | sfdisk -l 29 | 30 | # show the free space. 31 | df -h / 32 | 33 | # show the proxmox web address. 34 | cat <"$path.json" </etc/apt/sources.list.d/pve.sources </etc/apt/sources.list.d/ceph.sources </etc/network/interfaces <<'EOF' 27 | auto lo 28 | iface lo inet loopback 29 | 30 | auto eth0 31 | iface eth0 inet dhcp 32 | 33 | auto vmbr0 34 | iface vmbr0 inet manual 35 | EOF 36 | -------------------------------------------------------------------------------- /example/provision-pveproxy-certificate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | ip=$1 5 | domain=$(hostname --fqdn) 6 | dn=$(hostname) 7 | 8 | mkdir -p /vagrant/shared 9 | pushd /vagrant/shared 10 | 11 | # create a self-signed certificate. 12 | if [ ! -f $domain-crt.pem ]; then 13 | openssl genrsa \ 14 | -out $domain-key.pem \ 15 | 2048 \ 16 | 2>/dev/null 17 | chmod 400 $domain-key.pem 18 | openssl req -new \ 19 | -sha256 \ 20 | -subj "/CN=$domain" \ 21 | -key $domain-key.pem \ 22 | -out $domain-csr.pem 23 | openssl x509 -req -sha256 \ 24 | -signkey $domain-key.pem \ 25 | -extensions a \ 26 | -extfile <(echo "[a] 27 | subjectAltName=DNS:$domain,IP:$ip 28 | extendedKeyUsage=critical,serverAuth 29 | ") \ 30 | -days 365 \ 31 | -in $domain-csr.pem \ 32 | -out $domain-crt.pem 33 | openssl x509 \ 34 | -in $domain-crt.pem \ 35 | -outform der \ 36 | -out $domain-crt.der 37 | # dump the certificate contents (for logging purposes). 38 | #openssl x509 -noout -text -in $domain-crt.pem 39 | fi 40 | 41 | # install the certificate. 42 | # see https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer) 43 | cp $domain-key.pem "/etc/pve/nodes/$dn/pveproxy-ssl.key" 44 | cp $domain-crt.pem "/etc/pve/nodes/$dn/pveproxy-ssl.pem" 45 | systemctl restart pveproxy 46 | # dump the TLS connection details and certificate validation result. 47 | (printf 'GET /api2/json HTTP/1.0\r\n\r\n'; sleep .1) | openssl s_client -CAfile $domain-crt.pem -connect $domain:8006 -servername $domain 48 | -------------------------------------------------------------------------------- /example/provision-containers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | ip=$1 5 | fqdn=$(hostname --fqdn) 6 | 7 | # update the available container templates. 8 | # NB this downloads the https://www.turnkeylinux.org catalog. 9 | pveam update 10 | pveam available # show templates. 11 | 12 | # create and start two alpine-linux containers. 13 | pve_template="$(pveam available -section system | perl -ne '/ (alpine-.+)/ && print "$1\n"' | tail -1)" 14 | pveam download local $pve_template 15 | for pve_id in 100 101; do 16 | pve_ip=$(echo $ip | sed -E "s,\.[0-9]+\$,.$pve_id,") 17 | pve_disk_size=512M 18 | pvesm alloc local-lvm $pve_id vm-$pve_id-disk-1 $pve_disk_size 19 | pvesm status # show status. 20 | mkfs.ext4 $(pvesm path local-lvm:vm-$pve_id-disk-1) 21 | pct create $pve_id \ 22 | local:vztmpl/$pve_template \ 23 | --unprivileged 0 \ 24 | --onboot 1 \ 25 | --ostype alpine \ 26 | --hostname alpine-$pve_id \ 27 | --cores 1 \ 28 | --memory 128 \ 29 | --swap 0 \ 30 | --rootfs local-lvm:vm-$pve_id-disk-1,size=$pve_disk_size \ 31 | --net0 name=eth0,bridge=vmbr0,gw=$ip,ip=$pve_ip/24 32 | pct config $pve_id # show config. 33 | pct start $pve_id 34 | pct exec $pve_id sh </etc/nginx/nginx.conf <<'EOC' 41 | user www; 42 | worker_processes 1; 43 | error_log /var/log/nginx/error.log warn; 44 | pid /var/run/nginx.pid; 45 | events { 46 | worker_connections 1024; 47 | } 48 | http { 49 | include /etc/nginx/mime.types; 50 | default_type application/octet-stream; 51 | sendfile on; 52 | access_log /var/log/nginx/access.log; 53 | keepalive_timeout 3000; 54 | server { 55 | listen 80; 56 | root /www; 57 | index index.html; 58 | server_name localhost; 59 | client_max_body_size 4m; 60 | error_page 500 502 503 504 /50x.html; 61 | location = /50x.html { 62 | root /var/lib/nginx/html; 63 | } 64 | } 65 | } 66 | EOC 67 | cat >/www/index.html <<'EOC' 68 | 69 | 70 | 71 | server $pve_id 72 | 73 | 74 | this is server $pve_id 75 | 76 | 77 | EOC 78 | rc-service nginx start 79 | rc-update add nginx default 80 | EOF 81 | wget -qO- $pve_ip 82 | pct exec $pve_id -- cat /etc/alpine-release 83 | pct exec $pve_id -- passwd -d root # remove the root password. 84 | pct exec $pve_id -- sh -c "echo 'root:vagrant' | chpasswd" # or change it to vagrant. 85 | pct exec $pve_id -- ip addr 86 | pct exec $pve_id -- route -n 87 | pct exec $pve_id -- ping $ip -c 2 88 | pct status $pve_id 89 | done 90 | -------------------------------------------------------------------------------- /example/Vagrantfile: -------------------------------------------------------------------------------- 1 | ENV['VAGRANT_EXPERIMENTAL'] = 'typed_triggers' 2 | 3 | require 'open3' 4 | 5 | VM_CPU = 4 6 | VM_MEMORY_GB = 4 7 | VM_ROOT_DISK_SIZE_GB = 128 8 | 9 | Vagrant.configure('2') do |config| 10 | config.vm.box = 'proxmox-ve-amd64' 11 | #config.vm.box = 'proxmox-ve-uefi-amd64' 12 | config.vm.provider :libvirt do |lv, config| 13 | lv.memory = VM_MEMORY_GB*1024 14 | lv.cpus = VM_CPU 15 | lv.cpu_mode = 'host-passthrough' 16 | lv.nested = true 17 | lv.keymap = 'pt' 18 | lv.machine_virtual_size = VM_ROOT_DISK_SIZE_GB 19 | config.vm.synced_folder '.', '/vagrant', type: 'nfs', nfs_version: '4.2', nfs_udp: false 20 | end 21 | config.vm.provider :hyperv do |hv, config| 22 | hv.vmname = "#{File.basename(File.dirname(File.dirname(__FILE__)))}-example" 23 | hv.linked_clone = true 24 | hv.enable_virtualization_extensions = true # nested virtualization. 25 | hv.memory = VM_MEMORY_GB*1024 26 | hv.cpus = VM_CPU 27 | hv.vlan_id = ENV['HYPERV_VLAN_ID'] 28 | # set the management network adapter. 29 | # see https://github.com/hashicorp/vagrant/issues/7915 30 | # see https://github.com/hashicorp/vagrant/blob/10faa599e7c10541f8b7acf2f8a23727d4d44b6e/plugins/providers/hyperv/action/configure.rb#L21-L35 31 | config.vm.network :private_network, bridge: ENV['HYPERV_SWITCH_NAME'] if ENV['HYPERV_SWITCH_NAME'] 32 | config.vm.synced_folder '.', '/vagrant', 33 | type: 'smb', 34 | smb_username: ENV['VAGRANT_SMB_USERNAME'] || ENV['USER'], 35 | smb_password: ENV['VAGRANT_SMB_PASSWORD'] 36 | # further configure the VM (e.g. add the secondary network adapter for 37 | # the :private_network that is defined bellow). 38 | config.trigger.before :'VagrantPlugins::HyperV::Action::StartInstance', type: :action do |trigger| 39 | trigger.ruby do |env, machine| 40 | system( 41 | 'PowerShell', 42 | '-NoLogo', 43 | '-NoProfile', 44 | '-ExecutionPolicy', 45 | 'Bypass', 46 | '-File', 47 | 'configure-hyperv.ps1', 48 | machine.id 49 | ) 50 | end 51 | end 52 | end 53 | config.vm.provider :vsphere do |vsphere, config| 54 | vsphere.name = ENV['VSPHERE_VM_NAME'] 55 | vsphere.notes = "Created from #{__FILE__}" 56 | vsphere.cpu_count = VM_CPU 57 | vsphere.memory_mb = VM_MEMORY_GB*1024 58 | vsphere.user = ENV['GOVC_USERNAME'] 59 | vsphere.password = ENV['GOVC_PASSWORD'] 60 | vsphere.insecure = true 61 | vsphere.host = ENV['GOVC_HOST'] 62 | vsphere.data_center_name = ENV['GOVC_DATACENTER'] 63 | vsphere.compute_resource_name = ENV['GOVC_CLUSTER'] 64 | vsphere.data_store_name = ENV['GOVC_DATASTORE'] 65 | vsphere.template_name = ENV['VSPHERE_TEMPLATE_NAME'] 66 | vsphere.vm_base_path = ENV['VSPHERE_VM_FOLDER'] 67 | vsphere.vlan = ENV['VSPHERE_VLAN'] 68 | if ENV['VAGRANT_SMB_PASSWORD'] 69 | config.vm.synced_folder '.', '/vagrant', 70 | type: 'smb', 71 | smb_username: ENV['VAGRANT_SMB_USERNAME'] || ENV['USER'], 72 | smb_password: ENV['VAGRANT_SMB_PASSWORD'] 73 | end 74 | config.trigger.after :'VagrantPlugins::VSphere::Action::Clone', type: :action do |trigger| 75 | trigger.ruby do |env, machine| 76 | stdout, stderr, status = Open3.capture3( 77 | 'bash', 78 | 'configure-vsphere.sh', 79 | machine.id, 80 | "#{VM_ROOT_DISK_SIZE_GB}", 81 | "#{ENV['VSPHERE_VLAN']}") 82 | if status.exitstatus != 0 83 | raise "failed to configure vsphere. status=#{status.exitstatus} stdout=#{stdout} stderr=#{stderr}" 84 | end 85 | end 86 | end 87 | end 88 | ip = '10.10.10.2' 89 | config.vm.network :private_network, 90 | ip: ip, 91 | auto_config: false, 92 | libvirt__dhcp_enabled: false, 93 | libvirt__forward_mode: 'none' 94 | config.vm.provision :shell, path: 'provision.sh', args: ip 95 | config.vm.provision :shell, path: 'provision-pveproxy-certificate.sh', args: ip 96 | config.vm.provision :shell, path: 'provision-containers.sh', args: ip 97 | config.vm.provision :shell, path: 'summary.sh', args: ip 98 | end 99 | -------------------------------------------------------------------------------- /example/provision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | ip=$1 5 | fqdn=$(hostname --fqdn) 6 | 7 | # configure apt for non-interactive mode. 8 | export DEBIAN_FRONTEND=noninteractive 9 | 10 | # make sure the local apt cache is up to date. 11 | while true; do 12 | apt-get update && break || sleep 5 13 | done 14 | 15 | # extend the main partition to the end of the disk and extend the 16 | # pve/root and pve/data logical volume to use all the free space. 17 | apt-get install -y cloud-guest-utils 18 | if growpart /dev/[vs]da 3; then 19 | pvresize /dev/[vs]da3 20 | lvextend --extents +10%FREE /dev/pve/root 21 | resize2fs /dev/pve/root 22 | lvextend --extents +100%FREE /dev/pve/data 23 | fi 24 | 25 | # configure the network for NATting. 26 | ifdown vmbr0 27 | cat >/etc/network/interfaces </proc/sys/net/ipv4/ip_forward 46 | # NAT through eth0. 47 | post-up iptables -t nat -A POSTROUTING -s '$ip/24' ! -d '$ip/24' -o eth0 -j MASQUERADE 48 | post-down iptables -t nat -D POSTROUTING -s '$ip/24' ! -d '$ip/24' -o eth0 -j MASQUERADE 49 | EOF 50 | sed -i -E "s,^[^ ]+( .*pve.*)\$,$ip\1," /etc/hosts 51 | sed 's,\\,\\\\,g' >/etc/issue <<'EOF' 52 | 53 | _ __ _ __ _____ ___ __ ___ _____ __ __ _____ 54 | | '_ \| '__/ _ \ \/ / '_ ` _ \ / _ \ \/ / \ \ / / _ \ 55 | | |_) | | | (_) > <| | | | | | (_) > < \ V / __/ 56 | | .__/|_| \___/_/\_\_| |_| |_|\___/_/\_\ \_/ \___| 57 | | | 58 | |_| 59 | 60 | EOF 61 | cat >>/etc/issue <>/usr/share/pve-manager/js/pvemanagerlib.js 75 | 76 | # install vim. 77 | apt-get install -y --no-install-recommends vim 78 | cat >/etc/vim/vimrc.local <<'EOF' 79 | syntax on 80 | set background=dark 81 | set esckeys 82 | set ruler 83 | set laststatus=2 84 | set nobackup 85 | EOF 86 | 87 | # configure the shell. 88 | cat >/etc/profile.d/login.sh <<'EOF' 89 | [[ "$-" != *i* ]] && return 90 | export EDITOR=vim 91 | export PAGER=less 92 | alias l='ls -lF --color' 93 | alias ll='l -a' 94 | alias h='history 25' 95 | alias j='jobs -l' 96 | EOF 97 | 98 | cat >/etc/inputrc <<'EOF' 99 | set input-meta on 100 | set output-meta on 101 | set show-all-if-ambiguous on 102 | set completion-ignore-case on 103 | "\e[A": history-search-backward 104 | "\e[B": history-search-forward 105 | "\eOD": backward-word 106 | "\eOC": forward-word 107 | EOF 108 | 109 | # configure the motd. 110 | # NB this was generated at http://patorjk.com/software/taag/#p=display&f=Big&t=proxmox%20ve. 111 | # it could also be generated with figlet.org. 112 | cat >/etc/motd <<'EOF' 113 | 114 | _ __ _ __ _____ ___ __ ___ _____ __ __ _____ 115 | | '_ \| '__/ _ \ \/ / '_ ` _ \ / _ \ \/ / \ \ / / _ \ 116 | | |_) | | | (_) > <| | | | | | (_) > < \ V / __/ 117 | | .__/|_| \___/_/\_\_| |_| |_|\___/_/\_\ \_/ \___| 118 | | | 119 | |_| 120 | 121 | EOF 122 | 123 | # show versions. 124 | uname -a 125 | lvm version 126 | kvm --version 127 | lxc-ls --version 128 | cat /etc/os-release 129 | cat /etc/debian_version 130 | cat /etc/machine-id 131 | pveversion -v 132 | lsblk -x KNAME -o KNAME,SIZE,TRAN,SUBSYSTEMS,FSTYPE,UUID,LABEL,MODEL,SERIAL 133 | 134 | # show the proxmox web address. 135 | cat <tmp/$@-contents/metadata.json 105 | cp Vagrantfile.template tmp/$@-contents/Vagrantfile 106 | tar cvf $@ -C tmp/$@-contents . 107 | @./box-metadata.sh vsphere proxmox-ve-amd64 $@ 108 | 109 | clean: 110 | rm -rf packer_cache $${PACKER_OUTPUT_BASE_DIR:-.}/output-proxmox-ve* 111 | 112 | .PHONY: help build-libvirt build-uefi-libvirt build-proxmox build-uefi-proxmox build-hyperv build-vsphere clean 113 | -------------------------------------------------------------------------------- /proxmox-ve-vsphere.pkr.hcl: -------------------------------------------------------------------------------- 1 | packer { 2 | required_plugins { 3 | # see https://github.com/hashicorp/packer-plugin-vsphere 4 | vsphere = { 5 | version = "2.0.0" 6 | source = "github.com/hashicorp/vsphere" 7 | } 8 | } 9 | } 10 | 11 | variable "cpus" { 12 | type = number 13 | default = 2 14 | } 15 | 16 | variable "memory" { 17 | type = number 18 | default = 2 * 1024 19 | } 20 | 21 | variable "disk_size" { 22 | type = number 23 | default = 20 * 1024 24 | } 25 | 26 | variable "vsphere_os_iso" { 27 | type = string 28 | default = env("VSPHERE_OS_ISO") 29 | } 30 | 31 | variable "vsphere_host" { 32 | type = string 33 | default = env("GOVC_HOST") 34 | } 35 | 36 | variable "vsphere_username" { 37 | type = string 38 | default = env("GOVC_USERNAME") 39 | } 40 | 41 | variable "vsphere_password" { 42 | type = string 43 | default = env("GOVC_PASSWORD") 44 | } 45 | 46 | variable "vsphere_esxi_host" { 47 | type = string 48 | default = env("VSPHERE_ESXI_HOST") 49 | } 50 | 51 | variable "vsphere_datacenter" { 52 | type = string 53 | default = env("GOVC_DATACENTER") 54 | } 55 | 56 | variable "vsphere_cluster" { 57 | type = string 58 | default = env("GOVC_CLUSTER") 59 | } 60 | 61 | variable "vsphere_datastore" { 62 | type = string 63 | default = env("GOVC_DATASTORE") 64 | } 65 | 66 | variable "vsphere_folder" { 67 | type = string 68 | default = env("VSPHERE_TEMPLATE_FOLDER") 69 | } 70 | 71 | variable "vsphere_network" { 72 | type = string 73 | default = env("VSPHERE_VLAN") 74 | } 75 | 76 | variable "vsphere_ip_wait_address" { 77 | type = string 78 | default = env("VSPHERE_IP_WAIT_ADDRESS") 79 | } 80 | 81 | variable "apt_cache_host" { 82 | type = string 83 | default = env("APT_CACHE_HOST") 84 | } 85 | 86 | variable "apt_cache_port" { 87 | type = string 88 | default = env("APT_CACHE_PORT") 89 | } 90 | 91 | variable "shell_provisioner_scripts" { 92 | type = list(string) 93 | default = [ 94 | "provisioners/apt_proxy.sh", 95 | "provisioners/upgrade.sh", 96 | "provisioners/network.sh", 97 | "provisioners/localisation-pt.sh", 98 | "provisioners/reboot.sh", 99 | "provisioners/provision.sh", 100 | ] 101 | } 102 | 103 | source "vsphere-iso" "proxmox-ve-amd64" { 104 | vm_name = "proxmox-ve-amd64" 105 | guest_os_type = "debian12_64Guest" 106 | NestedHV = true 107 | CPUs = var.cpus 108 | RAM = var.memory 109 | storage { 110 | disk_size = var.disk_size 111 | disk_thin_provisioned = true 112 | } 113 | disk_controller_type = ["pvscsi"] 114 | iso_paths = [ 115 | var.vsphere_os_iso, 116 | ] 117 | vcenter_server = var.vsphere_host 118 | username = var.vsphere_username 119 | password = var.vsphere_password 120 | insecure_connection = true 121 | datacenter = var.vsphere_datacenter 122 | cluster = var.vsphere_cluster 123 | host = var.vsphere_esxi_host 124 | folder = var.vsphere_folder 125 | datastore = var.vsphere_datastore 126 | network_adapters { 127 | network = var.vsphere_network 128 | network_card = "vmxnet3" 129 | } 130 | convert_to_template = true 131 | ssh_username = "root" 132 | ssh_password = "password" 133 | ssh_timeout = "60m" 134 | cd_label = "proxmox-ais" 135 | cd_files = ["answer.toml"] 136 | boot_wait = "5s" 137 | boot_command = [ 138 | # select Advanced Options. 139 | "", 140 | # select Install Proxmox VE (Automated). 141 | "", 142 | # wait for the shell prompt. 143 | "", 144 | # do the installation. 145 | "proxmox-fetch-answer partition proxmox-ais >/run/automatic-installer-answersexit", 146 | # wait for the installation to finish. 147 | "", 148 | # login. 149 | "rootpassword", 150 | # install the guest agent. 151 | "apt-get update", 152 | "apt-get install -y open-vm-tools", 153 | ] 154 | shutdown_command = "poweroff" 155 | } 156 | 157 | build { 158 | sources = [ 159 | "source.vsphere-iso.proxmox-ve-amd64", 160 | ] 161 | 162 | provisioner "shell" { 163 | expect_disconnect = true 164 | environment_vars = [ 165 | "apt_cache_host=${var.apt_cache_host}", 166 | "apt_cache_port=${var.apt_cache_port}", 167 | ] 168 | scripts = var.shell_provisioner_scripts 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /provisioners/provision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | 4 | # configure apt for non-interactive mode. 5 | export DEBIAN_FRONTEND=noninteractive 6 | 7 | # remove old kernel packages. 8 | # NB as of pve 8.1, there's a metapackage, proxmox-kernel-6.5, then there are 9 | # the real kernels at proxmox-kernel-*-pve-signed (these are the ones that 10 | # are removed). 11 | proxmox_kernels=$(dpkg-query -f '${Package}\n' -W 'proxmox-kernel-*-pve-signed') 12 | for proxmox_kernel in $proxmox_kernels; do 13 | if [[ $proxmox_kernel != "proxmox-kernel-$(uname -r)-signed" ]]; then 14 | apt-get remove -y --purge $proxmox_kernel 15 | fi 16 | done 17 | 18 | # let the root user login. 19 | cat >/etc/ssh/sshd_config.d/local.conf <<'EOF' 20 | PermitRootLogin yes 21 | EOF 22 | 23 | # create a group where sudo will not ask for a password. 24 | apt-get install -q -y sudo 25 | groupadd -r admin 26 | echo '%admin ALL=(ALL) NOPASSWD:ALL' >/etc/sudoers.d/admin 27 | 28 | # create the vagrant user. also allow access with the insecure vagrant public key. 29 | # NB vagrant will replace it on the first run. 30 | groupadd vagrant 31 | useradd -g vagrant -m vagrant -s /bin/bash 32 | gpasswd -a vagrant admin 33 | chmod 750 /home/vagrant 34 | install -d -m 700 /home/vagrant/.ssh 35 | pushd /home/vagrant/.ssh 36 | wget -q --no-check-certificate https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub -O authorized_keys 37 | chmod 600 authorized_keys 38 | chown -R vagrant:vagrant . 39 | popd 40 | 41 | # install the Guest Additions. 42 | if [ -n "$(lspci | grep VMware | head -1)" ]; then 43 | # install the VMware Guest Additions. 44 | # NB the open-vm-tools package was already installed from the packer boot_command. 45 | true 46 | else 47 | # install the qemu-kvm Guest Additions. 48 | apt-get install -y qemu-guest-agent spice-vdagent 49 | fi 50 | 51 | # install rsync and sshfs to support shared folders in vagrant. 52 | apt-get install -y rsync sshfs 53 | 54 | # disable the DNS reverse lookup on the SSH server. this stops it from 55 | # trying to resolve the client IP address into a DNS domain name, which 56 | # is kinda slow and does not normally work when running inside VB. 57 | echo UseDNS no >>/etc/ssh/sshd_config 58 | 59 | # make sure the ssh connections are properly closed when the system is shutdown. 60 | # NB this is needed for vagrant reload and vagrant-reload plugin. 61 | # NB this also needs UsePAM yes in sshd_config (which is already there). 62 | apt-get install -y libpam-systemd 63 | 64 | # disable the graphical terminal. its kinda slow and useless on a VM. 65 | sed -i -E 's,#(GRUB_TERMINAL\s*=).*,\1console,g' /etc/default/grub 66 | update-grub 67 | 68 | # reset the machine-id. 69 | # NB systemd will re-generate it on the next boot. 70 | # NB machine-id is indirectly used in DHCP as Option 61 (Client Identifier), which 71 | # the DHCP server uses to (re-)assign the same or new client IP address. 72 | # see https://www.freedesktop.org/software/systemd/man/machine-id.html 73 | # see https://www.freedesktop.org/software/systemd/man/systemd-machine-id-setup.html 74 | echo '' >/etc/machine-id 75 | rm -f /var/lib/dbus/machine-id 76 | 77 | # reset the random-seed. 78 | # NB systemd-random-seed re-generates it on every boot and shutdown. 79 | # NB you can prove that random-seed file does not exist on the image with: 80 | # sudo virt-filesystems -a ~/.vagrant.d/boxes/proxmox-ve-amd64/0/libvirt/box.img 81 | # sudo guestmount -a ~/.vagrant.d/boxes/proxmox-ve-amd64/0/libvirt/box.img -m /dev/pve/root --pid-file guestmount.pid --ro /mnt 82 | # sudo ls -laF /mnt/var/lib/systemd 83 | # sudo guestunmount /mnt 84 | # sudo bash -c 'while kill -0 $(cat guestmount.pid) 2>/dev/null; do sleep .1; done; rm guestmount.pid' # wait for guestmount to finish. 85 | # see https://www.freedesktop.org/software/systemd/man/systemd-random-seed.service.html 86 | # see https://manpages.debian.org/stretch/manpages/random.4.en.html 87 | # see https://manpages.debian.org/stretch/manpages/random.7.en.html 88 | # see https://github.com/systemd/systemd/blob/master/src/random-seed/random-seed.c 89 | # see https://github.com/torvalds/linux/blob/master/drivers/char/random.c 90 | systemctl stop systemd-random-seed 91 | rm -f /var/lib/systemd/random-seed 92 | 93 | # clean packages. 94 | apt-get -y autoremove 95 | apt-get -y clean 96 | 97 | # show the free space. 98 | df -h / 99 | 100 | # zero the free disk space -- for better compression of the box file. 101 | # NB prefer discard/trim (safer; faster) over creating a big zero filled file 102 | # (somewhat unsafe as it has to fill the entire disk, which might trigger 103 | # a disk (near) full alarm; slower; slightly better compression). 104 | if [ "$(lsblk -no DISC-GRAN $(findmnt -no SOURCE /) | awk '{print $1}')" != '0B' ]; then 105 | while true; do 106 | output="$(fstrim -v /)" 107 | cat <<<"$output" 108 | sync && sync && sleep 15 109 | bytes_trimmed="$(echo "$output" | perl -n -e '/\((\d+) bytes\)/ && print $1')" 110 | # NB if this never reaches zero, it might be because there is not 111 | # enough free space for completing the trim. 112 | if (( bytes_trimmed < $((100*1024*1024)) )); then # < 100 MiB is good enough. 113 | break 114 | fi 115 | done 116 | else 117 | dd if=/dev/zero of=/EMPTY bs=1M || true && sync && rm -f /EMPTY 118 | fi 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This builds an up-to-date [Proxmox VE](https://www.proxmox.com/en/proxmox-ve) Vagrant Base Box. 2 | 3 | Currently this targets Proxmox VE 8. 4 | 5 | # Usage 6 | 7 | Create the base box as described in the section corresponding to your provider. 8 | 9 | If you want to troubleshoot the packer execution see the `.log` file that is created in the current directory. 10 | 11 | After the example vagrant environment is started, you can access the [Proxmox Web Interface](https://10.10.10.2:8006/) with the default `root` user and password `password`. 12 | 13 | For a cluster example see [rgl/proxmox-ve-cluster-vagrant](https://github.com/rgl/proxmox-ve-cluster-vagrant). 14 | 15 | ## libvirt 16 | 17 | Create the base box: 18 | 19 | ```bash 20 | make build-libvirt 21 | ``` 22 | 23 | Add the base box as suggested in make output: 24 | 25 | ```bash 26 | vagrant box add -f proxmox-ve-amd64 proxmox-ve-amd64-libvirt.box 27 | ``` 28 | 29 | Start the example vagrant environment with: 30 | 31 | ```bash 32 | cd example 33 | vagrant up --no-destroy-on-error --provider=libvirt 34 | ``` 35 | 36 | ## Proxmox 37 | 38 | Set the Proxmox VE details: 39 | 40 | ```bash 41 | cat >secrets-proxmox.sh <secrets-hyperv.sh <<'EOF' 94 | # set this value when you need to set the VM Switch Name. 95 | export HYPERV_SWITCH_NAME='Vagrant' 96 | 97 | # set this value when you need to set the VM VLAN ID. 98 | unset HYPERV_VLAN_ID 99 | #export HYPERV_VLAN_ID='' 100 | 101 | # set the credentials that the guest will use 102 | # to connect to this host smb share. 103 | # NB you should create a new local user named _vagrant_share 104 | # and use that one here instead of your user credentials. 105 | # NB it would be nice for this user to have its credentials 106 | # automatically rotated, if you implement that feature, 107 | # let me known! 108 | export VAGRANT_SMB_USERNAME='_vagrant_share' 109 | export VAGRANT_SMB_PASSWORD='' 110 | 111 | # remove the virtual switch from the windows firewall. 112 | # NB execute if the VM fails to obtain an IP address from DHCP. 113 | PowerShell -Command 'Set-NetFirewallProfile -DisabledInterfaceAliases (Get-NetAdapter -name "vEthernet*" | Where-Object {$_.ifIndex}).InterfaceAlias' 114 | EOF 115 | source secrets-hyperv.sh 116 | ``` 117 | 118 | Create the base box: 119 | 120 | ```bash 121 | make build-hyperv 122 | ``` 123 | 124 | Add the base box as suggested in make output: 125 | 126 | ```bash 127 | vagrant box add -f proxmox-ve-amd64 proxmox-ve-amd64-hyperv.box 128 | ``` 129 | 130 | Start the example vagrant environment with: 131 | 132 | ```bash 133 | cd example 134 | vagrant up --provider=hyperv 135 | ``` 136 | 137 | ## VMware vSphere usage 138 | 139 | Download [govc](https://github.com/vmware/govmomi/releases/latest) and place it inside your `/usr/local/bin` directory. 140 | 141 | Set your VMware vSphere details and test the connection: 142 | 143 | ```bash 144 | sudo apt-get install build-essential patch ruby-dev zlib1g-dev liblzma-dev 145 | vagrant plugin install vagrant-vsphere 146 | cat >secrets-vsphere.sh <<'EOF' 147 | export GOVC_INSECURE='1' 148 | export GOVC_HOST='vsphere.local' 149 | export GOVC_URL="https://$GOVC_HOST/sdk" 150 | export GOVC_USERNAME='administrator@vsphere.local' 151 | export GOVC_PASSWORD='password' 152 | export GOVC_DATACENTER='Datacenter' 153 | export GOVC_CLUSTER='Cluster' 154 | export GOVC_DATASTORE='Datastore' 155 | export VSPHERE_OS_ISO="[$GOVC_DATASTORE] iso/proxmox-ve_9.1-1.iso" 156 | export VSPHERE_ESXI_HOST='esxi.local' 157 | export VSPHERE_TEMPLATE_FOLDER='test/templates' 158 | # NB the VSPHERE_TEMPLATE_NAME last segment MUST match the 159 | # builders.vm_name property inside the packer template. 160 | export VSPHERE_TEMPLATE_NAME="$VSPHERE_TEMPLATE_FOLDER/proxmox-ve-amd64" 161 | export VSPHERE_VM_FOLDER='test' 162 | export VSPHERE_VM_NAME='proxmox-ve-example' 163 | # NB for the nested VMs to access the network, this VLAN port group security 164 | # policy MUST be configured to Accept: 165 | # Promiscuous mode 166 | # Forged transmits 167 | export VSPHERE_VLAN='packer' 168 | export VSPHERE_IP_WAIT_ADDRESS='0.0.0.0/0' 169 | # set the credentials that the guest will use 170 | # to connect to this host smb share. 171 | # NB you should create a new local user named _vagrant_share 172 | # and use that one here instead of your user credentials. 173 | # NB it would be nice for this user to have its credentials 174 | # automatically rotated, if you implement that feature, 175 | # let me known! 176 | export VAGRANT_SMB_USERNAME='_vagrant_share' 177 | export VAGRANT_SMB_PASSWORD='' 178 | EOF 179 | source secrets-vsphere.sh 180 | # see https://github.com/vmware/govmomi/blob/master/govc/USAGE.md 181 | govc version 182 | govc about 183 | govc datacenter.info # list datacenters 184 | govc find # find all managed objects 185 | ``` 186 | 187 | Download the Proxmox ISO (you can find the full iso URL in the [proxmox-ve.json](proxmox-ve.json) file) and place it inside the datastore as defined by the `iso_paths` property that is inside the [packer template](proxmox-ve-vsphere.json) file. 188 | 189 | See the [example Vagrantfile](example/Vagrantfile) to see how you could use a cloud-init configuration to configure the VM. 190 | 191 | Type `make build-vsphere` and follow the instructions. 192 | 193 | Try the example guest: 194 | 195 | ```bash 196 | source secrets-vsphere.sh 197 | cd example 198 | vagrant up --provider=vsphere --no-destroy-on-error --no-tty 199 | vagrant ssh 200 | exit 201 | vagrant destroy -f 202 | ``` 203 | 204 | ## Packer build performance options 205 | 206 | To improve the build performance you can use the following options. 207 | 208 | ### Accelerate build time with Apt Caching Proxy 209 | 210 | To speed up package downloads, you can specify an apt caching proxy 211 | (e.g. [apt-cacher-ng](https://www.unix-ag.uni-kl.de/~bloch/acng/)) 212 | by defining the environment variables `APT_CACHE_HOST` (default: undefined) 213 | and `APT_CACHE_PORT` (default: 3124). 214 | 215 | Example: 216 | 217 | ```bash 218 | APT_CACHE_HOST=10.10.10.100 make build-libvirt 219 | ``` 220 | 221 | ### Decrease disk wear by using temporary memory file-system 222 | 223 | To decrease disk wear (and potentially reduce io times), 224 | you can use `/dev/shm` (temporary memory file-system) as `output_directory` for Packer builders. 225 | Your system must have enough available memory to store the created virtual machine. 226 | 227 | Example: 228 | 229 | ```bash 230 | PACKER_OUTPUT_BASE_DIR=/dev/shm make build-libvirt 231 | ``` 232 | 233 | Remember to also define `PACKER_OUTPUT_BASE_DIR` when you run `make clean` afterwards. 234 | 235 | ## Variables override 236 | 237 | Some properties of the virtual machine and the Proxmox VE installation can be overridden. 238 | Take a look at `proxmox-ve.pkr.hcl`, `variable` blocks, to get an idea which values can be 239 | overridden. Do not override `iso_url` and `iso_checksum` as the `boot_command`s might be 240 | tied to a specific Proxmox VE version. 241 | 242 | Create the base box: 243 | 244 | ```bash 245 | make build-libvirt VAR_FILE=example.pkrvars.hcl 246 | ``` 247 | 248 | The following content of `example.pkrvars.hcl`: 249 | 250 | * sets the initial disk size to 128 GB 251 | * sets the initial memory to 4 GB 252 | * sets the Packer output base directory to /dev/shm 253 | * uses all default shell provisioners (see [`./provisioners`](./provisioners)) and a 254 | custom one for german localisation 255 | 256 | ```hcl 257 | disk_size = 128 * 1024 258 | memory = 4 * 1024 259 | output_base_dir = "/dev/shm" 260 | shell_provisioner_scripts = [ 261 | "provisioners/apt_proxy.sh", 262 | "provisioners/upgrade.sh", 263 | "provisioners/network.sh", 264 | "provisioners/localisation-de.sh", 265 | "provisioners/reboot.sh", 266 | "provisioners/provision.sh", 267 | ] 268 | ``` 269 | 270 | # Packer boot_command 271 | 272 | The Proxmox installation is [automatically configured](https://pve.proxmox.com/wiki/Automated_Installation) using the [`answer.toml` file](answer.toml), but to trigger the automatic installation, this environment has to nudge the default Proxmox installation ISO to use the [`answer.toml` file](answer.toml) through the packer `boot_command` interface. This is quite fragile, so be aware when you change anything. The following table describes the current steps and corresponding answers. 273 | 274 | | step | boot_command | 275 | |----------------------------------------:|--------------------------------------------------------------------------------------------| 276 | | select "Advanced Options" | `` | 277 | | select "Install Proxmox VE (Automated)" | `` | 278 | | wait for the shell prompt | `` | 279 | | do the installation | `proxmox-fetch-answer partition >/run/automatic-installer-answersexit` | 280 | -------------------------------------------------------------------------------- /proxmox-ve.pkr.hcl: -------------------------------------------------------------------------------- 1 | packer { 2 | required_plugins { 3 | # see https://github.com/hashicorp/packer-plugin-qemu 4 | qemu = { 5 | version = "1.1.4" 6 | source = "github.com/hashicorp/qemu" 7 | } 8 | # see https://github.com/hashicorp/packer-plugin-proxmox 9 | proxmox = { 10 | version = "1.2.3" 11 | source = "github.com/hashicorp/proxmox" 12 | } 13 | # see https://github.com/hashicorp/packer-plugin-hyperv 14 | hyperv = { 15 | version = "1.1.5" 16 | source = "github.com/hashicorp/hyperv" 17 | } 18 | # see https://github.com/hashicorp/packer-plugin-vagrant 19 | vagrant = { 20 | version = "1.1.6" 21 | source = "github.com/hashicorp/vagrant" 22 | } 23 | } 24 | } 25 | 26 | variable "vagrant_box" { 27 | type = string 28 | } 29 | 30 | variable "cpus" { 31 | type = number 32 | default = 2 33 | } 34 | 35 | variable "memory" { 36 | type = number 37 | default = 2 * 1024 38 | } 39 | 40 | variable "disk_size" { 41 | type = number 42 | default = 20 * 1024 43 | } 44 | 45 | variable "iso_url" { 46 | type = string 47 | default = "http://download.proxmox.com/iso/proxmox-ve_9.1-1.iso" 48 | } 49 | 50 | variable "iso_checksum" { 51 | type = string 52 | default = "sha256:6d8f5afc78c0c66812d7272cde7c8b98be7eb54401ceb045400db05eb5ae6d22" 53 | } 54 | 55 | variable "proxmox_node" { 56 | type = string 57 | default = env("PROXMOX_NODE") 58 | } 59 | 60 | variable "hyperv_switch_name" { 61 | type = string 62 | default = env("HYPERV_SWITCH_NAME") 63 | } 64 | 65 | variable "hyperv_vlan_id" { 66 | type = string 67 | default = env("HYPERV_VLAN_ID") 68 | } 69 | 70 | variable "apt_cache_host" { 71 | type = string 72 | default = env("APT_CACHE_HOST") 73 | } 74 | 75 | variable "apt_cache_port" { 76 | type = string 77 | default = env("APT_CACHE_PORT") 78 | } 79 | 80 | variable "output_base_dir" { 81 | type = string 82 | default = env("PACKER_OUTPUT_BASE_DIR") 83 | } 84 | 85 | variable "shell_provisioner_scripts" { 86 | type = list(string) 87 | default = [ 88 | "provisioners/apt_proxy.sh", 89 | "provisioners/upgrade.sh", 90 | "provisioners/network.sh", 91 | "provisioners/localisation-pt.sh", 92 | "provisioners/reboot.sh", 93 | "provisioners/provision.sh", 94 | ] 95 | } 96 | 97 | source "qemu" "proxmox-ve-amd64" { 98 | accelerator = "kvm" 99 | machine_type = "q35" 100 | cpus = var.cpus 101 | memory = var.memory 102 | qemuargs = [ 103 | ["-cpu", "host"], 104 | ] 105 | headless = true 106 | use_default_display = false 107 | net_device = "virtio-net" 108 | format = "qcow2" 109 | disk_size = var.disk_size 110 | disk_interface = "virtio-scsi" 111 | disk_cache = "unsafe" 112 | disk_discard = "unmap" 113 | iso_url = var.iso_url 114 | iso_checksum = var.iso_checksum 115 | output_directory = "${var.output_base_dir}/output-{{build_name}}" 116 | ssh_username = "root" 117 | ssh_password = "password" 118 | ssh_timeout = "60m" 119 | cd_label = "proxmox-ais" 120 | cd_files = ["answer.toml"] 121 | boot_wait = "5s" 122 | boot_command = [ 123 | # select Advanced Options. 124 | "", 125 | # select Install Proxmox VE (Automated). 126 | "", 127 | # wait for the shell prompt. 128 | "", 129 | # do the installation. 130 | "proxmox-fetch-answer partition proxmox-ais >/run/automatic-installer-answersexit", 131 | ] 132 | shutdown_command = "poweroff" 133 | } 134 | 135 | source "qemu" "proxmox-ve-uefi-amd64" { 136 | accelerator = "kvm" 137 | machine_type = "q35" 138 | efi_boot = true 139 | cpus = var.cpus 140 | memory = var.memory 141 | qemuargs = [ 142 | ["-cpu", "host"], 143 | ] 144 | headless = true 145 | use_default_display = false 146 | net_device = "virtio-net" 147 | format = "qcow2" 148 | disk_size = var.disk_size 149 | disk_interface = "virtio-scsi" 150 | disk_cache = "unsafe" 151 | disk_discard = "unmap" 152 | iso_url = var.iso_url 153 | iso_checksum = var.iso_checksum 154 | ssh_username = "root" 155 | ssh_password = "password" 156 | ssh_timeout = "60m" 157 | cd_label = "proxmox-ais" 158 | cd_files = ["answer.toml"] 159 | boot_wait = "10s" 160 | boot_command = [ 161 | # select Advanced Options. 162 | "", 163 | # select Install Proxmox VE (Automated). 164 | "", 165 | # wait for the shell prompt. 166 | "", 167 | # do the installation. 168 | "proxmox-fetch-answer partition proxmox-ais >/run/automatic-installer-answersexit", 169 | ] 170 | shutdown_command = "poweroff" 171 | } 172 | 173 | source "proxmox-iso" "proxmox-ve-amd64" { 174 | template_name = "template-proxmox-ve" 175 | template_description = "See https://github.com/rgl/proxmox-ve" 176 | tags = "proxmox-ve;template" 177 | insecure_skip_tls_verify = true 178 | node = var.proxmox_node 179 | machine = "q35" 180 | bios = "seabios" 181 | cpu_type = "host" 182 | cores = var.cpus 183 | memory = var.memory 184 | vga { 185 | type = "qxl" 186 | memory = 16 187 | } 188 | network_adapters { 189 | model = "virtio" 190 | bridge = "vmbr0" 191 | } 192 | scsi_controller = "virtio-scsi-single" 193 | disks { 194 | type = "scsi" 195 | io_thread = true 196 | ssd = true 197 | discard = true 198 | disk_size = "${var.disk_size}M" 199 | storage_pool = "local-lvm" 200 | format = "raw" 201 | } 202 | boot_iso { 203 | type = "scsi" 204 | iso_storage_pool = "local" 205 | iso_url = var.iso_url 206 | iso_checksum = var.iso_checksum 207 | iso_download_pve = true 208 | unmount = true 209 | } 210 | additional_iso_files { 211 | type = "scsi" 212 | cd_label = "proxmox-ais" 213 | cd_files = ["answer.toml"] 214 | unmount = true 215 | iso_storage_pool = "local" 216 | } 217 | os = "l26" 218 | ssh_username = "root" 219 | ssh_password = "password" 220 | ssh_timeout = "60m" 221 | boot_wait = "30s" 222 | boot_command = [ 223 | # select Advanced Options. 224 | "", 225 | # select Install Proxmox VE (Automated). 226 | "", 227 | # wait for the shell prompt. 228 | "", 229 | # do the installation. 230 | "proxmox-fetch-answer partition proxmox-ais >/run/automatic-installer-answersexit", 231 | # wait for the installation to finish. 232 | "", 233 | # login. 234 | "rootpassword", 235 | # install the guest agent. 236 | "rm -f /etc/apt/sources.list.d/{pve-enterprise,ceph}.sources", 237 | "apt-get update", 238 | "apt-get install -y qemu-guest-agent", 239 | "systemctl start qemu-guest-agent", 240 | ] 241 | } 242 | 243 | source "proxmox-iso" "proxmox-ve-uefi-amd64" { 244 | template_name = "template-proxmox-ve-uefi" 245 | template_description = "See https://github.com/rgl/proxmox-ve" 246 | tags = "proxmox-ve-uefi;template" 247 | insecure_skip_tls_verify = true 248 | node = var.proxmox_node 249 | machine = "q35" 250 | bios = "ovmf" 251 | efi_config { 252 | efi_storage_pool = "local-lvm" 253 | } 254 | cpu_type = "host" 255 | cores = var.cpus 256 | memory = var.memory 257 | vga { 258 | type = "qxl" 259 | memory = 16 260 | } 261 | network_adapters { 262 | model = "virtio" 263 | bridge = "vmbr0" 264 | } 265 | scsi_controller = "virtio-scsi-single" 266 | disks { 267 | type = "scsi" 268 | io_thread = true 269 | ssd = true 270 | discard = true 271 | disk_size = "${var.disk_size}M" 272 | storage_pool = "local-lvm" 273 | format = "raw" 274 | } 275 | boot_iso { 276 | type = "scsi" 277 | iso_storage_pool = "local" 278 | iso_url = var.iso_url 279 | iso_checksum = var.iso_checksum 280 | iso_download_pve = true 281 | unmount = true 282 | } 283 | additional_iso_files { 284 | type = "scsi" 285 | iso_storage_pool = "local" 286 | cd_label = "proxmox-ais" 287 | cd_files = ["answer.toml"] 288 | unmount = true 289 | } 290 | os = "l26" 291 | ssh_username = "root" 292 | ssh_password = "password" 293 | ssh_timeout = "60m" 294 | boot_wait = "30s" 295 | boot_command = [ 296 | # select Advanced Options. 297 | "", 298 | # select Install Proxmox VE (Automated). 299 | "", 300 | # wait for the shell prompt. 301 | "", 302 | # do the installation. 303 | "proxmox-fetch-answer partition proxmox-ais >/run/automatic-installer-answersexit", 304 | # wait for the installation to finish. 305 | "", 306 | # login. 307 | "rootpassword", 308 | # install the guest agent. 309 | "rm -f /etc/apt/sources.list.d/{pve-enterprise,ceph}.sources", 310 | "apt-get update", 311 | "apt-get install -y qemu-guest-agent", 312 | "systemctl start qemu-guest-agent", 313 | ] 314 | } 315 | 316 | source "hyperv-iso" "proxmox-ve-amd64" { 317 | temp_path = "tmp" 318 | headless = true 319 | generation = 2 320 | enable_virtualization_extensions = true 321 | enable_mac_spoofing = true 322 | cpus = var.cpus 323 | memory = var.memory 324 | switch_name = var.hyperv_switch_name 325 | vlan_id = var.hyperv_vlan_id 326 | disk_size = var.disk_size 327 | iso_url = var.iso_url 328 | iso_checksum = var.iso_checksum 329 | output_directory = "${var.output_base_dir}/output-{{build_name}}" 330 | ssh_username = "root" 331 | ssh_password = "password" 332 | ssh_timeout = "60m" 333 | first_boot_device = "DVD" 334 | boot_order = ["SCSI:0:0"] 335 | cd_label = "proxmox-ais" 336 | cd_files = ["answer.toml"] 337 | boot_wait = "5s" 338 | boot_command = [ 339 | # select Advanced Options. 340 | "", 341 | # select Install Proxmox VE (Automated). 342 | "", 343 | # wait for the shell prompt. 344 | "", 345 | # do the installation. 346 | "proxmox-fetch-answer partition proxmox-ais >/run/automatic-installer-answersexit", 347 | # wait for the installation to finish. 348 | "", 349 | # login. 350 | "rootpassword", 351 | # install the guest agent. 352 | "rm -f /etc/apt/sources.list.d/{pve-enterprise,ceph}.sources", 353 | "apt-get update", 354 | "apt-get install -y hyperv-daemons", 355 | ] 356 | shutdown_command = "poweroff" 357 | } 358 | 359 | build { 360 | sources = [ 361 | "source.qemu.proxmox-ve-amd64", 362 | "source.qemu.proxmox-ve-uefi-amd64", 363 | "source.proxmox-iso.proxmox-ve-amd64", 364 | "source.proxmox-iso.proxmox-ve-uefi-amd64", 365 | "source.hyperv-iso.proxmox-ve-amd64", 366 | ] 367 | 368 | provisioner "shell" { 369 | expect_disconnect = true 370 | environment_vars = [ 371 | "apt_cache_host=${var.apt_cache_host}", 372 | "apt_cache_port=${var.apt_cache_port}", 373 | ] 374 | scripts = var.shell_provisioner_scripts 375 | } 376 | 377 | post-processor "vagrant" { 378 | only = [ 379 | "qemu.proxmox-ve-amd64", 380 | "hyperv-iso.proxmox-ve-amd64", 381 | ] 382 | output = var.vagrant_box 383 | vagrantfile_template = "Vagrantfile.template" 384 | } 385 | 386 | post-processor "vagrant" { 387 | only = [ 388 | "qemu.proxmox-ve-uefi-amd64", 389 | ] 390 | output = var.vagrant_box 391 | vagrantfile_template = "Vagrantfile-uefi.template" 392 | } 393 | } 394 | --------------------------------------------------------------------------------