Berawal dari rasa kesal karena melakukan task repetitif ketika membuat VM (Virtual Machine) dalam Proxmox, sehingga saya penasaran dengan cara bagaimana bila kerjaan tersebut dijadikan otomatis? Lalu, lahir lah postingan ini. Hal pertama yang saya lakukan ialah dengan mencari referensi tools apa yang akan digunakan dan saya menemukan tools bernama Terraform.
Terraform adalah tools open-source buatan Hashicorp yang dapat mengelola Infrastructure as Code (IaC) atau mungkin apabila diterjemahkan kedalam Bahasa Indonesia menjadi "Infrastruktur Berbasis Kode", terdengar kurang nyambung ya? Maka dari itu pengigat IT lebih sering menggunakan istilah aslinya.
Tools ini menggunakan bahasa HCL (HashiCorp Configuration Language). Karena alat ini bersifat open-source, maka banyak orang/komunitas yang dapat berkontribusi di project ini. Salah satunya Proxmox. Oh iya, provider terraform itu sendiri adalah jembatan penghubung dari konfigurasi yang kita buat ke endpoint infrastruktur, tanpa itu kita tidak akan bisa membuat atau mengatur resource dari infrastruktur tujuan. Oke, cukup ceritanya sekarang lanjut ke pengerjaan.
1. Lab Environment
Proxmox v9.1.1
Terraform v1.14.8
Terraform provider (bpg/proxmox) v0.103.0
Ubuntu 24.04 noble
yaml parsing 'yq' (https://github.com/mikefarah/yq/) v4.49.2
envsubt 'gettext-base' v0.21
2. Setup API di Proxmox untuk Terraform
Ada 2 setup yang akan dilakukan untuk integrasi terrafom di proxmox yaitu setup API Connection dan Deploy Terraform. Detailnya saya jabarkan pada penjelasan dibawah ini.
2.1 Create Group and Roles
Sebelum setup API, saya membuat grup yang akan dipakai terraform. Grup di proxmox berguna untuk menandakan workdir dan environment terraform saya. Grup ini juga berfungsi untuk isolate permission, jadi kita bisa membuat rule atau aturan untuk grup ini terkait alokasi datastore, audit, dll. Untuk pembuatan grup bisa akses ke dashboard proxmox lalu pada sidebar, klik Datacenter > Permissions > Groups kemudian Create new group.

Setelah itu, kembali ke Permissions > Roles > Create new role

Untuk privileges saya menggunakan beberapa rules berikut:
untuk privileges nya bisa kasih ini
#Storage privileges
Datastore.AllocateSpace
Datastore.Audit#Network privilege
SDN.Use#VM privileges
VM.Allocate
VM.Audit
VM.Clone
VM.Config.CDROM
VM.Config.CPU
VM.Config.Cloudinit
VM.Config.Disk
VM.Config.HWType
VM.Config.Memory
VM.Config.Network
VM.Config.Options
VM.Console
VM.GuestAgent.Audit
VM.GuestAgent.Unrestricted
VM.PowerMgmt
2.2 Memasukkan Permission Roles kedalam Group
Klik Datacenter > Permissions. Selanjutnya klik add > group permissions.

Pada opsi Path, kita pilih root dir (/), karena provisioning memerlukan akses ke beberapa resource. Untuk itu saya berikan permission akses ke direktori root, agar grup terraform dapat full akses terhadap resouce proxmox sendiri. Kemudian ceklis Propogate agar direktori dibawah root bisa diakses dan memudahkan semua pemanggilan API ke proxmox.
2.3 Create user
Klik Datacenter > Permissions > Users, lalu pilih Add. Buat user baru dan masukkan ke dalam grup terraform yang telah dibuat sebelumnya. Pada bagian Realm, pilih Linux PAM standard authentication. Opsi ini memastikan user yang dibuat terintegrasi langsung dengan sistem autentikasi OS host, yang nantinya mempermudah manajemen akses dan sinkronisasi permission pada VM dengan single key.

2.4 Create token
Navigasi ke Datacenter > Permissions > API Token, klik Add, lalu pilih user yang telah dibuat sebelumnya.

Token ID: Merupakan identitas unik untuk token ini. ID ini bersifat permanen, wajib dicatat, dan akan digunakan dalam konfigurasi Terraform.
Privilege Separation: Fitur ini digunakan jika kita ingin membatasi hak akses spesifik pada token tersebut. Karena kita akan menggunakan permission role yang sudah diatur pada level Group/User, bagian ini tidak perlu diceklis agar token otomatis ikut permission yang sebelumnya kita buat.
Karena token hanya bisa dilihat sekali, maka dari itu setelah buat simpan baik baik token tersebut, apabila hilang, kita harus generate ulang token.
2.5 Cek Token
Untuk melihat API yang sudah kita buat sebelumnya, kita bisa cek di Datacenter > Permissions > API token , lalu klik token yang telah dibuat, selanjutnya klik show permission.

3. Membuat VM Template
Setelah proses pembuatan API untuk Terraform selesai, selanjutnya kita harus membuat template atau master vm untuk terrafom. Pada step ini saya menggunakan CLI (Command Line Interface), alasannya karena pengerjaan ini lebih mudah dan cepat apabila menggunakan CLI. Perintah dan penjelasannya bisa dibaca pada poin-poin berikut
3.1 Buat workdir dan download image
Image yang didownload diutamakan image yang sudah ada cloud-init didalamnya. Image-image yang sudah ada cloud-init biasanya mengandung kata cloud pada nama imagenya. Misalnya, jammy-server-cloudimg.img atau CentOS-7-x86_64-GenericCloud.qcow2. Agar mempermudah untuk pembuatan template, image yang disarankanpun berformat img,qcow2 dan raw. Berikut contohnya
mkdir tes-tfgen
cd tes-tfgen
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img -O jammy.img3.2 Buat VM template
Agar size template image tidak terlalu besar, disarankan gunakan size disk secukupnya. Sebisa mungkin, seminimal mungkin untuk efisiensi utilisasi server kita. Apabil ingin menambahkan disk, bisa ditambah setelah template sudah jadi. Plan template image dan vm template sebagai berikut:
Nama image: jammy
Image OS: Ubuntu 20
Extend Disk: 10G
# Buat VM template tanpa disk
qm create 6969 --name jammy --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pciJika sebelumnya sudah ada volume, bisa diattach kedalam vm template tersebut
qm importdisk 6969 jammy.img local-lvm
qm set 6969 --scsi0 local-lvm:6969/vm-6969-disk-0.rawApabila tidak ingin pakai volume, bisa dijalankan perintah berikut
qm set 6969 --scsi0 local-lvm:0,import-from=/root/tes-tfgen/jammy.imgTambah properties untuk image. Tambah yang dibutuhkan saja
# lokasi boot order
qm set 6969 --boot order=scsi0
# opsional apabila ingin extend vm storage
qm resize 6969 scsi0 10G
# IDE0 adalah lokasi cloud-init yang diset untuk local-lvm
qm set 6969 --ide0 local-lvm:cloudinit
# Serial0 sebagai output display (CLI)
qm set 6969 --serial0 socket --vga serial0
# Aktifkan QEMU guest agent
qm set 6969 --agent enabled=1Setup cloud-init
# User vm
qm set 6969 --ciuser ubuntu
# Set password (wajib)
qm set 6969 --cipassword 123 ##necessary on ubuntu cloudimg
# IP diset dhcp client
qm set 6969 --ipconfig0 ip=dhcpLalu start vm template.
# Start VM
qm start 69693.3 Install cloud-init
Tahapan diatas belum selesai, karena kita harus memastikan beberapa hal berikut
Cloud-init service: pastikan cloud-init sudah autorun pada systemd. Karena jika tidak, metadata yang dimasukkan proxmox (vm name, ip, network, dll) tidak akan terpasang (terinject).
sudo systemctl status cloud-initQemu agent: qemu agent harus dipasang untuk sinkronisasi data dari proxmox ke vm tersebut. Misal: sinkron jam ataupun perintah untuk shutdown dari dashboard proxmox.
sudo apt install qemu-guest-agent -y sudo systemctl enable qemu-guest-agentHapus machine-id: Machine-id harus dihapus karena kita akan membuat vm yang lebih dari 1, jadi machine-id tidak boleh sama. Jika sama, vm tersebut akan mempunyai data yang sama. Misal: ip dari dhcp server akan sama karena machine-id yang sama.
sudo truncate -s 0 /etc/machine-id sudo rm /var/lib/dbus/machine-id sudo ln -s /etc/machine-id /var/lib/dbus/machine-id
Selanjutnya template bisa di shutoff untuk mengambil snapshot vm-nya. Perintahnya bisa menggunakan sudo shutdown now.
4. Generate VM Template
Setelah vm template sudah dibuat dan di shutoff, Selanjutnya kita masuk ke tahapan generate vm template didalam proxmox. Tahapan ini wajib dijalankan karena Terraform memerlukan unit dengan status Template untuk melakukan proses cloning secara otomatis. Jika tidak diubah menjadi template, Terraform tidak akan mengenali ID tersebut sebagai source image yang valid.
4.1 Generating VM with terrafrom template (workaround)
Disini saya menggunakan template yang saya buat sendiri, apabila ingin menggunakannya silakan fork repository saya. Penjelasan template tersebut saya sertakan di repo tersebut. Karena jika saya masukkan kedalam artikel ini, akan terlalu panjang. Di sini saya hanya memberikan workaround untuk penggunaanya. Untuk host-nya sendiri saya pakai ubuntu noble.
Install package: Pada dasarnya, tidak ada OS yang bundling dengan package terraform, kecuali custom OS. Jadi kita harus memasang terraform di proxmox sebelum deploy multi vm.
## tambah repository terraform sudo apt-get update && sudo apt-get install -y gnupg software-properties-common wget -O- https://apt.releases.hashicorp.com/gpg | \ gpg --dearmor | \ sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list sudo apt update -y ; sudo apt install terraform -y ## install yaml parsing dan envsubt sudo apt-get install gettext-base sudo snap install yqClone repository: Saya menggunakan template yang sebelumnya saya buat dan saya publish di github.
## Clone github repository git clone https://github.com/fareisa/Spawn-VM-banyak-di-Proxmox cd Spawn-VM-banyak-di-Proxmox ## copy dan edit vm.yaml cp vm.yaml tes.yml nano tes.yml
Sedikit penjelasan, kenapa make yaml parsing dari luar, kenapa gk json parsing.
saya milih yaml karena emang enak aja kalau buat dijadiin template, sama kenapa yaml parsing dari luar, bukan dari package ubuntu nya?
'yq' dari ubuntu sama kayak 'jq' cuman beda depannya make y :v (https://unix.stackexchange.com/questions/774082/executing-yq-but-jq-gets-executed) makanya make yang rilll 'yq' (https://github.com/mikefarah/yq/)
4.2 Edit template
Apabila konfigurasi dan template yang kalian pakai sama dengan artikel ini, tidak perlu diedit apapun. Tapi apabila ada modifikasi yang kalian lakukan, bisa diubah sesuai yang kalian modif. Berikut contoh file konfig yang saya pakai
project_name: tes-wahaha
proxmox:
endpoint: "https://192.168.11.254:8006/api2/json"
api_token: "terraform@pam!tes=<token>"
node: "pve-core"
template_id: 6969
vms:
- name: tes-1
cores: 2
memory: 2048
user: ubuntu
password: ubuntu
ssh_keys:
- <ssh pubkey>
disks:
- datastore: local-lvm
interface: scsi0
size: 20
- datastore: shared-disk
interface: scsi1
size: 40
network:
- bridge: vmbr0
ip: 192.168.11.22/24
- bridge: vmbr1
ip: dhcp
- name: tes-2
cores: 2
memory: 2048
user: ubuntu
password: ubuntu
ssh_keys:
- <ssh pubkey>
disks:
- datastore: local-lvm
interface: scsi0
size: 20
- datastore: local-zfs
interface: scsi2
size: 40
network:
- bridge: vmbr0
ip: 192.168.11.45/24
gateway: 192.168.11.1
- bridge: vmbr14.3 Deploy dengan terraform
Setelah semuanya beres, saat yang kita tunggu-tunggu, yaitu buat multi vm di proxmox dengan terraform. Jalankan sesuai tahapan dibawah
Berikan akses execute untuk bash script
tfgen-1.shagar terraform dapat menjalankan script tersebut. lalu jalankan perintah terraform. Pastikan semua validasi statusnya OKchmod +x tfgen-1.sh # Execute terraform template ./tfgen-1.sh -i tes.yml # Terraform akan validasi config Validating YAML... Validation OK Replace ./tes-wahaha? [y/N]: y Rendering Terraform... Done → ./tes-wahaha/main.tfSetelah semua validasi ok, lanjut inisasi dan apply konfigurasi terraform.
cd ./tes-wahaha/ terraform init terraform apply
5. Testing
Untuk proses testing, saya menggunakan 2 metode. Yaitu ping dan ssh. Jika 2 metode itu sudah berhasil, kemungkinan besar vm sudah berhasil di deploy
Ping
wahaha@wahaha-thinkpad:~/Spawn-VM-banyak-di-Proxmox/tes-wahaha$ ping 192.168.11.22
PING 192.168.11.22 (192.168.11.22) 56(84) bytes of data.
64 bytes from 192.168.11.22: icmp_seq=1 ttl=62 time=9.19 ms
64 bytes from 192.168.11.22: icmp_seq=2 ttl=62 time=9.82 ms
^C
--- 192.168.11.22 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 9.187/9.501/9.815/0.314 ms
wahaha@wahaha-thinkpad:~/Spawn-VM-banyak-di-Proxmox/tes-wahaha$ ping 192.168.11.45
PING 192.168.11.45 (192.168.11.45) 56(84) bytes of data.
64 bytes from 192.168.11.45: icmp_seq=1 ttl=62 time=9.03 ms
^C
--- 192.168.11.45 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 9.031/9.031/9.031/0.000 msSSH
wahaha@wahaha-thinkpad:~/Spawn-VM-banyak-di-Proxmox/tes-wahaha$ ssh 192.168.11.22 -l ubuntu ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether bc:24:11:3d:e3:73 brd ff:ff:ff:ff:ff:ff
altname enp0s18
altname ens18
inet 192.168.11.22/24 brd 192.168.11.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::be24:11ff:fe3d:e373/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether bc:24:11:46:b3:f2 brd ff:ff:ff:ff:ff:ff
altname enp0s19
altname ens19
inet 192.168.12.252/24 metric 100 brd 192.168.12.255 scope global dynamic eth1
valid_lft 1356sec preferred_lft 1356sec
inet6 fe80::be24:11ff:fe46:b3f2/64 scope link
valid_lft forever preferred_lft foreverwahaha@wahaha-thinkpad:~/Spawn-VM-banyak-di-Proxmox/tes-wahaha$ ssh 192.168.11.45 -l ubuntu ip a
The authenticity of host '192.168.11.45 (192.168.11.45)' can't be established.
ED25519 key fingerprint is SHA256:s5JY5d7B6xRwC8elj/e7wDLw2zOC9lhiRS7wIj4F3hM.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.11.45' (ED25519) to the list of known hosts.
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether bc:24:11:e9:cb:9a brd ff:ff:ff:ff:ff:ff
altname enp0s18
altname ens18
inet 192.168.11.45/24 brd 192.168.11.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::be24:11ff:fee9:cb9a/64 scope link
valid_lft forever preferred_lft forever
3: ens19: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether bc:24:11:05:79:5f brd ff:ff:ff:ff:ff:ff
altname enp0s19Penutup
Mengotomatisasi pembuatan VM di Proxmox menggunakan terraform dapat mengefisiensi waktu dan mengantisipasi human error saat deploy vm yang banyak. Meskipun tahapannya panjang, tapi untuk kedepannya kita akan dipermudah saat deploy banyak vm sekaligus. Semoga postingan ini bermanfaat buat kalian yang ingin mulai terjun ke dunia Infrastructure as Code. Jika ada kendala saat mencoba workaround di atas, silakan mampir ke repository github saya atau tulis pertanyaan di kolom komentar.
Referensi
https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
https://manjit28.medium.com/automating-vm-creation-in-proxmox-a-complete-guide-18652f346db5
https://registry.terraform.io/providers/bpg/proxmox/latest/docs
https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks
https://medium.com/@DatBoyBlu3/provisioning-proxmox-virtual-machines-with-terraform-d9e9c549f947
https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425?permalink_comment_id=3799230
https://gist.github.com/zidenis/dfc05d9fa150ae55d7c87d870a0306c5
chatGPT gratis