Aller au contenu
Virtualisation medium

Provisionner des VM Proxmox avec Terraform

10 min de lecture

Logo Proxmox

Terraform provisionne des VMs Proxmox de façon déclarative et reproductible : on décrit la machine voulue, Terraform la crée, la modifie ou la détruit. Ce guide utilise le provider bpg/proxmox (le plus actif) pour, à partir d'un template cloud-init, cloner une VM, l'initialiser et récupérer son IP via l'agent. Il couvre la préparation côté Proxmox (rôle à moindre privilège, token API), le code Terraform complet et le cycle init / plan / apply / destroy. Tout a été testé sur Proxmox VE 9.1 avec le provider 0.111. Pour administrateurs qui industrialisent Proxmox.

  • Créer un rôle à moindre privilège et un token API pour Terraform.
  • Construire un template cloud-init Ubuntu réutilisable.
  • Configurer le provider bpg/proxmox et décrire une VM.
  • Cloner une VM, l'initialiser par cloud-init et lire son IP.
  • Gérer le cycle de vie complet (apply, destroy).
  • Un Proxmox VE (l'exemple est validé sur 9.1) accessible en API et en SSH.
  • Terraform (ou OpenTofu) installé sur votre poste.
  • Des notions de Terraform (syntaxe, providers, état).

Par sécurité, Terraform ne se connecte pas en root. On crée un rôle dédié limité aux actions nécessaires, un utilisateur, et un token API.

Fenêtre de terminal
pveum role add TerraformProv --privs "VM.Allocate VM.Audit VM.Clone VM.Config.Disk VM.Config.CPU VM.Config.Memory VM.Config.Network VM.Config.Options VM.Config.Cloudinit VM.Config.CDROM VM.Config.HWType VM.PowerMgmt VM.Console VM.Migrate VM.GuestAgent.Audit Datastore.Allocate Datastore.AllocateSpace Datastore.Audit Pool.Allocate Sys.Audit Sys.Modify SDN.Use"
Fenêtre de terminal
pveum user add terraform-prov@pve
pveum aclmod / -user terraform-prov@pve -role TerraformProv
pveum user token add terraform-prov@pve terraform -expire 0 -privsep 0 -comment "Terraform token"

L'option -privsep 0 fait que le token hérite des permissions de l'utilisateur. Notez le secret affiché, il ne sera plus jamais réaffiché :

full-tokenid │ terraform-prov@pve!terraform
value │ 4192f998-78d7-4d8f-a23e-5b32cbf4b157

Terraform clonera un template. On part d'une cloud image Ubuntu, on y injecte le qemu-guest-agent (indispensable pour remonter l'IP), puis on convertit la VM en template taggé template.

  1. Préparer l'image sur le nœud Proxmox :

    Fenêtre de terminal
    wget https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img \
    -O /tmp/ubuntu-24.04.img
    virt-customize -a /tmp/ubuntu-24.04.img --install qemu-guest-agent
  2. Créer la VM et importer le disque (syntaxe moderne import-from) :

    Fenêtre de terminal
    qm create 9000 --name ubuntu-2404-template --memory 2048 --cores 2 \
    --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-single \
    --serial0 socket --vga serial0 --agent enabled=1 --ostype l26
    qm set 9000 --scsi0 srv-pve:0,import-from=/tmp/ubuntu-24.04.img
    qm set 9000 --ide2 srv-pve:cloudinit
    qm set 9000 --boot order=scsi0
    qm set 9000 --tags template
  3. Convertir en template :

    Fenêtre de terminal
    qm template 9000

Adaptez le stockage (srv-pve) au vôtre. Le tag template permettra à Terraform de retrouver ce template dynamiquement.

Créez un fichier main.tf avec le provider bpg/proxmox. Il dialogue avec l'API (token) et en SSH pour les opérations sur fichiers (les snippets cloud-init).

terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.111.0"
}
}
}
provider "proxmox" {
endpoint = var.endpoint
api_token = var.api_token
insecure = true
ssh {
agent = true
username = "root"
}
}

Les variables dans variables.tf :

