Terraform / OpenTofu avec Proxmox¶
Objectif : décrire vos VM Proxmox VE dans du code (Terraform ou OpenTofu), les créer par clonage d'un template cloud-init, et les modifier ou les supprimer de façon reproductible et relue, avec un jeton API limité à un pool dédié.
Choisir un provider¶
Proxmox ne publie pas de provider officiel. Deux providers communautaires dominent ; ils fonctionnent à l'identique avec Terraform et OpenTofu.
bpg/proxmox |
Telmate/proxmox |
|
|---|---|---|
| Statut | Communautaire, développement actif | Communautaire, historique |
| Ressource VM | proxmox_virtual_environment_vm |
proxmox_vm_qemu |
| Couverture | VM, CT, fichiers/snippets, réseau, utilisateurs, firewall, HA… | Surtout VM et CT |
| Authentification | Jeton API, plus SSH pour certaines opérations (upload de snippets, import de disques) | Jeton API ou mot de passe |
| Recommandation MSP | Choix par défaut pour un nouveau projet | Projets existants ; vérifier la compatibilité PVE 9 de la version utilisée |
Étant communautaires, ces providers ne sont pas couverts par le support Proxmox ni garantis par le MSP : épinglez une version validée en lab et relisez les notes de version avant chaque montée.
Avant de toucher la prod
- Compte de service
svc-terraform@pve, rôleTerraformProv, poolterraformet jetonsvc-terraform@pve!cicréés : voir Jetons API et comptes de service. - Template cloud-init prêt (VMID 9000, agent QEMU installé), placé dans le pool
terraform: voir Templates cloud-init. - Plage de VMID et plage d'IP réservées à Terraform (ex. 1200-1299,
10.20.0.120-199). - État (state) stocké dans un backend distant avec verrouillage (GitLab, S3 compatible), jamais sur un poste.
- Secret du jeton dans le coffre ou la CI, exposé en variable d'environnement
TF_VAR_pve_api_token.
Étapes¶
1. Déclarer le provider¶
providers.tf :
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "0.111.0" # version validée en lab, à faire évoluer après test
}
}
}
variable "pve_api_token" {
type = string
sensitive = true # format : svc-terraform@pve!ci=<secret>
}
provider "proxmox" {
endpoint = "https://pve01.corp.local:8006/"
api_token = var.pve_api_token
insecure = false
}
terraform {
required_providers {
proxmox = {
source = "Telmate/proxmox"
version = "<version validée en lab>"
}
}
}
provider "proxmox" {
pm_api_url = "https://pve01.corp.local:8006/api2/json"
pm_api_token_id = "svc-terraform@pve!ci"
pm_api_token_secret = var.pve_api_token_secret
pm_tls_insecure = false
}
2. Décrire une VM clonée (bpg)¶
web01.tf :
resource "proxmox_virtual_environment_vm" "web01" {
name = "web01"
node_name = "pve01"
vm_id = 1201
pool_id = "terraform"
tags = ["terraform", "web"]
clone {
vm_id = 9000
full = true
}
agent { enabled = true }
cpu { cores = 2 }
memory { dedicated = 4096 }
disk {
datastore_id = "ceph-vm"
interface = "scsi0"
size = 40
}
network_device {
bridge = "vmbr0"
vlan_id = 20
}
initialization {
datastore_id = "ceph-vm"
ip_config {
ipv4 {
address = "10.20.0.121/24"
gateway = "10.20.0.1"
}
}
user_account {
username = "exploit"
keys = [file("~/.ssh/id_ed25519.pub")]
}
}
}
3. Planifier, relire, appliquer¶
export TF_VAR_pve_api_token='svc-terraform@pve!ci=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
tofu init # ou terraform init
tofu plan -out plan.bin
tofu apply plan.bin
Le plan est relu par un pair (merge request) avant tout apply en production. Une ligne must be replaced sur une VM existante signifie destruction puis recréation : on s'arrête et on analyse.
Vérifications
tofu planjuste après l'applyafficheNo changes. Your infrastructure matches the configuration.qm config 1201surpve01:agent: 1,net0survmbr0avectag=20,ipconfig0: ip=10.20.0.121/24,gw=10.20.0.1.- Datacenter → Pools → terraform contient la VM 1201.
ssh exploit@10.20.0.121 hostnamerépondweb01.
Rollback
Revenir au commit précédent puis tofu plan / tofu apply. Pour retirer une VM du code sans la détruire : tofu state rm proxmox_virtual_environment_vm.web01. tofu destroy détruit réellement les VM et leurs disques : à protéger, et sauvegarder les VM concernées avant (vzdump 1201 --storage pbs-local).
Pièges vus en support
- Modification faite à la main dans la GUI sur une VM gérée par Terraform : le
plansuivant veut l'annuler, parfois en recréant la VM. Toute modification passe par le code. - Changement d'un attribut qui force le remplacement (clone source, certains paramètres de disque) : la VM et ses données sont détruites. Lire le
plan. VM.GuestAgent.Auditabsent du rôle (PVE 9) : création OK puis erreur 403 en lisant l'IP.- Deux personnes qui appliquent en parallèle avec un state local : VMID en conflit, état divergent. Backend distant avec verrou obligatoire.
- Jeton
root@pamutilisé « pour aller vite » : undestroymal ciblé peut supprimer n'importe quelle VM du cluster. Le pool dédié limite les dégâts. - Snippets cloud-init personnalisés avec bpg : nécessitent un stockage acceptant le contenu snippets et un accès SSH au nœud, en plus du jeton.
Quand ouvrir un ticket iTop
VM détruite par erreur : P2, restauration selon RB-12. Revue d'un code Terraform, conception des rôles et pools, intégration CI : demande P4. Le MSP ne débogue pas les providers communautaires eux-mêmes, mais vérifie droits, templates et API côté Proxmox.
Pour aller plus loin¶
- API REST : premiers appels · Ansible : inventaire dynamique
- Provider bpg : registre Terraform · Provider Telmate : registre Terraform
- Tutoriel en français : Terraform et Proxmox (Stéphane Robert)
- OpenTofu