Warum Terraform für VM-Provisionierung statt Klicki-Bunti
Wer regelmäßig gleichartige VMs anlegt (Testumgebungen, CI-Runner, kurzlebige Entwicklungs-VMs), tippt mit virt-install oder der GUI schnell dieselben Schritte immer wieder. Terraform macht die VM-Konfiguration zu versioniertem, wiederholbarem Code — Änderungen laufen durch Code-Review, und der aktuelle Zustand lässt sich jederzeit mit dem Ist-Zustand abgleichen (terraform plan).
Aktueller Stand: Provider-Version
Der dmacvicar/libvirt-Provider liegt inzwischen bei Version 0.9.x — ältere Anleitungen mit ~> 0.7 funktionieren zwar meist noch, sollten aber aktualisiert werden, da neuere Versionen Bugfixes und zusätzliche Ressourcen-Attribute mitbringen.
Provider-Konfiguration
terraform {
required_providers {
libvirt = {
source = "dmacvicar/libvirt"
version = "~> 0.9"
}
}
}
provider "libvirt" {
uri = "qemu:///system"
}
VM-Ressourcen
resource "libvirt_volume" "ubuntu_base" {
name = "ubuntu-24.04-base"
pool = "default"
source = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
format = "qcow2"
}
resource "libvirt_volume" "vm_disk" {
count = var.vm_count
name = "vm-${count.index + 1}-disk"
base_volume_id = libvirt_volume.ubuntu_base.id
pool = "default"
size = 42949672960
}
resource "libvirt_domain" "vm" {
count = var.vm_count
name = "vm-${count.index + 1}"
memory = 4096
vcpu = 4
disk {
volume_id = libvirt_volume.vm_disk[count.index].id
scsi = true
}
network_interface {
network_name = "default"
wait_for_lease = true
}
cpu {
mode = "host-passthrough"
}
}
base_volume_id verweist auf das Cloud-Image als Basis — jede VM-Disk wird als Copy-on-Write-Ableitung davon erzeugt, nicht als vollständige Kopie. Das spart Storage, bedeutet aber auch: Solange abgeleitete Volumes existieren, kann das Basis-Volume nicht gelöscht werden.
wait_for_lease = true lässt Terraform warten, bis die VM tatsächlich eine DHCP-IP bekommen hat, bevor der Apply als abgeschlossen gilt — ohne das Flag kennt Terraform die IP der neuen VM oft gar nicht, was nachgelagerte Provisionierungsschritte (z.B. Ansible) erschwert.
Deployment
terraform init
terraform plan -var="vm_count=3"
terraform apply -auto-approve -var="vm_count=3"
virsh list --all
terraform destroy -auto-approve
-auto-approve ist praktisch für Test-/CI-Umgebungen, in denen VMs regelmäßig auf- und abgebaut werden — für produktive Infrastruktur sollte der manuelle Bestätigungsschritt bewusst drinbleiben, damit ein versehentlicher apply nicht ungeprüft Ressourcen verändert.
Wo dieser Ansatz an Grenzen stößt
Für Proxmox-Cluster mit mehreren Hosts ist dmacvicar/libvirt nicht das richtige Werkzeug — der Provider spricht direkt mit libvirt auf einem einzelnen KVM-Host, nicht mit der Proxmox-API. Wer Proxmox deklarativ verwalten will, braucht einen der Proxmox-spezifischen Terraform-Provider (Telmate/proxmox oder bpg/proxmox), die über die native Proxmox-REST-API arbeiten und Cluster-Funktionen wie Live-Migration und Storage-Pools korrekt abbilden.
FAQ
Kann ich Proxmox mit Terraform steuern?
Ja, aber nicht mit diesem Provider — dafür gibt es die dedizierten Proxmox-Terraform-Provider (Telmate oder BPG), die die Proxmox-API statt libvirt direkt ansprechen.
Funktioniert das auch mit lokalen VMs auf dem eigenen Laptop?
Ja, solange KVM/libvirt lokal installiert und qemu:///system erreichbar ist — praktisch für reproduzierbare lokale Testumgebungen.
Fazit
Terraform mit dem libvirt-Provider ist die richtige Wahl für deklarative VM-Verwaltung auf einem einzelnen KVM-Host, besonders bei häufig wechselnden Test- oder CI-Umgebungen. Für Proxmox-Cluster braucht es die spezialisierten Proxmox-Provider, nicht diesen.
Ich automatisiere VM-Provisionierung und Infrastructure-as-Code-Workflows für Unternehmen in Heidelberg und der Rhein-Neckar-Region. Anfragen.