variable "endpoint" {
type = string
}
variable "api_token" {
type = string
sensitive = true
}
variable "target_node" {
type = string
default = "proxmox1"
}
variable "vm_hostname" {
type = string
default = "tf-demo"
}

Le token ne doit jamais être écrit en clair dans le code. Passez-le par variable d'environnement :

Fenêtre de terminal
export TF_VAR_api_token='terraform-prov@pve!terraform=4192f998-...'
export TF_VAR_endpoint='https://192.168.10.11:8006/'

Ajoutez à main.tf la source de données qui retrouve le template, le fichier cloud-init et la ressource VM :

data "proxmox_virtual_environment_vms" "template" {
tags = ["template"]
}
resource "proxmox_virtual_environment_file" "cloud_user_config" {
content_type = "snippets"
datastore_id = "srv-pve"
node_name = var.target_node
source_raw {
data = file("${path.module}/cloud-init/user_data.yaml")
file_name = "${var.vm_hostname}-ci-user.yaml"
}
}
resource "proxmox_virtual_environment_vm" "vm" {
name = var.vm_hostname
node_name = var.target_node
on_boot = false
tags = ["terraform", "ubuntu"]
agent {
enabled = true
}
cpu {
type = "x86-64-v2-AES"
cores = 2
sockets = 1
}
memory {
dedicated = 2048
}
network_device {
bridge = "vmbr0"
model = "virtio"
}
scsi_hardware = "virtio-scsi-single"
disk {
interface = "scsi0"
iothread = true
datastore_id = "srv-pve"
size = 10
discard = "on"
}
clone {
vm_id = data.proxmox_virtual_environment_vms.template.vms[0].vm_id
}
initialization {
datastore_id = "srv-pve"
interface = "ide2"
ip_config {
ipv4 {
address = "dhcp"
}
}
user_data_file_id = proxmox_virtual_environment_file.cloud_user_config.id
}
}
output "vm_ipv4" {
value = proxmox_virtual_environment_vm.vm.ipv4_addresses
}

Le fichier cloud-init/user_data.yaml configure la machine au premier démarrage (clé SSH, agent) :

#cloud-config
hostname: tf-demo
package_update: true
packages:
- qemu-guest-agent
users:
- default
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAA... votre-cle
runcmd:
- systemctl enable --now qemu-guest-agent
Fenêtre de terminal
terraform init
terraform plan
terraform apply -auto-approve

Terraform crée le snippet via SSH puis clone la VM. La sortie réelle montre la création en quelques secondes :

proxmox_virtual_environment_file.cloud_user_config: Creation complete after 1s [id=srv-pve:snippets/tf-demo-ci-user.yaml]
proxmox_virtual_environment_vm.vm: Creation complete after 3s [id=101]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Une fois l'agent démarré (cloud-init installe qemu-guest-agent), l'IP remonte dans les outputs :

Fenêtre de terminal
terraform output vm_ipv4
# tolist([ ["127.0.0.1"], ["192.168.10.209"] ])

La connexion SSH confirme que cloud-init a fait son travail :

Fenêtre de terminal
ssh ubuntu@192.168.10.209
# tf-demo - Ubuntu 24.04.4 LTS

Tout l'intérêt de Terraform : défaire proprement ce qu'il a créé.

Fenêtre de terminal
terraform destroy -auto-approve
# proxmox_virtual_environment_vm.vm: Destruction complete after 2s
# Destroy complete! Resources: 2 destroyed.

Le template (géré via une source de données, pas par Terraform) reste intact pour les prochains déploiements.

  • Le provider bpg/proxmox est le plus actif ; épinglez une version récente (testé en 0.111).
  • Créez un rôle à moindre privilège : sur PVE 9, VM.Monitor n'existe plus et VM.GuestAgent.Audit est requis pour lire l'IP.
  • Le provider a besoin d'un accès SSH root pour les snippets cloud-init (agent en local, clé privée en CI).
  • On clone un template taggé via une source de données, puis on initialise par cloud-init.
  • Le token se passe par TF_VAR_api_token, jamais en clair dans le code.

FAQ : questions fréquentes sur Terraform et Proxmox

Section intitulée « FAQ : questions fréquentes sur Terraform et Proxmox »

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn